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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use std::{cell::RefCell, rc::Rc};
use antlr_rust::tree::ParseTree;
use hamelin_lib::{
antlr::{
completion_interval,
hamelinparser::{
AssignmentClauseContextAll, AssignmentClauseContextAttrs, AssignmentContextAttrs,
ExpressionContextAll, ExpressionEOFContextAttrs, TsTruncContextAttrs,
},
interval,
},
completion::Completion,
err::TranslationErrors,
parse_identifier,
parser::make_hamelin_parser_from_input,
sql::expression::{
identifier::{HamelinIdentifier, Identifier, SimpleIdentifier},
SQLExpression,
},
translation::ExpressionTranslation,
};
use super::{expression::HamelinExpression, ExpressionTranslationContext};
pub struct HamelinAssignmentClause {
pub ctx: Rc<AssignmentClauseContextAll<'static>>,
pub translation_context: Rc<ExpressionTranslationContext>,
}
impl HamelinAssignmentClause {
pub fn new(
ctx: Rc<AssignmentClauseContextAll<'static>>,
expression_translation_context: Rc<ExpressionTranslationContext>,
) -> Self {
Self {
ctx,
translation_context: expression_translation_context,
}
}
pub fn to_sql(&self) -> Result<(Identifier, ExpressionTranslation), TranslationErrors> {
if self.ctx.expression().is_none() && self.ctx.assignment().is_none() {
self.append_completions();
}
if let Some(expression_tree) = self.ctx.expression() {
let expression =
HamelinExpression::new(expression_tree.clone(), self.translation_context.clone());
let translation = expression.translate()?;
// Re-parse the expression to see if it works as an identifier.
// We have to re-parse because the ANTLR grammar would have applied the #deref expression alt in this context.
let maybe_identifier = parse_identifier(self.ctx.get_text())
.ok()
.and_then(|i| HamelinIdentifier::new(i).to_sql().ok());
// If the expression works as an identifier, we roll with that
let identifier = maybe_identifier
.or_else(|| match &translation.sql {
// If the expression is a function call with no arguments, roll with the function name
SQLExpression::FunctionCallApply(fca)
if fca.arguments.is_empty() && fca.named_arguments.is_empty() =>
{
fca.function_name.clone().parse().ok()
}
_ => None,
})
// If the expression is a tstrunc of an identifier, roll with the identifier.
.or_else(|| match expression_tree.as_ref() {
ExpressionContextAll::TsTruncContext(ctx) => ctx
.expression()
.and_then(|exp| parse_identifier(exp.get_text()).ok())
.and_then(|i| HamelinIdentifier::new(i).to_sql().ok()),
_ => None,
})
// Don't know what else to do. Use the backquote that I love and everyone else hates.
.unwrap_or_else(|| {
SimpleIdentifier::new(&expression_tree.get_text().replace("`", "")).into()
});
Ok((identifier.into(), translation))
} else {
let assignment_tree =
TranslationErrors::expect(self.ctx.as_ref(), self.ctx.assignment())?;
let identifier_tree =
TranslationErrors::expect(assignment_tree.as_ref(), assignment_tree.identifier())?;
let expression_tree =
TranslationErrors::expect(assignment_tree.as_ref(), assignment_tree.expression())?;
let identifier = HamelinIdentifier::new(identifier_tree);
let expression =
HamelinExpression::new(expression_tree.clone(), self.translation_context.clone());
let translation = expression.translate()?;
Ok((identifier.to_sql()?, translation))
}
}
// Append all expression completions.
//
// We only complete on an assignment clause when its children are broken. If the children
// aren't broken, there will be hooks on the children for doing better completion.
fn append_completions(&self) {
let range = completion_interval(self.ctx.as_ref());
match self.translation_context.at {
Some(at) if range.contains(&at) => {
let text = self.ctx.get_text();
if text.is_empty() {
// If the error node is literally empty text, this just means that we are free
// to dump the "empty completion" plan to the user. They haven't typed anything
// yet, so we just give them the "any expression" full list of options.
if let Ok(mut guard) = self.translation_context.completions.try_borrow_mut() {
if guard.is_none() {
let insert_interval = interval(self.ctx.as_ref());
let mut completion = Completion::new(insert_interval);
completion.add_items(
self.translation_context
.bindings
.autocomplete_suggestions(false),
);
completion.add_items(
self.translation_context.registry.autocomplete_suggestions(),
);
*guard = Some(completion);
}
}
} else {
// If the error node has text in it, brace yourself. Fuck my life.
//
// ANTLR's error recovery seems to always predict that a broken assignment
// clause is actually an assignment, rather than an expression.
//
// When the user types "<ident> = " then ANTLR can correctly figure out
// that the next thing is an expression, and normal completion works just fine.
//
// When the user just starts typing an expression, like "client.", it seems
// to think that the dot is actually just an incorrect =, and it predicts
// this as a broken assignment. Chaos ensues.
//
// So, if there is any text at all in the broken assignment clause, we just
// FORCE the issue -- re-parse the text as an expression and do completion of that.
// If that thing yields any items, we translate those sub-items and yeet them up.
//
// Christ on a bicycle.
let expression_tree = make_hamelin_parser_from_input(self.ctx.get_text())
.0
.expressionEOF();
if let Some(tree) = expression_tree.ok().and_then(|t| t.expression()) {
// Set up a brand new context, with a brand new completion spot.
// This new set of completions is entirely distinct from the current ones.
let sub_context = ExpressionTranslationContext::new(
self.translation_context.bindings.clone(),
self.translation_context.registry.clone(),
self.translation_context.translation_registry.clone(),
self.translation_context.fctx.clone(),
Some(at - range.start()),
Rc::new(RefCell::new(None)),
);
// Will have the side effect of completions on the above context.
// Don't love this. Should probably be able to .append_completions() from outside.
let _ignore = HamelinExpression::new(tree, sub_context.clone()).translate();
// Translate the nested completions into the current context. Ugh.
let top_guard = self.translation_context.completions.try_borrow_mut();
let sub_guard = sub_context.completions.borrow();
match (top_guard, &*sub_guard) {
(Ok(mut tg), Some(sg)) if tg.is_none() => {
let mut completion = Completion::new(
sg.at.start() + range.start()..=sg.at.end() + range.end(),
);
completion.filter(false);
completion.add_items(sg.items.clone());
*tg = Some(completion);
}
_ => {}
}
}
}
}
_ => {}
}
}
}