From 54ed2102f7e2ba64b61d93dc5d8feaecd351ffe4 Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 24 Oct 2025 20:24:29 +0200 Subject: [PATCH 1/2] adjust grammar, remove custom node --- .../src/relevance/filtering.rs | 19 +- crates/pgt_treesitter/src/context/mod.rs | 241 +++++--------- crates/pgt_treesitter_grammar/grammar.js | 300 ++++++++++-------- 3 files changed, 242 insertions(+), 318 deletions(-) diff --git a/crates/pgt_completions/src/relevance/filtering.rs b/crates/pgt_completions/src/relevance/filtering.rs index 118b8f43d..50681ce62 100644 --- a/crates/pgt_completions/src/relevance/filtering.rs +++ b/crates/pgt_completions/src/relevance/filtering.rs @@ -1,5 +1,5 @@ use pgt_schema_cache::ProcKind; -use pgt_treesitter::context::{NodeUnderCursor, TreesitterContext, WrappingClause, WrappingNode}; +use pgt_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; use super::CompletionRelevanceData; @@ -67,23 +67,20 @@ impl CompletionFilter<'_> { } // No autocompletions if there are two identifiers without a separator. - if ctx.node_under_cursor.as_ref().is_some_and(|n| match n { - NodeUnderCursor::TsNode(node) => node.prev_sibling().is_some_and(|p| { + if ctx.node_under_cursor.as_ref().is_some_and(|node| { + node.prev_sibling().is_some_and(|p| { (p.kind() == "identifier" || p.kind() == "object_reference") - && n.kind() == "identifier" - }), - NodeUnderCursor::CustomNode { .. } => false, + && node.kind() == "identifier" + }) }) { return None; } // no completions if we're right after an asterisk: // `select * {}` - if ctx.node_under_cursor.as_ref().is_some_and(|n| match n { - NodeUnderCursor::TsNode(node) => node - .prev_sibling() - .is_some_and(|p| (p.kind() == "all_fields") && n.kind() == "identifier"), - NodeUnderCursor::CustomNode { .. } => false, + if ctx.node_under_cursor.as_ref().is_some_and(|n| { + n.prev_sibling() + .is_some_and(|p| (p.kind() == "all_fields") && n.kind() == "identifier") }) { return None; } diff --git a/crates/pgt_treesitter/src/context/mod.rs b/crates/pgt_treesitter/src/context/mod.rs index 06de8c822..9e94249c5 100644 --- a/crates/pgt_treesitter/src/context/mod.rs +++ b/crates/pgt_treesitter/src/context/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::queries::{self, QueryResult, TreeSitterQueriesExecutor}; -use pgt_text_size::{TextRange, TextSize}; +use pgt_text_size::TextSize; #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum WrappingClause<'a> { @@ -57,46 +57,6 @@ pub enum WrappingNode { List, } -#[derive(Debug)] -pub enum NodeUnderCursor<'a> { - TsNode(tree_sitter::Node<'a>), - CustomNode { - text: String, - range: TextRange, - kind: String, - previous_node_kind: Option, - }, -} - -impl NodeUnderCursor<'_> { - pub fn start_byte(&self) -> usize { - match self { - NodeUnderCursor::TsNode(node) => node.start_byte(), - NodeUnderCursor::CustomNode { range, .. } => range.start().into(), - } - } - - pub fn end_byte(&self) -> usize { - match self { - NodeUnderCursor::TsNode(node) => node.end_byte(), - NodeUnderCursor::CustomNode { range, .. } => range.end().into(), - } - } - - pub fn kind(&self) -> &str { - match self { - NodeUnderCursor::TsNode(node) => node.kind(), - NodeUnderCursor::CustomNode { kind, .. } => kind.as_str(), - } - } -} - -impl<'a> From> for NodeUnderCursor<'a> { - fn from(node: tree_sitter::Node<'a>) -> Self { - NodeUnderCursor::TsNode(node) - } -} - impl TryFrom<&str> for WrappingNode { type Error = String; @@ -135,7 +95,7 @@ pub struct TreeSitterContextParams<'a> { #[derive(Debug)] pub struct TreesitterContext<'a> { - pub node_under_cursor: Option>, + pub node_under_cursor: Option>, pub tree: &'a tree_sitter::Tree, pub text: &'a str, @@ -284,10 +244,9 @@ impl<'a> TreesitterContext<'a> { } pub fn get_node_under_cursor_content(&self) -> Option { - match self.node_under_cursor.as_ref()? { - NodeUnderCursor::TsNode(node) => self.get_ts_node_content(node), - NodeUnderCursor::CustomNode { text, .. } => Some(text.clone()), - } + self.node_under_cursor + .as_ref() + .and_then(|n| self.get_ts_node_content(n)) } fn gather_tree_context(&mut self) { @@ -339,7 +298,7 @@ impl<'a> TreesitterContext<'a> { // prevent infinite recursion – this can happen with ERROR nodes if current_node_kind == parent_node_kind && ["ERROR", "program"].contains(&parent_node_kind) { - self.node_under_cursor = Some(NodeUnderCursor::from(current_node)); + self.node_under_cursor = Some(current_node); return; } @@ -408,7 +367,7 @@ impl<'a> TreesitterContext<'a> { if current_node.child_count() == 0 || current_node.first_child_for_byte(self.position).is_none() { - self.node_under_cursor = Some(NodeUnderCursor::from(current_node)); + self.node_under_cursor = Some(current_node); return; } @@ -644,27 +603,17 @@ impl<'a> TreesitterContext<'a> { } pub fn before_cursor_matches_kind(&self, kinds: &[&'static str]) -> bool { - self.node_under_cursor.as_ref().is_some_and(|under_cursor| { - match under_cursor { - NodeUnderCursor::TsNode(node) => { - let mut current = *node; - - // move up to the parent until we're at top OR we have a prev sibling - while current.prev_sibling().is_none() && current.parent().is_some() { - current = current.parent().unwrap(); - } - - current - .prev_sibling() - .is_some_and(|sib| kinds.contains(&sib.kind())) - } + self.node_under_cursor.as_ref().is_some_and(|node| { + let mut current = *node; - NodeUnderCursor::CustomNode { - previous_node_kind, .. - } => previous_node_kind - .as_ref() - .is_some_and(|k| kinds.contains(&k.as_str())), + // move up to the parent until we're at top OR we have a prev sibling + while current.prev_sibling().is_none() && current.parent().is_some() { + current = current.parent().unwrap(); } + + current + .prev_sibling() + .is_some_and(|sib| kinds.contains(&sib.kind())) }) } @@ -674,25 +623,20 @@ impl<'a> TreesitterContext<'a> { /// If the tree shows `relation > object_reference > identifier` and the "identifier" is a leaf node, /// you need to pass `&["relation", "object_reference"]`. pub fn matches_ancestor_history(&self, expected_ancestors: &[&'static str]) -> bool { - self.node_under_cursor - .as_ref() - .is_some_and(|under_cursor| match under_cursor { - NodeUnderCursor::TsNode(node) => { - let mut current = Some(*node); + self.node_under_cursor.as_ref().is_some_and(|node| { + let mut current = Some(*node); - for &expected_kind in expected_ancestors.iter().rev() { - current = current.and_then(|n| n.parent()); + for &expected_kind in expected_ancestors.iter().rev() { + current = current.and_then(|n| n.parent()); - match current { - Some(ancestor) if ancestor.kind() == expected_kind => continue, - _ => return false, - } - } - - true + match current { + Some(ancestor) if ancestor.kind() == expected_kind => continue, + _ => return false, } - NodeUnderCursor::CustomNode { .. } => false, - }) + } + + true + }) } /// Verifies whether the node_under_cursor has the passed in ancestors in the right order. @@ -701,14 +645,10 @@ impl<'a> TreesitterContext<'a> { /// If the tree shows `relation > object_reference > identifier` and the "identifier" is a leaf node, /// you need to pass `&["relation", "object_reference"]`. pub fn matches_one_of_ancestors(&self, expected_ancestors: &[&'static str]) -> bool { - self.node_under_cursor - .as_ref() - .is_some_and(|under_cursor| match under_cursor { - NodeUnderCursor::TsNode(node) => node - .parent() - .is_some_and(|p| expected_ancestors.contains(&p.kind())), - NodeUnderCursor::CustomNode { .. } => false, - }) + self.node_under_cursor.as_ref().is_some_and(|node| { + node.parent() + .is_some_and(|p| expected_ancestors.contains(&p.kind())) + }) } /// Checks whether the Node under the cursor is the nth child of the parent. @@ -735,32 +675,24 @@ impl<'a> TreesitterContext<'a> { /// } /// ``` pub fn node_under_cursor_is_nth_child(&self, nth: usize) -> bool { - self.node_under_cursor - .as_ref() - .is_some_and(|under_cursor| match under_cursor { - NodeUnderCursor::TsNode(node) => { - let mut cursor = node.walk(); - node.parent().is_some_and(|p| { - p.children(&mut cursor) - .nth(nth - 1) - .is_some_and(|n| n.id() == node.id()) - }) - } - NodeUnderCursor::CustomNode { .. } => false, + self.node_under_cursor.as_ref().is_some_and(|node| { + let mut cursor = node.walk(); + node.parent().is_some_and(|p| { + p.children(&mut cursor) + .nth(nth - 1) + .is_some_and(|n| n.id() == node.id()) }) + }) } /// Returns the number of siblings of the node under the cursor. pub fn num_siblings(&self) -> usize { self.node_under_cursor .as_ref() - .map(|n| match n { - NodeUnderCursor::TsNode(node) => { - // if there's no parent, we're on the top of the tree, - // where we have 0 siblings. - node.parent().map(|p| p.child_count() - 1).unwrap_or(0) - } - NodeUnderCursor::CustomNode { .. } => 0, + .map(|node| { + // if there's no parent, we're on the top of the tree, + // where we have 0 siblings. + node.parent().map(|p| p.child_count() - 1).unwrap_or(0) }) .unwrap_or(0) } @@ -769,34 +701,31 @@ impl<'a> TreesitterContext<'a> { pub fn node_under_cursor_is_within_field_name(&self, name: &str) -> bool { self.node_under_cursor .as_ref() - .map(|n| match n { - NodeUnderCursor::TsNode(node) => { - // It might seem weird that we have to check for the field_name from the parent, - // but TreeSitter wants it this way, since nodes often can only be named in - // the context of their parents. - let root_node = self.tree.root_node(); - let mut cursor = node.walk(); - let mut parent = node.parent(); - - while let Some(p) = parent { - if p == root_node { - break; - } - - if p.children_by_field_name(name, &mut cursor).any(|c| { - let r = c.range(); - // if the parent range contains the node range, the node is of the field_name. - r.start_byte <= node.start_byte() && r.end_byte >= node.end_byte() - }) { - return true; - } else { - parent = p.parent(); - } + .map(|node| { + // It might seem weird that we have to check for the field_name from the parent, + // but TreeSitter wants it this way, since nodes often can only be named in + // the context of their parents. + let root_node = self.tree.root_node(); + let mut cursor = node.walk(); + let mut parent = node.parent(); + + while let Some(p) = parent { + if p == root_node { + break; } - false + if p.children_by_field_name(name, &mut cursor).any(|c| { + let r = c.range(); + // if the parent range contains the node range, the node is of the field_name. + r.start_byte <= node.start_byte() && r.end_byte >= node.end_byte() + }) { + return true; + } else { + parent = p.parent(); + } } - NodeUnderCursor::CustomNode { .. } => false, + + false }) .unwrap_or(false) } @@ -858,8 +787,6 @@ mod tests { use pgt_test_utils::QueryWithCursorPosition; - use super::NodeUnderCursor; - fn get_tree(input: &str) -> tree_sitter::Tree { let mut parser = tree_sitter::Parser::new(); parser @@ -1091,17 +1018,12 @@ mod tests { let node = ctx.node_under_cursor.as_ref().unwrap(); - match node { - NodeUnderCursor::TsNode(node) => { - assert_eq!(ctx.get_ts_node_content(node), Some("select".into())); + assert_eq!(ctx.get_ts_node_content(node), Some("select".into())); - assert_eq!( - ctx.wrapping_clause_type, - Some(crate::context::WrappingClause::Select) - ); - } - _ => unreachable!(), - } + assert_eq!( + ctx.wrapping_clause_type, + Some(crate::context::WrappingClause::Select) + ); } } @@ -1126,12 +1048,7 @@ mod tests { let node = ctx.node_under_cursor.as_ref().unwrap(); - match node { - NodeUnderCursor::TsNode(node) => { - assert_eq!(ctx.get_ts_node_content(node), Some("from".into())); - } - _ => unreachable!(), - } + assert_eq!(ctx.get_ts_node_content(node), Some("from".into())); } #[test] @@ -1152,13 +1069,8 @@ mod tests { let node = ctx.node_under_cursor.as_ref().unwrap(); - match node { - NodeUnderCursor::TsNode(node) => { - assert_eq!(ctx.get_ts_node_content(node), Some("".into())); - assert_eq!(ctx.wrapping_clause_type, None); - } - _ => unreachable!(), - } + assert_eq!(ctx.get_ts_node_content(node), Some("".into())); + assert_eq!(ctx.wrapping_clause_type, None); } #[test] @@ -1181,13 +1093,8 @@ mod tests { let node = ctx.node_under_cursor.as_ref().unwrap(); - match node { - NodeUnderCursor::TsNode(node) => { - assert_eq!(ctx.get_ts_node_content(node), Some("fro".into())); - assert_eq!(ctx.wrapping_clause_type, Some(WrappingClause::Select)); - } - _ => unreachable!(), - } + assert_eq!(ctx.get_ts_node_content(node), Some("fro".into())); + assert_eq!(ctx.wrapping_clause_type, Some(WrappingClause::Select)); } #[test] diff --git a/crates/pgt_treesitter_grammar/grammar.js b/crates/pgt_treesitter_grammar/grammar.js index 07a135e52..1c746560a 100644 --- a/crates/pgt_treesitter_grammar/grammar.js +++ b/crates/pgt_treesitter_grammar/grammar.js @@ -49,7 +49,7 @@ module.exports = grammar({ ], ], - word: ($) => $._identifier, + word: ($) => $._word, rules: { program: ($) => @@ -814,8 +814,8 @@ module.exports = grammar({ cte: ($) => seq( - $.identifier, - optional(paren_list(field("argument", $.identifier), false)), + $.any_identifier, + optional(paren_list(field("argument", $.any_identifier), false)), $.keyword_as, optional(seq(optional($.keyword_not), $.keyword_materialized)), wrapped_in_parenthesis( @@ -871,7 +871,7 @@ module.exports = grammar({ function_argument: ($) => seq( optional($._argmode), - optional($.identifier), + optional($.any_identifier), $.type, optional(seq(choice($.keyword_default, "="), $.literal)) ), @@ -887,7 +887,7 @@ module.exports = grammar({ seq($.keyword_column, alias($._qualified_field, $.object_reference)), // TODO: constraint (on domain) // TODO: conversion - seq($.keyword_database, $.identifier), + seq($.keyword_database, $.any_identifier), // TODO: domain seq($.keyword_extension, $.object_reference), // TODO: event trigger @@ -906,20 +906,25 @@ module.exports = grammar({ // TODO: (procedural) language // TODO: procedure // TODO: publication - seq($.keyword_role, $.identifier), + seq($.keyword_role, $.role_identifier), // TODO: routine // TODO: rule - seq($.keyword_schema, $.identifier), + seq($.keyword_schema, $.schema_identifier), seq($.keyword_sequence, $.object_reference), // TODO: server // TODO: statistics // TODO: subscription seq($.keyword_table, $.object_reference), - seq($.keyword_tablespace, $.identifier), + seq($.keyword_tablespace, $.any_identifier), // TODO: text search (configuration|dictionary|parser|template) // TODO: transform for - seq($.keyword_trigger, $.identifier, $.keyword_on, $.object_reference), - seq($.keyword_type, $.identifier), + seq( + $.keyword_trigger, + $.any_identifier, + $.keyword_on, + $.object_reference + ), + seq($.keyword_type, $.any_identifier), seq($.keyword_view, $.object_reference) ), @@ -997,7 +1002,7 @@ module.exports = grammar({ seq( $.keyword_with, paren_list( - seq($.identifier, optional(seq("=", choice($.literal, $.array)))), + seq($.any_identifier, optional(seq("=", choice($.literal, $.array)))), true ) ), @@ -1031,7 +1036,7 @@ module.exports = grammar({ seq( $.keyword_create, $.keyword_policy, - $.identifier, + $.any_identifier, $.keyword_on, $.object_reference, optional( @@ -1055,13 +1060,13 @@ module.exports = grammar({ alter_policy: ($) => seq( - seq($.keyword_alter, $.keyword_policy, $.identifier), + seq($.keyword_alter, $.keyword_policy, $.policy_identifier), optional( seq( $.keyword_on, $.object_reference, choice( - seq($.keyword_rename, $.keyword_to, $.identifier), + seq($.keyword_rename, $.keyword_to, $.any_identifier), $.policy_to_role, optional($.check_or_using_clause) ) @@ -1070,19 +1075,7 @@ module.exports = grammar({ ), policy_to_role: ($) => - seq( - $.keyword_to, - comma_list( - choice( - $.identifier, - $.keyword_public, - $.keyword_current_user, - $.keyword_current_role, - $.keyword_session_user - ), - true - ) - ), + seq($.keyword_to, comma_list($.role_specification, true)), drop_policy: ($) => seq( @@ -1090,7 +1083,7 @@ module.exports = grammar({ $.keyword_drop, $.keyword_policy, optional($._if_exists), - $.identifier + $.policy_identifier ), optional( seq( @@ -1153,7 +1146,7 @@ module.exports = grammar({ choice( $.literal, $.keyword_default, - $.identifier, + $.any_identifier, $.keyword_on, $.keyword_off ) @@ -1168,14 +1161,14 @@ module.exports = grammar({ seq( $.keyword_session, $.keyword_authorization, - choice($.identifier, $.keyword_default) + choice($.any_identifier, $.keyword_default) ), - seq($.keyword_role, choice($.identifier, $.keyword_none)) + seq($.keyword_role, choice($.role_identifier, $.keyword_none)) ) ), seq( $.keyword_constraints, - choice($.keyword_all, comma_list($.identifier, true)), + choice($.keyword_all, comma_list($.any_identifier, true)), choice($.keyword_deferred, $.keyword_immediate) ), seq($.keyword_transaction, $._transaction_mode), @@ -1202,7 +1195,7 @@ module.exports = grammar({ $.keyword_view, optional($._if_not_exists), $.object_reference, - optional(paren_list($.identifier, false)), + optional(paren_list($.any_identifier, false)), $.keyword_as, $.create_query, optional( @@ -1289,7 +1282,7 @@ module.exports = grammar({ function_declaration: ($) => seq( - $.identifier, + $.any_identifier, $.type, optional( seq( @@ -1349,7 +1342,7 @@ module.exports = grammar({ // regard to the defined language to match either sql, plsql or // plpgsql. Currently the function_body_statement support only sql. And // maybe for other language the function_body should be a string. - $.identifier + $.any_identifier ), function_volatility: ($) => @@ -1391,7 +1384,7 @@ module.exports = grammar({ _operator_class: ($) => seq( - field("opclass", $.identifier), + field("opclass", $.any_identifier), optional( field( "opclass_parameters", @@ -1407,7 +1400,7 @@ module.exports = grammar({ field("function", $.invocation), field("column", $._column) ), - optional(seq($.keyword_collate, $.identifier)), + optional(seq($.keyword_collate, $.any_identifier)), optional($._operator_class), optional($.direction), optional(seq($.keyword_nulls, choice($.keyword_first, $.keyword_last))) @@ -1453,21 +1446,21 @@ module.exports = grammar({ choice( seq( optional($._if_not_exists), - $.identifier, - optional(seq($.keyword_authorization, $.identifier)) + $.any_identifier, + optional(seq($.keyword_authorization, $.any_identifier)) ), - seq($.keyword_authorization, $.identifier) + seq($.keyword_authorization, $.any_identifier) ) ) ), _with_settings: ($) => seq( - field("name", $.identifier), + field("name", $.any_identifier), optional("="), field( "value", - choice($.identifier, alias($._single_quote_string, $.literal)) + choice($.any_identifier, alias($._single_quote_string, $.literal)) ) ), @@ -1476,7 +1469,7 @@ module.exports = grammar({ $.keyword_create, $.keyword_database, optional($._if_not_exists), - $.identifier, + $.any_identifier, optional($.keyword_with), repeat($._with_settings) ), @@ -1485,14 +1478,14 @@ module.exports = grammar({ seq( $.keyword_create, choice($.keyword_user, $.keyword_role, $.keyword_group), - $.identifier, + $.any_identifier, optional($.keyword_with), repeat(choice($._user_access_role_config, $._role_options)) ), _role_options: ($) => choice( - field("option", $.identifier), + field("option", $.any_identifier), seq( $.keyword_valid, $.keyword_until, @@ -1521,7 +1514,7 @@ module.exports = grammar({ $.keyword_admin, $.keyword_user ), - comma_list($.identifier, true) + comma_list($.any_identifier, true) ), create_sequence: ($) => @@ -1575,13 +1568,13 @@ module.exports = grammar({ $.keyword_create, $.keyword_extension, optional($._if_not_exists), - $.identifier, + $.any_identifier, optional($.keyword_with), - optional(seq($.keyword_schema, $.identifier)), + optional(seq($.keyword_schema, $.schema_identifier)), optional( seq( $.keyword_version, - choice($.identifier, alias($._literal_string, $.literal)) + choice($.any_identifier, alias($._literal_string, $.literal)) ) ), optional($.keyword_cascade) @@ -1592,7 +1585,7 @@ module.exports = grammar({ $.keyword_create, optional($._or_replace), // mariadb - optional(seq($.keyword_definer, "=", $.identifier)), + optional(seq($.keyword_definer, "=", $.any_identifier)), optional($.keyword_constraint), // sqlite optional($._temporary), @@ -1623,7 +1616,7 @@ module.exports = grammar({ choice($.keyword_old, $.keyword_new), $.keyword_table, optional($.keyword_as), - $.identifier + $.any_identifier ), seq( $.keyword_for, @@ -1631,7 +1624,10 @@ module.exports = grammar({ choice($.keyword_row, $.keyword_statement), // mariadb optional( - seq(choice($.keyword_follows, $.keyword_precedes), $.identifier) + seq( + choice($.keyword_follows, $.keyword_precedes), + $.any_identifier + ) ) ), seq($.keyword_when, wrapped_in_parenthesis($._expression)) @@ -1648,7 +1644,7 @@ module.exports = grammar({ $.keyword_insert, seq( $.keyword_update, - optional(seq($.keyword_of, comma_list($.identifier, true))) + optional(seq($.keyword_of, comma_list($.any_identifier, true))) ), $.keyword_delete, $.keyword_truncate @@ -1665,7 +1661,7 @@ module.exports = grammar({ seq( $.keyword_as, $.column_definitions, - optional(seq($.keyword_collate, $.identifier)) + optional(seq($.keyword_collate, $.any_identifier)) ), seq($.keyword_as, $.keyword_enum, $.enum_elements), seq( @@ -1768,7 +1764,7 @@ module.exports = grammar({ seq( $.keyword_add, optional($.keyword_constraint), - $.identifier, + $.any_identifier, $.constraint ), @@ -1777,7 +1773,7 @@ module.exports = grammar({ $.keyword_drop, $.keyword_constraint, optional($._if_exists), - $.identifier, + $.any_identifier, optional($._drop_behavior) ), @@ -1786,7 +1782,7 @@ module.exports = grammar({ // TODO constraint management $.keyword_alter, optional($.keyword_column), - field("name", $.identifier), + field("name", $.column_identifier), choice( seq( choice($.keyword_set, $.keyword_drop), @@ -1812,10 +1808,7 @@ module.exports = grammar({ $.keyword_default ) ), - seq( - $.keyword_compression, - field("compression_method", $._identifier) - ), + seq($.keyword_compression, field("compression_method", $._word)), seq(paren_list($._key_value_pair, true)), seq($.keyword_default, $._expression) ) @@ -1838,7 +1831,7 @@ module.exports = grammar({ $.keyword_change, optional($.keyword_column), optional($._if_exists), - field("old_name", $.identifier), + field("old_name", $.column_identifier), $.column_definition, optional($.column_position) ), @@ -1846,7 +1839,7 @@ module.exports = grammar({ column_position: ($) => choice( $.keyword_first, - seq($.keyword_after, field("col_name", $.identifier)) + seq($.keyword_after, field("col_name", $.column_identifier)) ), drop_column: ($) => @@ -1854,16 +1847,16 @@ module.exports = grammar({ $.keyword_drop, optional($.keyword_column), optional($._if_exists), - field("name", $.identifier) + field("name", $.column_identifier) ), rename_column: ($) => seq( $.keyword_rename, optional($.keyword_column), - field("old_name", $.identifier), + field("old_name", $.column_identifier), $.keyword_to, - field("new_name", $.identifier) + field("new_name", $.any_identifier) ), alter_view: ($) => @@ -1885,17 +1878,17 @@ module.exports = grammar({ seq( $.keyword_alter, $.keyword_schema, - $.identifier, + $.schema_identifier, choice($.keyword_rename, $.keyword_owner), $.keyword_to, - $.identifier + $.any_identifier ), alter_database: ($) => seq( $.keyword_alter, $.keyword_database, - $.identifier, + $.any_identifier, optional($.keyword_with), choice( seq($.rename_object), @@ -1904,12 +1897,15 @@ module.exports = grammar({ $.keyword_reset, choice( $.keyword_all, - field("configuration_parameter", $.identifier) + field("configuration_parameter", $.any_identifier) ) ), seq( $.keyword_set, - choice(seq($.keyword_tablespace, $.identifier), $.set_configuration) + choice( + seq($.keyword_tablespace, $.any_identifier), + $.set_configuration + ) ) ) ), @@ -1918,17 +1914,17 @@ module.exports = grammar({ seq( $.keyword_alter, choice($.keyword_role, $.keyword_group, $.keyword_user), - choice($.identifier, $.keyword_all), + choice($.role_identifier, $.keyword_all), choice( $.rename_object, seq(optional($.keyword_with), repeat($._role_options)), seq( - optional(seq($.keyword_in, $.keyword_database, $.identifier)), + optional(seq($.keyword_in, $.keyword_database, $.any_identifier)), choice( seq($.keyword_set, $.set_configuration), seq( $.keyword_reset, - choice($.keyword_all, field("option", $.identifier)) + choice($.keyword_all, field("option", $.any_identifier)) ) ) ) @@ -1937,13 +1933,13 @@ module.exports = grammar({ set_configuration: ($) => seq( - field("option", $.identifier), + field("option", $.any_identifier), choice( seq($.keyword_from, $.keyword_current), seq( choice($.keyword_to, "="), choice( - field("parameter", $.identifier), + field("parameter", $.any_identifier), $.literal, $.keyword_default ) @@ -1956,7 +1952,7 @@ module.exports = grammar({ $.keyword_alter, $.keyword_index, optional($._if_exists), - $.identifier, + $.any_identifier, choice( $.rename_object, seq( @@ -1967,13 +1963,13 @@ module.exports = grammar({ $.keyword_statistics, alias($._natural_number, $.literal) ), - seq($.keyword_reset, paren_list($.identifier, false)), + seq($.keyword_reset, paren_list($.any_identifier, false)), seq( $.keyword_set, choice( - seq($.keyword_tablespace, $.identifier), + seq($.keyword_tablespace, $.any_identifier), paren_list( - seq($.identifier, "=", field("value", $.literal)), + seq($.any_identifier, "=", field("value", $.literal)), false ) ) @@ -2028,7 +2024,7 @@ module.exports = grammar({ $.keyword_set, choice( choice($.keyword_logged, $.keyword_unlogged), - seq($.keyword_schema, $.identifier) + seq($.keyword_schema, $.schema_identifier) ) ) ) @@ -2038,7 +2034,7 @@ module.exports = grammar({ seq( $.keyword_alter, $.keyword_type, - $.identifier, + $.type_identifier, choice( $.change_ownership, $.set_schema, @@ -2046,9 +2042,9 @@ module.exports = grammar({ seq( $.keyword_rename, $.keyword_attribute, - $.identifier, + $.any_identifier, $.keyword_to, - $.identifier, + $.any_identifier, optional($._drop_behavior) ), seq( @@ -2072,23 +2068,23 @@ module.exports = grammar({ ), seq( choice( - seq($.keyword_add, $.keyword_attribute, $.identifier, $.type), + seq($.keyword_add, $.keyword_attribute, $.any_identifier, $.type), seq( $.keyword_drop, $.keyword_attribute, optional($._if_exists), - $.identifier + $.any_identifier ), seq( $.keyword_alter, $.keyword_attribute, - $.identifier, + $.any_identifier, optional(seq($.keyword_set, $.keyword_data)), $.keyword_type, $.type ) ), - optional(seq($.keyword_collate, $.identifier)), + optional(seq($.keyword_collate, $.any_identifier)), optional($._drop_behavior) ) ) @@ -2136,7 +2132,7 @@ module.exports = grammar({ $.keyword_drop, $.keyword_schema, optional($._if_exists), - $.identifier, + $.schema_identifier, optional($._drop_behavior) ), @@ -2145,7 +2141,7 @@ module.exports = grammar({ $.keyword_drop, $.keyword_database, optional($._if_exists), - $.identifier, + $.any_identifier, optional($.keyword_with), optional($.keyword_force) ), @@ -2155,7 +2151,7 @@ module.exports = grammar({ $.keyword_drop, choice($.keyword_group, $.keyword_role, $.keyword_user), optional($._if_exists), - $.identifier + $.role_identifier ), drop_type: ($) => @@ -2182,7 +2178,7 @@ module.exports = grammar({ $.keyword_index, optional($.keyword_concurrently), optional($._if_exists), - field("name", $.identifier), + field("name", $.any_identifier), optional($._drop_behavior), optional(seq($.keyword_on, $.object_reference)) ), @@ -2192,7 +2188,7 @@ module.exports = grammar({ $.keyword_drop, $.keyword_extension, optional($._if_exists), - comma_list($.identifier, true), + comma_list($.any_identifier, true), optional(choice($.keyword_cascade, $.keyword_restrict)) ), @@ -2209,9 +2205,14 @@ module.exports = grammar({ seq($.keyword_rename, $.keyword_to, $.object_reference), set_schema: ($) => - seq($.keyword_set, $.keyword_schema, field("schema", $.identifier)), + seq( + $.keyword_set, + $.keyword_schema, + field("schema", $.schema_identifier) + ), - change_ownership: ($) => seq($.keyword_owner, $.keyword_to, $.identifier), + change_ownership: ($) => + seq($.keyword_owner, $.keyword_to, $.role_identifier), object_id: ($) => seq( @@ -2227,14 +2228,18 @@ module.exports = grammar({ object_reference: ($) => choice( seq( - field("database", $.identifier), + field("database", $.any_identifier), ".", - field("schema", $.identifier), + field("schema", $.any_identifier), ".", - field("name", $.identifier) + field("name", $.any_identifier) ), - seq(field("schema", $.identifier), ".", field("name", $.identifier)), - field("name", $.identifier) + seq( + field("schema", $.any_identifier), + ".", + field("name", $.any_identifier) + ), + field("name", $.any_identifier) ), _copy_statement: ($) => @@ -2270,7 +2275,7 @@ module.exports = grammar({ $.keyword_quote, $.keyword_encoding ), - alias($._literal_string, $.identifier) + alias($._literal_string, $.any_identifier) ), seq( choice( @@ -2307,7 +2312,7 @@ module.exports = grammar({ ), $.object_reference, optional($.table_partition), // Spark SQL - optional(seq($.keyword_as, field("alias", $.identifier))), + optional(seq($.keyword_as, field("alias", $.any_identifier))), // TODO we need a test for `insert...set` choice($._insert_values, $._set_values), optional(choice($._on_conflict, $._on_duplicate_key_update)) @@ -2346,7 +2351,8 @@ module.exports = grammar({ _set_values: ($) => seq($.keyword_set, comma_list($.assignment, true)), _column_list: ($) => paren_list(alias($._column, $.column), true), - _column: ($) => choice($.identifier, alias($._literal_string, $.literal)), + _column: ($) => + choice($.column_identifier, alias($._literal_string, $.literal)), _update_statement: ($) => seq($.update, optional($.returning)), @@ -2555,7 +2561,7 @@ module.exports = grammar({ ), table_sort: ($) => - seq($.keyword_sort, $.keyword_by, paren_list($.identifier, true)), + seq($.keyword_sort, $.keyword_by, paren_list($.any_identifier, true)), table_partition: ($) => seq( @@ -2572,7 +2578,7 @@ module.exports = grammar({ $.keyword_partition ), choice( - paren_list($.identifier, false), // postgres & Impala (CTAS) + paren_list($.any_identifier, false), // postgres & Impala (CTAS) $.column_definitions, // impala/hive external tables paren_list($._key_value_pair, true) // Spark SQL ) @@ -2580,7 +2586,7 @@ module.exports = grammar({ _key_value_pair: ($) => seq( - field("key", $.identifier), + field("key", $.any_identifier), "=", field("value", alias($._literal_string, $.literal)) ), @@ -2614,17 +2620,17 @@ module.exports = grammar({ $.keyword_default, $.keyword_character, $.keyword_set, - $.identifier + $.any_identifier ), - seq($.keyword_collate, $.identifier), + seq($.keyword_collate, $.any_identifier), field("name", $.keyword_default), seq( field( "name", - choice($.keyword_engine, $.identifier, $._literal_string) + choice($.keyword_engine, $.any_identifier, $._literal_string) ), "=", - field("value", choice($.identifier, $._literal_string)) + field("value", choice($.any_identifier, $._literal_string)) ) ), @@ -2653,7 +2659,7 @@ module.exports = grammar({ seq( $.keyword_references, $.object_reference, - paren_list($.identifier, true), + paren_list($.column_identifier, true), repeat( seq( $.keyword_on, @@ -2665,7 +2671,7 @@ module.exports = grammar({ seq( $.keyword_set, choice($.keyword_null, $.keyword_default), - optional(paren_list($.identifier, true)) + optional(paren_list($.any_identifier, true)) ) ) ) @@ -2722,7 +2728,7 @@ module.exports = grammar({ _constraint_literal: ($) => seq( $.keyword_constraint, - field("name", $.identifier), + field("name", $.any_identifier), choice(seq($._primary_key, $.ordered_columns), seq($._check_constraint)) ), @@ -2752,13 +2758,13 @@ module.exports = grammar({ ), $.keyword_index ), - optional(field("name", $.identifier)), + optional(field("name", $.any_identifier)), $.ordered_columns, optional( seq( $.keyword_references, $.object_reference, - paren_list($.identifier, true), + paren_list($.column_identifier, true), repeat( seq( $.keyword_on, @@ -2770,7 +2776,7 @@ module.exports = grammar({ seq( $.keyword_set, choice($.keyword_null, $.keyword_default), - optional(paren_list($.identifier, true)) + optional(paren_list($.column_identifier, true)) ) ) ) @@ -2817,10 +2823,10 @@ module.exports = grammar({ $.keyword_end ), - field: ($) => field("name", $.identifier), + field: ($) => field("name", $.any_identifier), _qualified_field: ($) => - seq(optional($.field_qualifier), field("name", $.identifier)), + seq(optional($.field_qualifier), field("name", $.any_identifier)), field_qualifier: ($) => seq(prec.left(optional_parenthesis($.object_reference)), "."), @@ -2889,7 +2895,7 @@ module.exports = grammar({ field( "start", choice( - $.identifier, + $.any_identifier, $.binary_expression, alias($._literal_string, $.literal), alias($._integer, $.literal) @@ -2902,7 +2908,7 @@ module.exports = grammar({ field( "end", choice( - $.identifier, + $.any_identifier, $.binary_expression, alias($._literal_string, $.literal), alias($._integer, $.literal) @@ -2937,7 +2943,12 @@ module.exports = grammar({ ), window_clause: ($) => - seq($.keyword_window, $.identifier, $.keyword_as, $.window_specification), + seq( + $.keyword_window, + $.any_identifier, + $.keyword_as, + $.window_specification + ), window_specification: ($) => wrapped_in_parenthesis( @@ -2952,10 +2963,11 @@ module.exports = grammar({ seq( $.invocation, $.keyword_over, - choice($.identifier, $.window_specification) + choice($.any_identifier, $.window_specification) ), - _alias: ($) => seq(optional($.keyword_as), field("alias", $.identifier)), + _alias: ($) => + seq(optional($.keyword_as), field("alias", $.any_identifier)), from: ($) => seq( @@ -2994,7 +3006,7 @@ module.exports = grammar({ choice($.keyword_force, $.keyword_use, $.keyword_ignore), $.keyword_index, optional(seq($.keyword_for, $.keyword_join)), - wrapped_in_parenthesis(field("index_name", $.identifier)) + wrapped_in_parenthesis(field("index_name", $.any_identifier)) ), join: ($) => @@ -3034,8 +3046,8 @@ module.exports = grammar({ optional( seq( $.keyword_as, - field("alias", $.identifier), - paren_list($.identifier, false) + field("alias", $.any_identifier), + paren_list($.any_identifier, false) ) ) ) @@ -3058,8 +3070,8 @@ module.exports = grammar({ choice($.invocation, $.subquery), optional( choice( - seq($.keyword_as, field("alias", $.identifier)), - field("alias", $.identifier) + seq($.keyword_as, field("alias", $.any_identifier)), + field("alias", $.any_identifier) ) ), $.keyword_on, @@ -3074,8 +3086,8 @@ module.exports = grammar({ choice($.invocation, $.subquery), optional( choice( - seq($.keyword_as, field("alias", $.identifier)), - field("alias", $.identifier) + seq($.keyword_as, field("alias", $.any_identifier)), + field("alias", $.any_identifier) ) ) ), @@ -3162,13 +3174,13 @@ module.exports = grammar({ $.grantable_on_all ) ), - $.identifier + $.any_identifier ), grantable_targets: ($) => choice( - seq($._grantable, comma_list($.identifier, false)), - comma_list($.identifier, true) + seq($._grantable, comma_list($.any_identifier, false)), + comma_list($.any_identifier, true) ), _grantable: ($) => @@ -3225,12 +3237,12 @@ module.exports = grammar({ ), $.keyword_in, $.keyword_schema, - comma_list($.identifier, true) + comma_list($.schema_identifier, true) ), role_specification: ($) => choice( - seq(optional($.keyword_group), $.identifier), + seq(optional($.keyword_group), $.role_identifier), $.keyword_public, $.keyword_current_role, $.keyword_current_user, @@ -3503,19 +3515,27 @@ module.exports = grammar({ ), _bit_string: ($) => seq(/[bBxX]'([^']|'')*'/, repeat(/'([^']|'')*'/)), // The identifier should be followed by a string (no parenthesis allowed) - _string_casting: ($) => seq($.identifier, $._single_quote_string), + _string_casting: ($) => seq($.any_identifier, $._single_quote_string), bang: (_) => "!", - identifier: ($) => + any_identifier: ($) => $._identifier, + column_identifier: ($) => $._identifier, + table_identifier: ($) => $._identifier, + schema_identifier: ($) => $._identifier, + role_identifier: ($) => $._identifier, + policy_identifier: ($) => $._identifier, + type_identifier: ($) => $._identifier, + + _identifier: ($) => choice( - $._identifier, + $._word, $._double_quote_string, $._sql_parameter, - seq("`", $._identifier, "`") + seq("`", $._word, "`") ), _sql_parameter: (_) => /[:$@?][a-zA-Z_][0-9a-zA-Z_]*/, - _identifier: (_) => /[a-zA-Z_][0-9a-zA-Z_]*/, + _word: (_) => /[a-zA-Z_][0-9a-zA-Z_]*/, }, }); From ca93d5b66675d67091825fb23e397fe83347134c Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 24 Oct 2025 20:34:38 +0200 Subject: [PATCH 2/2] =?UTF-8?q?in=20progress=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/relevance/filtering.rs | 8 ++--- crates/pgt_hover/src/hovered_node.rs | 36 +++++-------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/crates/pgt_completions/src/relevance/filtering.rs b/crates/pgt_completions/src/relevance/filtering.rs index 50681ce62..5cc6d9b4b 100644 --- a/crates/pgt_completions/src/relevance/filtering.rs +++ b/crates/pgt_completions/src/relevance/filtering.rs @@ -69,8 +69,8 @@ impl CompletionFilter<'_> { // 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() == "identifier" || p.kind() == "object_reference") - && node.kind() == "identifier" + (p.kind() == "any_identifier" || p.kind() == "object_reference") + && node.kind() == "any_identifier" }) }) { return None; @@ -80,7 +80,7 @@ impl CompletionFilter<'_> { // `select * {}` if ctx.node_under_cursor.as_ref().is_some_and(|n| { n.prev_sibling() - .is_some_and(|p| (p.kind() == "all_fields") && n.kind() == "identifier") + .is_some_and(|p| (p.kind() == "all_fields") && n.kind() == "any_identifier") }) { return None; } @@ -254,7 +254,7 @@ impl CompletionFilter<'_> { WrappingClause::RevokeStatement | WrappingClause::GrantStatement => { ctx.matches_ancestor_history(&["role_specification"]) || ctx.node_under_cursor.as_ref().is_some_and(|k| { - k.kind() == "identifier" + k.kind() == "any_identifier" && ctx.before_cursor_matches_kind(&[ "keyword_grant", "keyword_revoke", diff --git a/crates/pgt_hover/src/hovered_node.rs b/crates/pgt_hover/src/hovered_node.rs index 7a7e388ac..54cdda63c 100644 --- a/crates/pgt_hover/src/hovered_node.rs +++ b/crates/pgt_hover/src/hovered_node.rs @@ -32,7 +32,7 @@ impl HoveredNode { let under_cursor = ctx.node_under_cursor.as_ref()?; match under_cursor.kind() { - "identifier" + "any_identifier" if ctx.matches_ancestor_history(&["relation", "object_reference"]) || ctx .matches_ancestor_history(&["grantable_on_table", "object_reference"]) => @@ -52,7 +52,7 @@ impl HoveredNode { } } - "identifier" + "any_identifier" if ctx.matches_ancestor_history(&["object_reference"]) && ctx.wrapping_clause_type.as_ref().is_some_and(|clause| { matches!( @@ -73,7 +73,7 @@ impl HoveredNode { } } - "identifier" if ctx.matches_ancestor_history(&["field"]) => { + "any_identifier" if ctx.matches_ancestor_history(&["field"]) => { if let Some(table_or_alias) = ctx.schema_or_alias_name.as_ref() { Some(HoveredNode::Column(NodeIdentification::SchemaAndName(( table_or_alias.clone(), @@ -84,7 +84,9 @@ impl HoveredNode { } } - "identifier" if ctx.matches_ancestor_history(&["invocation", "object_reference"]) => { + "any_identifier" + if ctx.matches_ancestor_history(&["invocation", "object_reference"]) => + { if let Some(schema) = ctx.schema_or_alias_name.as_ref() { Some(HoveredNode::Function(NodeIdentification::SchemaAndName(( schema.clone(), @@ -97,20 +99,9 @@ impl HoveredNode { } } - "identifier" - if ctx.matches_one_of_ancestors(&[ - "alter_role", - "policy_to_role", - "role_specification", - ]) || ctx.before_cursor_matches_kind(&["keyword_revoke"]) => - { - Some(HoveredNode::Role(NodeIdentification::Name(node_content))) - } - "grant_role" | "policy_role" => { - Some(HoveredNode::Role(NodeIdentification::Name(node_content))) - } + "role_identifier" => Some(HoveredNode::Role(NodeIdentification::Name(node_content))), - "identifier" + "any_identifier" if ( // hover over custom type in `create table` or `returns` (ctx.matches_ancestor_history(&["type", "object_reference"]) @@ -145,17 +136,6 @@ impl HoveredNode { Some(HoveredNode::Column(NodeIdentification::Name(node_content))) } - "grant_table" => { - if let Some(schema) = ctx.schema_or_alias_name.as_ref() { - Some(HoveredNode::Table(NodeIdentification::SchemaAndName(( - schema.clone(), - node_content, - )))) - } else { - Some(HoveredNode::Table(NodeIdentification::Name(node_content))) - } - } - _ => None, } }