From 0701724957f4b614ccd35a3fe49af940bbe347bf Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 21 Apr 2025 16:22:00 +0200 Subject: [PATCH 1/4] fix(completions): cursor on dot, other issues --- crates/pgt_completions/src/sanitization.rs | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/crates/pgt_completions/src/sanitization.rs b/crates/pgt_completions/src/sanitization.rs index 710d488d2..f2f912bc0 100644 --- a/crates/pgt_completions/src/sanitization.rs +++ b/crates/pgt_completions/src/sanitization.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, cmp::max}; use pgt_text_size::TextSize; @@ -24,6 +24,7 @@ where if cursor_inbetween_nodes(params.tree, params.position) || cursor_prepared_to_write_token_after_last_node(params.tree, params.position) || cursor_before_semicolon(params.tree, params.position) + || cursor_on_a_dot(¶ms.text, params.position) { SanitizedCompletionParams::with_adjusted_sql(params) } else { @@ -44,12 +45,13 @@ where let mut sql_iter = params.text.chars(); - for idx in 0..cursor_pos + 1 { + let max = max(cursor_pos + 1, params.text.len()); + + for idx in 0..max { match sql_iter.next() { Some(c) => { if idx == cursor_pos { sql.push_str(SANITIZED_TOKEN); - sql.push(' '); } sql.push(c); } @@ -149,6 +151,11 @@ fn cursor_prepared_to_write_token_after_last_node( cursor_pos == tree.root_node().end_byte() + 1 } +fn cursor_on_a_dot(sql: &str, position: TextSize) -> bool { + let position: usize = position.into(); + sql.chars().nth(position - 1).is_some_and(|c| c == '.') +} + fn cursor_before_semicolon(tree: &tree_sitter::Tree, position: TextSize) -> bool { let mut cursor = tree.walk(); let mut leaf_node = tree.root_node(); @@ -198,7 +205,7 @@ mod tests { use pgt_text_size::TextSize; use crate::sanitization::{ - cursor_before_semicolon, cursor_inbetween_nodes, + cursor_before_semicolon, cursor_inbetween_nodes, cursor_on_a_dot, cursor_prepared_to_write_token_after_last_node, }; @@ -212,7 +219,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input, None).unwrap(); + let mut tree = parser.parse(input.to_string(), None).unwrap(); // select | from users; <-- just right, one space after select token, one space before from assert!(cursor_inbetween_nodes(&mut tree, TextSize::new(7))); @@ -236,7 +243,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input, None).unwrap(); + let mut tree = parser.parse(input.to_string(), None).unwrap(); // select * from| <-- still on previous token assert!(!cursor_prepared_to_write_token_after_last_node( @@ -263,6 +270,20 @@ mod tests { )); } + #[test] + fn on_a_dot() { + let input = "select * from private."; + + // select * from private.| <-- on a dot + assert!(cursor_on_a_dot(&input, TextSize::new(22))); + + // select * from private|. <-- before the dot + assert!(!cursor_on_a_dot(&input, TextSize::new(21))); + + // select * from private. | <-- too far off the dot + assert!(!cursor_on_a_dot(&input, TextSize::new(23))); + } + #[test] fn test_cursor_before_semicolon() { // Idx "13" is the exlusive end of `select * from` (first space after from) @@ -274,7 +295,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input, None).unwrap(); + let mut tree = parser.parse(input.to_string(), None).unwrap(); // select * from ;| <-- it's after the statement assert!(!cursor_before_semicolon(&mut tree, TextSize::new(19))); From e3e54e45d54721b3ab997a01a049009786a784db Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 21 Apr 2025 17:07:57 +0200 Subject: [PATCH 2/4] ? --- crates/pgt_completions/src/sanitization.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pgt_completions/src/sanitization.rs b/crates/pgt_completions/src/sanitization.rs index f2f912bc0..921c6e1f6 100644 --- a/crates/pgt_completions/src/sanitization.rs +++ b/crates/pgt_completions/src/sanitization.rs @@ -219,7 +219,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input.to_string(), None).unwrap(); + let mut tree = parser.parse(input, None).unwrap(); // select | from users; <-- just right, one space after select token, one space before from assert!(cursor_inbetween_nodes(&mut tree, TextSize::new(7))); @@ -243,7 +243,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input.to_string(), None).unwrap(); + let mut tree = parser.parse(input, None).unwrap(); // select * from| <-- still on previous token assert!(!cursor_prepared_to_write_token_after_last_node( @@ -295,7 +295,7 @@ mod tests { .set_language(tree_sitter_sql::language()) .expect("Error loading sql language"); - let mut tree = parser.parse(input.to_string(), None).unwrap(); + let mut tree = parser.parse(input, None).unwrap(); // select * from ;| <-- it's after the statement assert!(!cursor_before_semicolon(&mut tree, TextSize::new(19))); From ac122a592fd21c622d414aed5cfb02ea2f143551 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 21 Apr 2025 17:12:05 +0200 Subject: [PATCH 3/4] test belongs here --- .../pgt_completions/src/providers/columns.rs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/crates/pgt_completions/src/providers/columns.rs b/crates/pgt_completions/src/providers/columns.rs index b792ba2ca..d1c3e110c 100644 --- a/crates/pgt_completions/src/providers/columns.rs +++ b/crates/pgt_completions/src/providers/columns.rs @@ -224,4 +224,69 @@ mod tests { "`email` not present in first four completion items." ); } + + #[tokio::test] + async fn prefers_columns_of_mentioned_tables() { + let setup = r#" + create schema private; + + create table private.users ( + id1 serial primary key, + name1 text, + address1 text, + email1 text + ); + + create table public.users ( + id2 serial primary key, + name2 text, + address2 text, + email2 text + ); + "#; + + { + let test_case = TestCase { + message: "", + query: format!(r#"select {} from users"#, CURSOR_POS), + label: "suggests from table", + description: "", + }; + + let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await; + let params = get_test_params(&tree, &cache, test_case.get_input_query()); + let results = complete(params); + + assert_eq!( + results + .into_iter() + .take(4) + .map(|item| item.label) + .collect::>(), + vec!["address2", "email2", "id2", "name2"] + ); + } + + { + let test_case = TestCase { + message: "", + query: format!(r#"select {} from private.users"#, CURSOR_POS), + label: "suggests from table", + description: "", + }; + + let (tree, cache) = get_test_deps(setup, test_case.get_input_query()).await; + let params = get_test_params(&tree, &cache, test_case.get_input_query()); + let results = complete(params); + + assert_eq!( + results + .into_iter() + .take(4) + .map(|item| item.label) + .collect::>(), + vec!["address1", "email1", "id1", "name1"] + ); + } + } } From f24c0724ede290483bf730ec8eba7e12c2b8187c Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 21 Apr 2025 17:24:39 +0200 Subject: [PATCH 4/4] refs --- crates/pgt_completions/src/sanitization.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pgt_completions/src/sanitization.rs b/crates/pgt_completions/src/sanitization.rs index 921c6e1f6..59eb609f5 100644 --- a/crates/pgt_completions/src/sanitization.rs +++ b/crates/pgt_completions/src/sanitization.rs @@ -275,13 +275,13 @@ mod tests { let input = "select * from private."; // select * from private.| <-- on a dot - assert!(cursor_on_a_dot(&input, TextSize::new(22))); + assert!(cursor_on_a_dot(input, TextSize::new(22))); // select * from private|. <-- before the dot - assert!(!cursor_on_a_dot(&input, TextSize::new(21))); + assert!(!cursor_on_a_dot(input, TextSize::new(21))); // select * from private. | <-- too far off the dot - assert!(!cursor_on_a_dot(&input, TextSize::new(23))); + assert!(!cursor_on_a_dot(input, TextSize::new(23))); } #[test]