Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions crates/pgls_completions/src/providers/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ mod tests {
use crate::{
CompletionItem, CompletionItemKind, complete,
test_helper::{
CompletionAssertion, assert_complete_results, assert_no_complete_results,
get_test_deps, get_test_params,
CompletionAssertion, assert_complete_results, get_test_deps, get_test_params,
},
};

Expand Down Expand Up @@ -717,18 +716,6 @@ mod tests {
&pool,
)
.await;

// no completions in the values list!
assert_no_complete_results(
format!(
"insert into instruments (id, name) values ({})",
QueryWithCursorPosition::cursor_marker()
)
.as_str(),
None,
&pool,
)
.await;
}

#[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")]
Expand Down
32 changes: 14 additions & 18 deletions crates/pgls_completions/src/providers/helper.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pgls_text_size::{TextRange, TextSize};
use pgls_text_size::TextRange;
use pgls_treesitter::TreesitterContext;

use crate::{is_sanitized_token_with_quote, remove_sanitized_token};
Expand All @@ -9,29 +9,25 @@ pub(crate) fn node_text_surrounded_by_quotes(ctx: &TreesitterContext) -> bool {
}

pub(crate) fn get_range_to_replace(ctx: &TreesitterContext) -> TextRange {
match ctx.node_under_cursor.as_ref() {
Some(node) => {
let content = ctx.get_node_under_cursor_content().unwrap_or("".into());
let content = content.as_str();
let node = &ctx.node_under_cursor;
let content = ctx.get_node_under_cursor_content().unwrap_or("".into());
let content = content.as_str();

let sanitized = remove_sanitized_token(content);
let length = sanitized.len();
let sanitized = remove_sanitized_token(content);
let length = sanitized.len();

let mut start = node.start_byte();
let mut end = start + length;
let mut start = node.start_byte();
let mut end = start + length;

if sanitized.starts_with('"') && sanitized.ends_with('"') {
start += 1;
if sanitized.starts_with('"') && sanitized.ends_with('"') {
start += 1;

if sanitized.len() > 1 {
end -= 1;
}
}

TextRange::new(start.try_into().unwrap(), end.try_into().unwrap())
if sanitized.len() > 1 {
end -= 1;
}
None => TextRange::empty(TextSize::new(0)),
}

TextRange::new(start.try_into().unwrap(), end.try_into().unwrap())
}

pub(crate) fn only_leading_quote(ctx: &TreesitterContext) -> bool {
Expand Down
184 changes: 83 additions & 101 deletions crates/pgls_completions/src/relevance/filtering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ impl CompletionFilter<'_> {
return None;
}

let current_node_kind = ctx
.node_under_cursor
.as_ref()
.map(|n| n.kind())
.unwrap_or("");
let current_node_kind = ctx.node_under_cursor.kind();

if current_node_kind.starts_with("keyword_")
|| current_node_kind == "="
Expand Down Expand Up @@ -70,30 +66,24 @@ impl CompletionFilter<'_> {
}
}

if ctx
.node_under_cursor
.as_ref()
.is_some_and(|n| n.kind() == "any_identifier")
&& ctx.matches_ancestor_history(&["alias"])
if ctx.node_under_cursor.kind() == "any_identifier"
&& ctx.history_ends_with(&["alias", "any_identifier"])
{
return None;
}

// No autocompletions if there are two identifiers without a separator.
if ctx.node_under_cursor.as_ref().is_some_and(|node| {
node.prev_sibling().is_some_and(|p| {
(p.kind() == "any_identifier" || p.kind() == "object_reference")
&& node.kind() == "any_identifier"
})
if ctx.node_under_cursor.prev_sibling().is_some_and(|p| {
(p.kind() == "any_identifier" || p.kind() == "object_reference")
&& ctx.node_under_cursor.kind() == "any_identifier"
}) {
return None;
}

// no completions if we're right after an asterisk:
// `select * {}`
if ctx.node_under_cursor.as_ref().is_some_and(|node| {
node.prev_sibling()
.is_some_and(|p| (p.kind() == "all_fields") && node.kind() == "any_identifier")
if ctx.node_under_cursor.prev_sibling().is_some_and(|p| {
(p.kind() == "all_fields") && ctx.node_under_cursor.kind() == "any_identifier"
}) {
return None;
}
Expand All @@ -102,7 +92,7 @@ impl CompletionFilter<'_> {
}

fn check_specific_node_type(&self, ctx: &TreesitterContext) -> Option<()> {
let kind = ctx.node_under_cursor.as_ref().map(|n| n.kind())?;
let kind = ctx.node_under_cursor.kind();

let is_allowed = match kind {
"column_identifier" => matches!(self.data, CompletionRelevanceData::Column(_)),
Expand All @@ -112,67 +102,55 @@ impl CompletionFilter<'_> {
"table_identifier" => matches!(self.data, CompletionRelevanceData::Table(_)),
"policy_identifier" => matches!(self.data, CompletionRelevanceData::Policy(_)),

"any_identifier" => {
if false || ctx.matches_ancestor_history(&["insert_values", "object_reference"]) {
false
} else {
match self.data {
CompletionRelevanceData::Column(_) => {
ctx.node_under_cursor_is_within_field_name(&[
"object_reference_1of1",
"object_reference_2of2",
"object_reference_3of3",
"column_reference_1of1",
"column_reference_2of2",
"column_reference_3of3",
]) && !ctx
.node_under_cursor_is_within_field_name(&["binary_expr_right"])
&& !ctx.matches_ancestor_history(&[
"insert_values",
"object_reference",
])
}
"any_identifier" => match self.data {
CompletionRelevanceData::Column(_) => {
ctx.node_under_cursor_is_within_field(&[
"object_reference_1of1",
"object_reference_2of2",
"object_reference_3of3",
"column_reference_1of1",
"column_reference_2of2",
"column_reference_3of3",
]) && !ctx.node_under_cursor_is_within_field(&["binary_expr_right"])
}

CompletionRelevanceData::Schema(_) => ctx
.node_under_cursor_is_within_field_name(&[
"object_reference_1of1",
"object_reference_1of2",
"object_reference_1of3",
"type_reference_1of1",
"table_reference_1of1",
"column_reference_1of1",
"column_reference_1of2",
"function_reference_1of1",
]),
CompletionRelevanceData::Schema(_) => ctx.node_under_cursor_is_within_field(&[
"object_reference_1of1",
"object_reference_1of2",
"object_reference_1of3",
"type_reference_1of1",
"table_reference_1of1",
"column_reference_1of1",
"column_reference_1of2",
"function_reference_1of1",
]),

CompletionRelevanceData::Function(f) => {
ctx.node_under_cursor_is_within_field(&[
"object_reference_1of1",
"object_reference_2of2",
"function_reference_1of1",
]) && !(ctx.history_ends_with(&[
"check_or_using_clause",
"binary_expression",
"object_reference",
"any_identifier",
]) && matches!(f.kind, ProcKind::Aggregate))
}

CompletionRelevanceData::Function(f) => {
ctx.node_under_cursor_is_within_field_name(&[
"object_reference_1of1",
"object_reference_2of2",
"function_reference_1of1",
]) && !(ctx.matches_ancestor_history(&[
"check_or_using_clause",
"binary_expression",
"object_reference",
]) && matches!(f.kind, ProcKind::Aggregate))
}
CompletionRelevanceData::Table(_) => ctx.node_under_cursor_is_within_field(&[
"object_reference_1of1",
"object_reference_1of2",
"object_reference_2of2",
"object_reference_2of3",
"table_reference_1of1",
"column_reference_1of1",
"column_reference_1of2",
"column_reference_2of2",
]),

CompletionRelevanceData::Table(_) => ctx
.node_under_cursor_is_within_field_name(&[
"object_reference_1of1",
"object_reference_1of2",
"object_reference_2of2",
"object_reference_2of3",
"table_reference_1of1",
"column_reference_1of1",
"column_reference_1of2",
"column_reference_2of2",
]),

_ => false,
}
}
}
_ => false,
},

_ => false,
};
Expand All @@ -189,21 +167,27 @@ impl CompletionFilter<'_> {
WrappingClause::From | WrappingClause::Update => true,

WrappingClause::RevokeStatement | WrappingClause::GrantStatement => ctx
.matches_ancestor_history(&["grantable_on_table", "object_reference"]),
.history_ends_with(&[
"grantable_on_table",
"object_reference",
"any_identifier",
]),

WrappingClause::Join { on_node: None } => true,
WrappingClause::Join { on_node: Some(on) } => ctx
.node_under_cursor
.as_ref()
.is_some_and(|cn| cn.start_byte() < on.end_byte()),
WrappingClause::Join { on_node: Some(on) } => {
ctx.node_under_cursor.start_byte() < on.end_byte()
}

WrappingClause::Insert => {
ctx.wrapping_node_kind
.as_ref()
.is_none_or(|n| n != &WrappingNode::List)
&& (ctx.before_cursor_matches_kind(&["keyword_into"])
|| (ctx.before_cursor_matches_kind(&["."])
&& ctx.matches_ancestor_history(&["object_reference"])))
&& ctx.history_ends_with(&[
"object_reference",
"any_identifier",
])))
}

WrappingClause::DropTable | WrappingClause::AlterTable => ctx
Expand All @@ -216,7 +200,7 @@ impl CompletionFilter<'_> {
WrappingClause::CreatePolicy
| WrappingClause::AlterPolicy
| WrappingClause::DropPolicy => {
ctx.matches_ancestor_history(&["object_reference"])
ctx.history_ends_with(&["object_reference", "any_identifier"])
&& ctx.before_cursor_matches_kind(&["keyword_on", "."])
}

Expand All @@ -239,10 +223,9 @@ impl CompletionFilter<'_> {

// We can complete columns in JOIN cluases, but only if we are after the
// ON node in the "ON u.id = posts.user_id" part.
WrappingClause::Join { on_node: Some(on) } => ctx
.node_under_cursor
.as_ref()
.is_some_and(|cn| cn.start_byte() >= on.end_byte()),
WrappingClause::Join { on_node: Some(on) } => {
ctx.node_under_cursor.start_byte() >= on.end_byte()
}

// we are in a JOIN, but definitely not after an ON
WrappingClause::Join { on_node: None } => false,
Expand All @@ -256,7 +239,7 @@ impl CompletionFilter<'_> {
WrappingClause::Where => {
ctx.before_cursor_matches_kind(&["keyword_and", "keyword_where"])
|| (ctx.before_cursor_matches_kind(&["field_qualifier"])
&& ctx.matches_ancestor_history(&["field"]))
&& ctx.history_ends_with(&["field", "any_identifier"]))
}

WrappingClause::CheckOrUsingClause => {
Expand Down Expand Up @@ -294,11 +277,12 @@ impl CompletionFilter<'_> {
| WrappingClause::Delete => true,

WrappingClause::RevokeStatement | WrappingClause::GrantStatement => {
(ctx.matches_ancestor_history(&[
(ctx.history_ends_with(&[
"grantable_on_table",
"object_reference",
"any_identifier",
]) && !ctx.has_any_qualifier())
|| ctx.matches_ancestor_history(&["grantable_on_all"])
|| ctx.history_ends_with(&["grantable_on_all", "any_identifier"])
}

WrappingClause::Where => {
Expand Down Expand Up @@ -343,20 +327,18 @@ impl CompletionFilter<'_> {
.before_cursor_matches_kind(&["keyword_role", "keyword_authorization"]),

WrappingClause::RevokeStatement | WrappingClause::GrantStatement => {
ctx.matches_ancestor_history(&["role_specification"])
|| ctx.node_under_cursor.as_ref().is_some_and(|k| {
k.kind() == "any_identifier"
&& ctx.before_cursor_matches_kind(&[
"keyword_grant",
"keyword_revoke",
"keyword_for",
])
})
ctx.history_ends_with(&["role_specification", "any_identifier"])
|| (ctx.node_under_cursor.kind() == "any_identifier"
&& ctx.before_cursor_matches_kind(&[
"keyword_grant",
"keyword_revoke",
"keyword_for",
]))
}

WrappingClause::AlterPolicy | WrappingClause::CreatePolicy => {
ctx.before_cursor_matches_kind(&["keyword_to"])
&& ctx.matches_ancestor_history(&["policy_to_role"])
&& ctx.history_ends_with(&["policy_to_role", "any_identifier"])
}

_ => false,
Expand Down
14 changes: 4 additions & 10 deletions crates/pgls_completions/src/relevance/scoring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,8 @@ impl CompletionScore<'_> {
WrappingClause::Delete => 10,
WrappingClause::From => 5,
WrappingClause::Join { on_node }
if on_node.is_none_or(|on| {
ctx.node_under_cursor
.as_ref()
.is_none_or(|n| n.end_byte() < on.start_byte())
}) =>
if on_node
.is_none_or(|on| ctx.node_under_cursor.end_byte() < on.start_byte()) =>
{
5
}
Expand All @@ -110,11 +107,8 @@ impl CompletionScore<'_> {
WrappingClause::Where => 10,
WrappingClause::CheckOrUsingClause => 0,
WrappingClause::Join { on_node }
if on_node.is_some_and(|on| {
ctx.node_under_cursor
.as_ref()
.is_some_and(|n| n.start_byte() > on.end_byte())
}) =>
if on_node
.is_some_and(|on| ctx.node_under_cursor.start_byte() > on.end_byte()) =>
{
// Users will probably join on primary keys
if col.is_primary_key { 20 } else { 10 }
Expand Down
2 changes: 2 additions & 0 deletions crates/pgls_completions/src/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,7 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>,
let params = get_test_params(&tree, &cache, query.into());
let items = complete(params);

println!("{items:#?}");

assert_eq!(items.len(), 0)
}
Loading
Loading