Skip to content

Commit 6c4812b

Browse files
hmmm
1 parent 5c8e8e8 commit 6c4812b

4 files changed

Lines changed: 72 additions & 23 deletions

File tree

crates/pgt_completions/src/complete.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ pub struct CompletionParams<'a> {
1717
pub tree: Option<&'a tree_sitter::Tree>,
1818
}
1919

20-
#[tracing::instrument(level = "debug")]
20+
#[tracing::instrument(level = "debug", skip_all, fields(
21+
text = params.text,
22+
position = params.position.to_string()
23+
))]
2124
pub fn complete(params: CompletionParams) -> Vec<CompletionItem> {
2225
let ctx = CompletionContext::new(&params);
2326

crates/pgt_completions/src/context.rs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,29 @@ impl TryFrom<String> for ClauseType {
4949
}
5050

5151
pub(crate) struct CompletionContext<'a> {
52-
pub ts_node: Option<tree_sitter::Node<'a>>,
52+
pub node_under_cursor: Option<tree_sitter::Node<'a>>,
53+
pub previous_node: Option<tree_sitter::Node<'a>>,
54+
5355
pub tree: Option<&'a tree_sitter::Tree>,
5456
pub text: &'a str,
5557
pub schema_cache: &'a SchemaCache,
5658
pub position: usize,
5759

60+
/// If the cursor of the user is offset to the right of the statement,
61+
/// we'll have to move it back to the last node, otherwise, tree-sitter will break.
62+
/// However, knowing that the user is typing on the "next" node lets us prioritize different completion results.
63+
/// We consider an offset of up to two characters as valid.
64+
///
65+
/// Example:
66+
///
67+
/// ```
68+
/// select * from {}
69+
/// ```
70+
///
71+
/// We'll adjust the cursor position so it lies on the "from" token – but we're looking
72+
/// for table completions.
73+
pub cursor_offset_from_end: bool,
74+
5875
pub schema_name: Option<String>,
5976
pub wrapping_clause_type: Option<ClauseType>,
6077
pub is_invocation: bool,
@@ -70,7 +87,9 @@ impl<'a> CompletionContext<'a> {
7087
text: &params.text,
7188
schema_cache: params.schema,
7289
position: usize::from(params.position),
73-
ts_node: None,
90+
cursor_offset_from_end: false,
91+
previous_node: None,
92+
node_under_cursor: None,
7493
schema_name: None,
7594
wrapping_clause_type: None,
7695
wrapping_statement_range: None,
@@ -81,8 +100,6 @@ impl<'a> CompletionContext<'a> {
81100
ctx.gather_tree_context();
82101
ctx.gather_info_from_ts_queries();
83102

84-
println!("Here's my node: {:?}", ctx.ts_node.unwrap());
85-
86103
ctx
87104
}
88105

@@ -147,30 +164,34 @@ impl<'a> CompletionContext<'a> {
147164
* `select * from use {}` becomes `select * from use{}`.
148165
*/
149166
let current_node = cursor.node();
167+
let position_cache = self.position.clone();
150168
while cursor.goto_first_child_for_byte(self.position).is_none() && self.position > 0 {
151169
self.position -= 1;
152170
}
153171

172+
let cursor_offset = position_cache - self.position;
173+
self.cursor_offset_from_end = cursor_offset > 0 && cursor_offset <= 2;
174+
154175
self.gather_context_from_node(cursor, current_node);
155176
}
156177

157178
fn gather_context_from_node(
158179
&mut self,
159180
mut cursor: tree_sitter::TreeCursor<'a>,
160-
previous_node: tree_sitter::Node<'a>,
181+
parent_node: tree_sitter::Node<'a>,
161182
) {
162183
let current_node = cursor.node();
163184

164185
// prevent infinite recursion – this can happen if we only have a PROGRAM node
165-
if current_node.kind() == previous_node.kind() {
166-
self.ts_node = Some(current_node);
186+
if current_node.kind() == parent_node.kind() {
187+
self.node_under_cursor = Some(current_node);
167188
return;
168189
}
169190

170-
match previous_node.kind() {
191+
match parent_node.kind() {
171192
"statement" | "subquery" => {
172193
self.wrapping_clause_type = current_node.kind().try_into().ok();
173-
self.wrapping_statement_range = Some(previous_node.range());
194+
self.wrapping_statement_range = Some(parent_node.range());
174195
}
175196
"invocation" => self.is_invocation = true,
176197

@@ -202,7 +223,23 @@ impl<'a> CompletionContext<'a> {
202223

203224
// We have arrived at the leaf node
204225
if current_node.child_count() == 0 {
205-
self.ts_node = Some(current_node);
226+
if self.cursor_offset_from_end {
227+
self.node_under_cursor = None;
228+
self.previous_node = Some(current_node);
229+
} else {
230+
// for the previous node, either select the previous sibling,
231+
// or collect the parent's previous sibling's last child.
232+
let previous = match current_node.prev_sibling() {
233+
Some(n) => Some(n),
234+
None => {
235+
let sib_of_parent = parent_node.prev_sibling();
236+
sib_of_parent.and_then(|p| p.children(&mut cursor).last())
237+
}
238+
};
239+
self.node_under_cursor = Some(current_node);
240+
self.previous_node = previous;
241+
}
242+
206243
return;
207244
}
208245

@@ -361,7 +398,7 @@ mod tests {
361398

362399
let ctx = CompletionContext::new(&params);
363400

364-
let node = ctx.ts_node.unwrap();
401+
let node = ctx.node_under_cursor.unwrap();
365402

366403
assert_eq!(ctx.get_ts_node_content(node), Some("select"));
367404

@@ -389,7 +426,7 @@ mod tests {
389426

390427
let ctx = CompletionContext::new(&params);
391428

392-
let node = ctx.ts_node.unwrap();
429+
let node = ctx.node_under_cursor.unwrap();
393430

394431
assert_eq!(ctx.get_ts_node_content(node), Some("from"));
395432
assert_eq!(
@@ -415,7 +452,7 @@ mod tests {
415452

416453
let ctx = CompletionContext::new(&params);
417454

418-
let node = ctx.ts_node.unwrap();
455+
let node = ctx.node_under_cursor.unwrap();
419456

420457
assert_eq!(ctx.get_ts_node_content(node), Some(""));
421458
assert_eq!(ctx.wrapping_clause_type, None);
@@ -440,7 +477,7 @@ mod tests {
440477

441478
let ctx = CompletionContext::new(&params);
442479

443-
let node = ctx.ts_node.unwrap();
480+
let node = ctx.node_under_cursor.unwrap();
444481

445482
assert_eq!(ctx.get_ts_node_content(node), Some("fro"));
446483
assert_eq!(ctx.wrapping_clause_type, Some(ClauseType::Select));

crates/pgt_completions/src/relevance.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ impl CompletionRelevance<'_> {
4141
}
4242

4343
fn check_matches_query_input(&mut self, ctx: &CompletionContext) {
44-
let node = ctx.ts_node.unwrap();
44+
let node = match ctx.node_under_cursor {
45+
Some(node) => node,
46+
None => return,
47+
};
4548

4649
let content = match ctx.get_ts_node_content(node) {
4750
Some(c) => c,

crates/pgt_workspace/src/workspace/server.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use pgt_analyse::{AnalyserOptions, AnalysisFilter};
1313
use pgt_analyser::{Analyser, AnalyserConfig, AnalyserContext};
1414
use pgt_diagnostics::{Diagnostic, DiagnosticExt, Severity, serde::Diagnostic as SDiagnostic};
1515
use pgt_fs::{ConfigName, PgTPath};
16+
use pgt_text_size::{TextRange, TextSize};
1617
use pgt_typecheck::TypecheckParams;
1718
use schema_cache_manager::SchemaCacheManager;
1819
use sqlx::Executor;
@@ -535,13 +536,18 @@ impl Workspace for WorkspaceServer {
535536
.get(&params.path)
536537
.ok_or(WorkspaceError::not_found())?;
537538

538-
let (statement, stmt_range, text) = match doc
539-
.iter_statements_with_text_and_range()
540-
.find(|(_, r, _)| r.contains(params.position))
541-
{
542-
Some(s) => s,
543-
None => return Ok(CompletionsResult::default()),
544-
};
539+
let (statement, stmt_range, text) =
540+
match doc.iter_statements_with_text_and_range().find(|(_, r, _)| {
541+
let expanded_range = TextRange::new(
542+
r.start(),
543+
r.end().checked_add(TextSize::new(2)).unwrap_or(r.end()),
544+
);
545+
546+
expanded_range.contains(params.position)
547+
}) {
548+
Some(s) => s,
549+
None => return Ok(CompletionsResult::default()),
550+
};
545551

546552
// `offset` is the position in the document,
547553
// but we need the position within the *statement*.

0 commit comments

Comments
 (0)