Skip to content

Commit 28ca671

Browse files
Merge branch 'feat/compl-filtering' of https://github.com/supabase-community/postgres_lsp into fix/compl-item-sorting
2 parents 8ca854b + 3120724 commit 28ca671

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

crates/pgt_completions/src/providers/columns.rs

+66-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ pub fn complete_columns<'a>(ctx: &CompletionContext<'a>, builder: &mut Completio
2727
mod tests {
2828
use crate::{
2929
CompletionItem, CompletionItemKind, complete,
30-
test_helper::{
31-
CURSOR_POS, InputQuery, get_test_deps, get_test_params, test_against_connection_string,
32-
},
30+
test_helper::{CURSOR_POS, InputQuery, get_test_deps, get_test_params},
3331
};
3432

3533
struct TestCase {
@@ -262,4 +260,69 @@ mod tests {
262260
.any(|item| item.kind == CompletionItemKind::Column)
263261
);
264262
}
263+
264+
#[tokio::test]
265+
async fn prefers_columns_of_mentioned_tables() {
266+
let setup = r#"
267+
create schema private;
268+
269+
create table private.users (
270+
id1 serial primary key,
271+
name1 text,
272+
address1 text,
273+
email1 text
274+
);
275+
276+
create table public.users (
277+
id2 serial primary key,
278+
name2 text,
279+
address2 text,
280+
email2 text
281+
);
282+
"#;
283+
284+
{
285+
let test_case = TestCase {
286+
message: "",
287+
query: format!(r#"select {} from users"#, CURSOR_POS),
288+
label: "suggests from table",
289+
description: "",
290+
};
291+
292+
let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await;
293+
let params = get_test_params(&tree, &cache, test_case.get_input_query());
294+
let results = complete(params);
295+
296+
assert_eq!(
297+
results
298+
.into_iter()
299+
.take(4)
300+
.map(|item| item.label)
301+
.collect::<Vec<String>>(),
302+
vec!["address2", "email2", "id2", "name2"]
303+
);
304+
}
305+
306+
{
307+
let test_case = TestCase {
308+
message: "",
309+
query: format!(r#"select {} from private.users"#, CURSOR_POS),
310+
label: "suggests from table",
311+
description: "",
312+
};
313+
314+
let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await;
315+
let params = get_test_params(&tree, &cache, test_case.get_input_query());
316+
let results = complete(params);
317+
318+
assert_eq!(
319+
results
320+
.into_iter()
321+
.take(4)
322+
.map(|item| item.label)
323+
.collect::<Vec<String>>(),
324+
vec!["address1", "email1", "id1", "name1"]
325+
);
326+
}
327+
}
265328
}

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)