tinymist_query/analysis/completion/
import.rs1use super::*;
4impl CompletionPair<'_, '_, '_> {
5 pub fn complete_imports(&mut self) -> bool {
7 if matches!(self.cursor.leaf.kind(), SyntaxKind::Colon)
10 && let Some(parent) = self.cursor.leaf.clone().parent()
11 && let Some(ast::Expr::Import(import)) = parent.get().cast()
12 && !matches!(import.imports(), Some(ast::Imports::Wildcard))
13 && let Some(source) = parent.children().find(|child| child.is::<ast::Expr>())
14 {
15 let items = match import.imports() {
16 Some(ast::Imports::Items(items)) => items,
17 _ => Default::default(),
18 };
19
20 self.cursor.from = self.cursor.cursor;
21
22 self.import_item_completions(items, vec![], &source);
23 if items.iter().next().is_some() {
24 self.worker.enrich("", ", ");
25 }
26 return true;
27 }
28
29 if let Some(prev) = self.cursor.leaf.prev_sibling()
34 && let Some(ast::Expr::Import(import)) = prev.get().cast()
35 && !self.cursor.text[prev.offset()..self.cursor.cursor].contains('\n')
36 && let Some(ast::Imports::Items(items)) = import.imports()
37 && let Some(source) = prev.children().find(|child| child.is::<ast::Expr>())
38 {
39 self.cursor.from = self.cursor.cursor;
40 self.import_item_completions(items, vec![], &source);
41 return true;
42 }
43
44 if matches!(self.cursor.leaf.kind(), SyntaxKind::Comma)
47 && let Some(parent) = self.cursor.leaf.clone().parent()
48 && parent.kind() == SyntaxKind::ImportItems
49 && let Some(grand) = parent.parent()
50 && let Some(ast::Expr::Import(import)) = grand.get().cast()
51 && let Some(ast::Imports::Items(items)) = import.imports()
52 && let Some(source) = grand.children().find(|child| child.is::<ast::Expr>())
53 {
54 self.import_item_completions(items, vec![], &source);
55 self.worker.enrich(" ", "");
56 return true;
57 }
58
59 if matches!(self.cursor.leaf.kind(), SyntaxKind::Ident | SyntaxKind::Dot)
62 && let Some(path_ctx) = self.cursor.leaf.clone().parent()
63 && path_ctx.kind() == SyntaxKind::ImportItemPath
64 && let Some(parent) = path_ctx.parent()
65 && parent.kind() == SyntaxKind::ImportItems
66 && let Some(grand) = parent.parent()
67 && let Some(ast::Expr::Import(import)) = grand.get().cast()
68 && let Some(ast::Imports::Items(items)) = import.imports()
69 && let Some(source) = grand.children().find(|child| child.is::<ast::Expr>())
70 {
71 if self.cursor.leaf.kind() == SyntaxKind::Ident {
72 self.cursor.from = self.cursor.leaf.offset();
73 }
74 let path = path_ctx.cast::<ast::ImportItemPath>().map(|path| {
75 path.iter()
76 .take_while(|ident| ident.span() != self.cursor.leaf.span())
77 .collect()
78 });
79 self.import_item_completions(items, path.unwrap_or_default(), &source);
80 return true;
81 }
82
83 false
84 }
85
86 pub fn import_item_completions(
88 &mut self,
89 existing: ast::ImportItems,
90 comps: Vec<ast::Ident>,
91 source: &LinkedNode,
92 ) {
93 let value = self.worker.ctx.module_by_syntax(source);
95 let value = comps.iter().fold(value.as_ref(), |value, comp| {
96 value?.scope()?.get(comp)?.read().into()
97 });
98 let Some(scope) = value.and_then(|v| v.scope()) else {
99 return;
100 };
101
102 let seen = existing
104 .iter()
105 .flat_map(|item| {
106 let item_comps = item.path().iter().collect::<Vec<_>>();
107 if item_comps.len() == comps.len() + 1
108 && item_comps
109 .iter()
110 .zip(comps.as_slice())
111 .all(|(l, r)| l.as_str() == r.as_str())
112 {
113 item_comps.last().cloned()
115 } else {
116 None
117 }
118 })
119 .collect::<Vec<_>>();
120
121 if existing.iter().next().is_none() {
122 self.snippet_completion("*", "*", "Import everything.");
123 }
124
125 for (name, bind) in scope.iter() {
126 if seen.iter().all(|item| item.as_str() != name) {
127 self.value_completion(Some(name.clone()), bind.read(), false, None);
128 }
129 }
130 }
131}