-
Notifications
You must be signed in to change notification settings - Fork 463
/
mod.rs
164 lines (154 loc) · 7.06 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use cairo_lang_compiler::db::RootDatabase;
use cairo_lang_semantic::items::us::get_use_segments;
use cairo_lang_semantic::resolve::AsSegments;
use cairo_lang_syntax::node::ast::PathSegment;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{ast, SyntaxNode, TypedSyntaxNode};
use cairo_lang_utils::Upcast;
use tower_lsp::lsp_types::{CompletionParams, CompletionResponse, CompletionTriggerKind};
use tracing::debug;
use self::completions::{colon_colon_completions, dot_completions, generic_completions};
use crate::lang::db::{LsSemanticGroup, LsSyntaxGroup};
use crate::lang::lsp::{LsProtoGroup, ToCairo};
mod completions;
/// Compute completion items at a given cursor position.
#[tracing::instrument(
level = "debug",
skip_all,
fields(uri = %params.text_document_position.text_document.uri)
)]
pub fn complete(params: CompletionParams, db: &RootDatabase) -> Option<CompletionResponse> {
let text_document_position = params.text_document_position;
let file_id = db.file_for_url(&text_document_position.text_document.uri)?;
let mut position = text_document_position.position;
position.character = position.character.saturating_sub(1);
let mut node = db.find_syntax_node_at_position(file_id, position.to_cairo())?;
let lookup_items = db.collect_lookup_items_stack(&node)?;
let module_file_id = db.find_module_file_containing_node(&node)?;
// Skip trivia.
while ast::Trivium::is_variant(node.kind(db))
|| node.kind(db) == SyntaxKind::Trivia
|| node.kind(db).is_token()
{
node = node.parent().unwrap_or(node);
}
let trigger_kind =
params.context.map(|it| it.trigger_kind).unwrap_or(CompletionTriggerKind::INVOKED);
match completion_kind(db, node) {
CompletionKind::Dot(expr) => {
dot_completions(db, file_id, lookup_items, expr).map(CompletionResponse::Array)
}
CompletionKind::ColonColon(segments) if !segments.is_empty() => {
colon_colon_completions(db, module_file_id, lookup_items, segments)
.map(CompletionResponse::Array)
}
_ if trigger_kind == CompletionTriggerKind::INVOKED => {
Some(CompletionResponse::Array(generic_completions(db, module_file_id, lookup_items)))
}
_ => None,
}
}
enum CompletionKind {
Dot(ast::ExprBinary),
ColonColon(Vec<PathSegment>),
}
#[tracing::instrument(level = "trace", skip_all)]
fn completion_kind(db: &RootDatabase, node: SyntaxNode) -> CompletionKind {
debug!("node.kind: {:#?}", node.kind(db));
match node.kind(db) {
SyntaxKind::TerminalDot => {
let parent = node.parent().unwrap();
if parent.kind(db) == SyntaxKind::ExprBinary {
return CompletionKind::Dot(ast::ExprBinary::from_syntax_node(db, parent));
}
}
SyntaxKind::TerminalColonColon => {
let parent = node.parent().unwrap();
debug!("parent.kind: {:#?}", parent.kind(db));
if parent.kind(db) == SyntaxKind::ExprPath {
return completion_kind_from_path_node(db, parent);
}
let grandparent = parent.parent().unwrap();
debug!("grandparent.kind: {:#?}", grandparent.kind(db));
if grandparent.kind(db) == SyntaxKind::ExprPath {
return completion_kind_from_path_node(db, grandparent);
}
let (use_ast, should_pop) = if parent.kind(db) == SyntaxKind::UsePathLeaf {
(ast::UsePath::Leaf(ast::UsePathLeaf::from_syntax_node(db, parent)), true)
} else if grandparent.kind(db) == SyntaxKind::UsePathLeaf {
(ast::UsePath::Leaf(ast::UsePathLeaf::from_syntax_node(db, grandparent)), true)
} else if parent.kind(db) == SyntaxKind::UsePathSingle {
(ast::UsePath::Single(ast::UsePathSingle::from_syntax_node(db, parent)), false)
} else if grandparent.kind(db) == SyntaxKind::UsePathSingle {
(ast::UsePath::Single(ast::UsePathSingle::from_syntax_node(db, grandparent)), false)
} else {
debug!("Generic");
return CompletionKind::ColonColon(vec![]);
};
let mut segments = vec![];
let Ok(()) = get_use_segments(db.upcast(), &use_ast, &mut segments) else {
debug!("Generic");
return CompletionKind::ColonColon(vec![]);
};
if should_pop {
segments.pop();
}
debug!("ColonColon");
return CompletionKind::ColonColon(segments);
}
SyntaxKind::TerminalIdentifier => {
let parent = node.parent().unwrap();
debug!("parent.kind: {:#?}", parent.kind(db));
let grandparent = parent.parent().unwrap();
debug!("grandparent.kind: {:#?}", grandparent.kind(db));
if grandparent.kind(db) == SyntaxKind::ExprPath {
if db.get_children(grandparent.clone())[0].stable_ptr() != parent.stable_ptr() {
// Not the first segment.
debug!("Not first segment");
return completion_kind_from_path_node(db, grandparent);
}
// First segment.
let grandgrandparent = grandparent.parent().unwrap();
debug!("grandgrandparent.kind: {:#?}", grandgrandparent.kind(db));
if grandgrandparent.kind(db) == SyntaxKind::ExprBinary {
let expr = ast::ExprBinary::from_syntax_node(db, grandgrandparent.clone());
if matches!(
ast::ExprBinary::from_syntax_node(db, grandgrandparent).op(db),
ast::BinaryOperator::Dot(_)
) {
debug!("Dot");
return CompletionKind::Dot(expr);
}
}
}
if grandparent.kind(db) == SyntaxKind::UsePathLeaf {
let use_ast = ast::UsePathLeaf::from_syntax_node(db, grandparent);
let mut segments = vec![];
let Ok(()) =
get_use_segments(db.upcast(), &ast::UsePath::Leaf(use_ast), &mut segments)
else {
debug!("Generic");
return CompletionKind::ColonColon(vec![]);
};
segments.pop();
debug!("ColonColon");
return CompletionKind::ColonColon(segments);
}
}
_ => (),
}
debug!("Generic");
CompletionKind::ColonColon(vec![])
}
#[tracing::instrument(level = "trace", skip_all)]
fn completion_kind_from_path_node(db: &RootDatabase, parent: SyntaxNode) -> CompletionKind {
debug!("completion_kind_from_path_node: {}", parent.clone().get_text_without_trivia(db));
let expr = ast::ExprPath::from_syntax_node(db, parent);
debug!("has_tail: {}", expr.has_tail(db));
let mut segments = expr.to_segments(db);
if expr.has_tail(db) {
segments.pop();
}
CompletionKind::ColonColon(segments)
}