tinymist_query/analysis/completion/
func.rs

1//! Completion for functions on nodes.
2
3use super::*;
4
5impl CompletionPair<'_, '_, '_> {
6    pub fn func_completion(
7        &mut self,
8        mode: InterpretMode,
9        fn_feat: FnCompletionFeat,
10        name: EcoString,
11        label_details: Option<EcoString>,
12        detail: Option<EcoString>,
13        parens: bool,
14    ) {
15        let base = Completion {
16            kind: CompletionKind::Func,
17            label_details,
18            detail,
19            command: self
20                .worker
21                .ctx
22                .analysis
23                .trigger_on_snippet_with_param_hint(true)
24                .map(From::from),
25            ..Default::default()
26        };
27
28        if matches!(
29            self.cursor.surrounding_syntax,
30            SurroundingSyntax::ShowTransform
31        ) && (fn_feat.min_pos() > 0 || fn_feat.min_named() > 0)
32        {
33            self.push_completion(Completion {
34                label: eco_format!("{name}.with"),
35                apply: Some(eco_format!("{name}.with(${{}})")),
36                ..base.clone()
37            });
38        }
39        if fn_feat.is_element
40            && matches!(self.cursor.surrounding_syntax, SurroundingSyntax::Selector)
41        {
42            self.push_completion(Completion {
43                label: eco_format!("{name}.where"),
44                apply: Some(eco_format!("{name}.where(${{}})")),
45                ..base.clone()
46            });
47        }
48
49        let bad_instantiate = match self.cursor.surrounding_syntax {
50            // todo: filter invalid function as a selector.
51            SurroundingSyntax::Selector => name == "with",
52            SurroundingSyntax::SetRule => !fn_feat.is_element,
53            _ => false,
54        };
55        if !bad_instantiate {
56            let only_parens = !parens
57                || (matches!(self.cursor.surrounding_syntax, SurroundingSyntax::Selector)
58                    && fn_feat.is_element);
59            let is_set = matches!(self.cursor.surrounding_syntax, SurroundingSyntax::SetRule);
60
61            if !only_parens && fn_feat.prefer_to_be_scope(is_set) {
62                self.push_completion(Completion {
63                    label: name.clone(),
64                    ..base.clone()
65                });
66            }
67
68            if only_parens {
69                self.push_completion(Completion {
70                    label: name,
71                    ..base
72                });
73            } else if (fn_feat.min_pos() < 1 || fn_feat.has_only_self()) && !fn_feat.has_rest {
74                self.push_completion(Completion {
75                    apply: Some(eco_format!("{}()${{}}", name)),
76                    label: paren_label(&name, &fn_feat, is_set),
77                    command: None,
78                    ..base
79                });
80            } else {
81                let accept_content_arg = fn_feat.next_arg_is_content && !fn_feat.has_rest;
82                let scope_reject_content = matches!(mode, InterpretMode::Math)
83                    || matches!(
84                        self.cursor.surrounding_syntax,
85                        SurroundingSyntax::Selector | SurroundingSyntax::SetRule
86                    );
87                self.push_completion(Completion {
88                    apply: Some(eco_format!("{name}(${{}})")),
89                    label: paren_label(&name, &fn_feat, is_set),
90                    ..base.clone()
91                });
92                if !scope_reject_content && accept_content_arg {
93                    self.push_completion(Completion {
94                        apply: Some(eco_format!("{name}[${{}}]")),
95                        label: eco_format!("{name}.bracket"),
96                        ..base
97                    });
98                };
99            }
100        }
101
102        fn paren_label(name: &EcoString, fn_feat: &FnCompletionFeat, is_set: bool) -> EcoString {
103            if fn_feat.prefer_to_be_scope(is_set) {
104                eco_format!("{name}.paren")
105            } else {
106                name.clone()
107            }
108        }
109    }
110}