Skip to content

Commit e4a7a7a

Browse files
refactor(treesitter): rely more on treesitter grammar than business logic (#562)
1 parent 6a27850 commit e4a7a7a

File tree

14 files changed

+513
-1263
lines changed

14 files changed

+513
-1263
lines changed

crates/pgt_completions/src/providers/roles.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ mod tests {
292292
QueryWithCursorPosition::cursor_marker()
293293
),
294294
format!(
295-
"revoke all on table userse from owner, {}",
295+
"revoke all on table users from owner, {}",
296296
QueryWithCursorPosition::cursor_marker()
297297
),
298298
];

crates/pgt_completions/src/relevance/filtering.rs

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use pgt_schema_cache::ProcKind;
2-
use pgt_treesitter::context::{NodeUnderCursor, TreesitterContext, WrappingClause, WrappingNode};
2+
use pgt_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode};
33

44
use super::CompletionRelevanceData;
55

@@ -17,7 +17,11 @@ impl<'a> From<CompletionRelevanceData<'a>> for CompletionFilter<'a> {
1717
impl CompletionFilter<'_> {
1818
pub fn is_relevant(&self, ctx: &TreesitterContext) -> Option<()> {
1919
self.completable_context(ctx)?;
20-
self.check_clause(ctx)?;
20+
21+
self.check_node_type(ctx)
22+
// we want to rely on treesitter more, so checking the clause is a fallback
23+
.or_else(|| self.check_clause(ctx))?;
24+
2125
self.check_invocation(ctx)?;
2226
self.check_mentioned_schema_or_alias(ctx)?;
2327

@@ -67,30 +71,42 @@ impl CompletionFilter<'_> {
6771
}
6872

6973
// No autocompletions if there are two identifiers without a separator.
70-
if ctx.node_under_cursor.as_ref().is_some_and(|n| match n {
71-
NodeUnderCursor::TsNode(node) => node.prev_sibling().is_some_and(|p| {
72-
(p.kind() == "identifier" || p.kind() == "object_reference")
73-
&& n.kind() == "identifier"
74-
}),
75-
NodeUnderCursor::CustomNode { .. } => false,
74+
if ctx.node_under_cursor.as_ref().is_some_and(|node| {
75+
node.prev_sibling().is_some_and(|p| {
76+
(p.kind() == "any_identifier" || p.kind() == "object_reference")
77+
&& node.kind() == "any_identifier"
78+
})
7679
}) {
7780
return None;
7881
}
7982

8083
// no completions if we're right after an asterisk:
8184
// `select * {}`
82-
if ctx.node_under_cursor.as_ref().is_some_and(|n| match n {
83-
NodeUnderCursor::TsNode(node) => node
84-
.prev_sibling()
85-
.is_some_and(|p| (p.kind() == "all_fields") && n.kind() == "identifier"),
86-
NodeUnderCursor::CustomNode { .. } => false,
85+
if ctx.node_under_cursor.as_ref().is_some_and(|node| {
86+
node.prev_sibling()
87+
.is_some_and(|p| (p.kind() == "all_fields") && node.kind() == "any_identifier")
8788
}) {
8889
return None;
8990
}
9091

9192
Some(())
9293
}
9394

95+
fn check_node_type(&self, ctx: &TreesitterContext) -> Option<()> {
96+
let kind = ctx.node_under_cursor.as_ref().map(|n| n.kind())?;
97+
98+
let is_allowed = match kind {
99+
"column_identifier" => {
100+
matches!(self.data, CompletionRelevanceData::Column(_))
101+
&& !ctx.matches_ancestor_history(&["insert_values", "field"])
102+
&& !ctx.node_under_cursor_is_within_field_name("binary_expr_right")
103+
}
104+
_ => false,
105+
};
106+
107+
if is_allowed { Some(()) } else { None }
108+
}
109+
94110
fn check_clause(&self, ctx: &TreesitterContext) -> Option<()> {
95111
ctx.wrapping_clause_type
96112
.as_ref()
@@ -99,6 +115,9 @@ impl CompletionFilter<'_> {
99115
CompletionRelevanceData::Table(_) => match clause {
100116
WrappingClause::From | WrappingClause::Update => true,
101117

118+
WrappingClause::RevokeStatement | WrappingClause::GrantStatement => ctx
119+
.matches_ancestor_history(&["grantable_on_table", "object_reference"]),
120+
102121
WrappingClause::Join { on_node: None } => true,
103122
WrappingClause::Join { on_node: Some(on) } => ctx
104123
.node_under_cursor
@@ -202,6 +221,14 @@ impl CompletionFilter<'_> {
202221
| WrappingClause::Update
203222
| WrappingClause::Delete => true,
204223

224+
WrappingClause::RevokeStatement | WrappingClause::GrantStatement => {
225+
(ctx.matches_ancestor_history(&[
226+
"grantable_on_table",
227+
"object_reference",
228+
]) && ctx.schema_or_alias_name.is_none())
229+
|| ctx.matches_ancestor_history(&["grantable_on_all"])
230+
}
231+
205232
WrappingClause::Where => {
206233
ctx.before_cursor_matches_kind(&["keyword_and", "keyword_where"])
207234
}
@@ -238,13 +265,23 @@ impl CompletionFilter<'_> {
238265
}
239266

240267
CompletionRelevanceData::Role(_) => match clause {
241-
WrappingClause::DropRole
242-
| WrappingClause::AlterRole
243-
| WrappingClause::ToRoleAssignment => true,
268+
WrappingClause::DropRole | WrappingClause::AlterRole => true,
244269

245270
WrappingClause::SetStatement => ctx
246271
.before_cursor_matches_kind(&["keyword_role", "keyword_authorization"]),
247272

273+
WrappingClause::RevokeStatement | WrappingClause::GrantStatement => {
274+
ctx.matches_ancestor_history(&["role_specification"])
275+
|| ctx.node_under_cursor.as_ref().is_some_and(|k| {
276+
k.kind() == "any_identifier"
277+
&& ctx.before_cursor_matches_kind(&[
278+
"keyword_grant",
279+
"keyword_revoke",
280+
"keyword_for",
281+
])
282+
})
283+
}
284+
248285
WrappingClause::AlterPolicy | WrappingClause::CreatePolicy => {
249286
ctx.before_cursor_matches_kind(&["keyword_to"])
250287
&& ctx.matches_ancestor_history(&["policy_to_role"])

crates/pgt_hover/src/hovered_node.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ impl HoveredNode {
3232
let under_cursor = ctx.node_under_cursor.as_ref()?;
3333

3434
match under_cursor.kind() {
35-
"identifier" if ctx.matches_ancestor_history(&["relation", "object_reference"]) => {
35+
"any_identifier"
36+
if ctx.matches_ancestor_history(&["relation", "object_reference"])
37+
|| ctx
38+
.matches_ancestor_history(&["grantable_on_table", "object_reference"]) =>
39+
{
3640
let num_sibs = ctx.num_siblings();
3741
if ctx.node_under_cursor_is_nth_child(1) && num_sibs > 0 {
3842
return Some(HoveredNode::Schema(NodeIdentification::Name(node_content)));
@@ -48,7 +52,7 @@ impl HoveredNode {
4852
}
4953
}
5054

51-
"identifier"
55+
"any_identifier"
5256
if ctx.matches_ancestor_history(&["object_reference"])
5357
&& ctx.wrapping_clause_type.as_ref().is_some_and(|clause| {
5458
matches!(
@@ -69,7 +73,7 @@ impl HoveredNode {
6973
}
7074
}
7175

72-
"identifier" if ctx.matches_ancestor_history(&["field"]) => {
76+
"column_identifier" => {
7377
if let Some(table_or_alias) = ctx.schema_or_alias_name.as_ref() {
7478
Some(HoveredNode::Column(NodeIdentification::SchemaAndName((
7579
table_or_alias.clone(),
@@ -80,7 +84,9 @@ impl HoveredNode {
8084
}
8185
}
8286

83-
"identifier" if ctx.matches_ancestor_history(&["invocation", "object_reference"]) => {
87+
"any_identifier"
88+
if ctx.matches_ancestor_history(&["invocation", "object_reference"]) =>
89+
{
8490
if let Some(schema) = ctx.schema_or_alias_name.as_ref() {
8591
Some(HoveredNode::Function(NodeIdentification::SchemaAndName((
8692
schema.clone(),
@@ -93,11 +99,20 @@ impl HoveredNode {
9399
}
94100
}
95101

96-
"identifier" if ctx.matches_one_of_ancestors(&["alter_role", "policy_to_role"]) => {
102+
"any_identifier"
103+
if ctx.matches_one_of_ancestors(&[
104+
"alter_role",
105+
"policy_to_role",
106+
"role_specification",
107+
]) || ctx.before_cursor_matches_kind(&["keyword_revoke"]) =>
108+
{
109+
Some(HoveredNode::Role(NodeIdentification::Name(node_content)))
110+
}
111+
"grant_role" | "policy_role" => {
97112
Some(HoveredNode::Role(NodeIdentification::Name(node_content)))
98113
}
99114

100-
"identifier"
115+
"any_identifier"
101116
if (
102117
// hover over custom type in `create table` or `returns`
103118
(ctx.matches_ancestor_history(&["type", "object_reference"])
@@ -127,16 +142,12 @@ impl HoveredNode {
127142
}
128143
}
129144

130-
"revoke_role" | "grant_role" | "policy_role" => {
131-
Some(HoveredNode::Role(NodeIdentification::Name(node_content)))
132-
}
133-
134145
// quoted columns
135146
"literal" if ctx.matches_ancestor_history(&["select_expression", "term"]) => {
136147
Some(HoveredNode::Column(NodeIdentification::Name(node_content)))
137148
}
138149

139-
"revoke_table" | "grant_table" => {
150+
"grant_table" => {
140151
if let Some(schema) = ctx.schema_or_alias_name.as_ref() {
141152
Some(HoveredNode::Table(NodeIdentification::SchemaAndName((
142153
schema.clone(),

crates/pgt_treesitter/src/context/base_parser.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ impl TokenNavigator {
1414
.is_some_and(|c| options.contains(&c.get_word_without_quotes().as_str()))
1515
}
1616

17-
pub(crate) fn prev_matches(&self, options: &[&str]) -> bool {
18-
self.previous_token
19-
.as_ref()
20-
.is_some_and(|t| options.contains(&t.get_word_without_quotes().as_str()))
21-
}
22-
2317
pub(crate) fn advance(&mut self) -> Option<WordWithIndex> {
2418
// we can't peek back n an iterator, so we'll have to keep track manually.
2519
self.previous_token = self.current_token.take();

0 commit comments

Comments
 (0)