Skip to content

Commit cab3bbe

Browse files
fix(completions): cursor on dot, other issues (#359)
* fix(completions): cursor on dot, other issues * ? * test belongs here * refs
1 parent f6fda31 commit cab3bbe

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

crates/pgt_completions/src/providers/columns.rs

+65
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,69 @@ mod tests {
224224
"`email` not present in first four completion items."
225225
);
226226
}
227+
228+
#[tokio::test]
229+
async fn prefers_columns_of_mentioned_tables() {
230+
let setup = r#"
231+
create schema private;
232+
233+
create table private.users (
234+
id1 serial primary key,
235+
name1 text,
236+
address1 text,
237+
email1 text
238+
);
239+
240+
create table public.users (
241+
id2 serial primary key,
242+
name2 text,
243+
address2 text,
244+
email2 text
245+
);
246+
"#;
247+
248+
{
249+
let test_case = TestCase {
250+
message: "",
251+
query: format!(r#"select {} from users"#, CURSOR_POS),
252+
label: "suggests from table",
253+
description: "",
254+
};
255+
256+
let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await;
257+
let params = get_test_params(&tree, &cache, test_case.get_input_query());
258+
let results = complete(params);
259+
260+
assert_eq!(
261+
results
262+
.into_iter()
263+
.take(4)
264+
.map(|item| item.label)
265+
.collect::<Vec<String>>(),
266+
vec!["address2", "email2", "id2", "name2"]
267+
);
268+
}
269+
270+
{
271+
let test_case = TestCase {
272+
message: "",
273+
query: format!(r#"select {} from private.users"#, CURSOR_POS),
274+
label: "suggests from table",
275+
description: "",
276+
};
277+
278+
let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await;
279+
let params = get_test_params(&tree, &cache, test_case.get_input_query());
280+
let results = complete(params);
281+
282+
assert_eq!(
283+
results
284+
.into_iter()
285+
.take(4)
286+
.map(|item| item.label)
287+
.collect::<Vec<String>>(),
288+
vec!["address1", "email1", "id1", "name1"]
289+
);
290+
}
291+
}
227292
}

crates/pgt_completions/src/sanitization.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::borrow::Cow;
1+
use std::{borrow::Cow, cmp::max};
22

33
use pgt_text_size::TextSize;
44

@@ -24,6 +24,7 @@ where
2424
if cursor_inbetween_nodes(params.tree, params.position)
2525
|| cursor_prepared_to_write_token_after_last_node(params.tree, params.position)
2626
|| cursor_before_semicolon(params.tree, params.position)
27+
|| cursor_on_a_dot(&params.text, params.position)
2728
{
2829
SanitizedCompletionParams::with_adjusted_sql(params)
2930
} else {
@@ -44,12 +45,13 @@ where
4445

4546
let mut sql_iter = params.text.chars();
4647

47-
for idx in 0..cursor_pos + 1 {
48+
let max = max(cursor_pos + 1, params.text.len());
49+
50+
for idx in 0..max {
4851
match sql_iter.next() {
4952
Some(c) => {
5053
if idx == cursor_pos {
5154
sql.push_str(SANITIZED_TOKEN);
52-
sql.push(' ');
5355
}
5456
sql.push(c);
5557
}
@@ -149,6 +151,11 @@ fn cursor_prepared_to_write_token_after_last_node(
149151
cursor_pos == tree.root_node().end_byte() + 1
150152
}
151153

154+
fn cursor_on_a_dot(sql: &str, position: TextSize) -> bool {
155+
let position: usize = position.into();
156+
sql.chars().nth(position - 1).is_some_and(|c| c == '.')
157+
}
158+
152159
fn cursor_before_semicolon(tree: &tree_sitter::Tree, position: TextSize) -> bool {
153160
let mut cursor = tree.walk();
154161
let mut leaf_node = tree.root_node();
@@ -198,7 +205,7 @@ mod tests {
198205
use pgt_text_size::TextSize;
199206

200207
use crate::sanitization::{
201-
cursor_before_semicolon, cursor_inbetween_nodes,
208+
cursor_before_semicolon, cursor_inbetween_nodes, cursor_on_a_dot,
202209
cursor_prepared_to_write_token_after_last_node,
203210
};
204211

@@ -263,6 +270,20 @@ mod tests {
263270
));
264271
}
265272

273+
#[test]
274+
fn on_a_dot() {
275+
let input = "select * from private.";
276+
277+
// select * from private.| <-- on a dot
278+
assert!(cursor_on_a_dot(input, TextSize::new(22)));
279+
280+
// select * from private|. <-- before the dot
281+
assert!(!cursor_on_a_dot(input, TextSize::new(21)));
282+
283+
// select * from private. | <-- too far off the dot
284+
assert!(!cursor_on_a_dot(input, TextSize::new(23)));
285+
}
286+
266287
#[test]
267288
fn test_cursor_before_semicolon() {
268289
// Idx "13" is the exlusive end of `select * from` (first space after from)

0 commit comments

Comments
 (0)