From d3eabfe3c891c5846e1c5858e0b4d5be8c8960ae Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Wed, 25 Feb 2026 22:39:52 -0500 Subject: [PATCH 1/2] ide: refactor hover to use goto def --- crates/squawk_ide/src/classify.rs | 138 +++++++++++++++++++--- crates/squawk_ide/src/code_actions.rs | 1 + crates/squawk_ide/src/completion.rs | 7 ++ crates/squawk_ide/src/document_symbols.rs | 1 + crates/squawk_ide/src/find_references.rs | 3 + crates/squawk_ide/src/goto_definition.rs | 4 + crates/squawk_ide/src/hover.rs | 108 +++++++++-------- crates/squawk_ide/src/inlay_hints.rs | 3 + crates/squawk_ide/src/resolve.rs | 29 ++--- 9 files changed, 208 insertions(+), 86 deletions(-) diff --git a/crates/squawk_ide/src/classify.rs b/crates/squawk_ide/src/classify.rs index 55a9c6c5..f3f7ac09 100644 --- a/crates/squawk_ide/src/classify.rs +++ b/crates/squawk_ide/src/classify.rs @@ -1,10 +1,10 @@ use crate::symbols::Name; use squawk_syntax::{ - SyntaxKind, + SyntaxKind, SyntaxNode, ast::{self, AstNode}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) enum NameRefClass { Aggregate, AlterColumn, @@ -91,7 +91,7 @@ fn is_special_fn(kind: SyntaxKind) -> bool { ) } -pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option { +pub(crate) fn classify_name_ref(node: &SyntaxNode) -> Option { let mut in_function_name = false; let mut in_arg_list = false; let mut in_column_list = false; @@ -111,13 +111,13 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option let mut in_conflict_target = false; // TODO: can we combine this if and the one that follows? - if let Some(parent) = name_ref.syntax().parent() + if let Some(parent) = node.parent() && let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) && let Some(base) = field_expr.base() && let ast::Expr::NameRef(base_name_ref) = base // check that the name_ref we're looking at in the field expr is the // base name_ref, i.e., the schema, rather than the item - && base_name_ref.syntax() == name_ref.syntax() + && base_name_ref.syntax() == node { let is_function_call = field_expr .syntax() @@ -217,13 +217,13 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option return Some(NameRefClass::Schema); } - if let Some(parent) = name_ref.syntax().parent() + if let Some(parent) = node.parent() && let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) && field_expr .field() // we're at the field in a FieldExpr, i.e., foo.bar // ^^^ - .is_some_and(|field_name_ref| field_name_ref.syntax() == name_ref.syntax()) + .is_some_and(|field_name_ref| field_name_ref.syntax() == node) // we're not inside a call expr && field_expr.star_token().is_none() && field_expr @@ -296,7 +296,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option } } - if let Some(parent) = name_ref.syntax().parent() + if let Some(parent) = node.parent() && let Some(inner_path) = ast::PathSegment::cast(parent) .and_then(|p| p.syntax().parent().and_then(ast::Path::cast)) && let Some(outer_path) = inner_path @@ -309,7 +309,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option } // Check for function/procedure reference in CREATE OPERATOR before the type check - for ancestor in name_ref.syntax().ancestors() { + for ancestor in node.ancestors() { if let Some(attr_option) = ast::AttributeOption::cast(ancestor.clone()) && let Some(name) = attr_option.name() { @@ -327,7 +327,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option } let mut in_type = false; - for ancestor in name_ref.syntax().ancestors() { + for ancestor in node.ancestors() { if ast::PathType::can_cast(ancestor.kind()) || ast::ExprType::can_cast(ancestor.kind()) { in_type = true; } @@ -462,7 +462,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option && to_columns .syntax() .text_range() - .contains_range(name_ref.syntax().text_range()) + .contains_range(node.text_range()) { return Some(NameRefClass::ForeignKeyColumn); } @@ -470,7 +470,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option && from_columns .syntax() .text_range() - .contains_range(name_ref.syntax().text_range()) + .contains_range(node.text_range()) { return Some(NameRefClass::ConstraintColumn); } @@ -487,15 +487,12 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option && column_ref .syntax() .text_range() - .contains_range(name_ref.syntax().text_range()) + .contains_range(node.text_range()) { return Some(NameRefClass::ForeignKeyColumn); } if let Some(path) = references_constraint.table() - && path - .syntax() - .text_range() - .contains_range(name_ref.syntax().text_range()) + && path.syntax().text_range().contains_range(node.text_range()) { return Some(NameRefClass::ForeignKeyTable); } @@ -826,6 +823,113 @@ pub(crate) fn classify_name(name: &ast::Name) -> Option { None } +pub(crate) fn classify_def_node(def_node: &SyntaxNode) -> Option { + let mut in_column = false; + let mut in_column_list = false; + for ancestor in def_node.ancestors() { + if ast::Column::can_cast(ancestor.kind()) { + in_column = true; + } + if ast::ColumnList::can_cast(ancestor.kind()) { + in_column_list = true; + } + if ast::Param::can_cast(ancestor.kind()) { + return Some(NameRefClass::NamedArgParameter); + } + if ast::CreateTableLike::can_cast(ancestor.kind()) { + if in_column { + return Some(NameRefClass::SelectColumn); + } + return Some(NameRefClass::Table); + } + if ast::CreateType::can_cast(ancestor.kind()) { + if in_column { + return Some(NameRefClass::CompositeTypeField); + } + return Some(NameRefClass::Type); + } + if ast::CreateFunction::can_cast(ancestor.kind()) { + return Some(NameRefClass::Function); + } + if ast::CreateProcedure::can_cast(ancestor.kind()) { + return Some(NameRefClass::Procedure); + } + if ast::WithTable::can_cast(ancestor.kind()) { + if in_column_list { + return Some(NameRefClass::SelectColumn); + } + return Some(NameRefClass::Table); + } + if ast::CreateTableAs::can_cast(ancestor.kind()) { + return Some(NameRefClass::Table); + } + if ast::CreateIndex::can_cast(ancestor.kind()) { + return Some(NameRefClass::Index); + } + if ast::CreateSequence::can_cast(ancestor.kind()) { + return Some(NameRefClass::Sequence); + } + if ast::CreateTrigger::can_cast(ancestor.kind()) { + return Some(NameRefClass::Trigger); + } + if ast::CreateEventTrigger::can_cast(ancestor.kind()) { + return Some(NameRefClass::EventTrigger); + } + if ast::CreateTablespace::can_cast(ancestor.kind()) { + return Some(NameRefClass::Tablespace); + } + if ast::CreateDatabase::can_cast(ancestor.kind()) { + return Some(NameRefClass::Database); + } + if ast::CreateServer::can_cast(ancestor.kind()) { + return Some(NameRefClass::Server); + } + if ast::CreateExtension::can_cast(ancestor.kind()) { + return Some(NameRefClass::Extension); + } + if ast::CreateRole::can_cast(ancestor.kind()) { + return Some(NameRefClass::Role); + } + if ast::CreateAggregate::can_cast(ancestor.kind()) { + return Some(NameRefClass::Aggregate); + } + if ast::CreateSchema::can_cast(ancestor.kind()) { + return Some(NameRefClass::Schema); + } + if ast::CreateView::can_cast(ancestor.kind()) + || ast::CreateMaterializedView::can_cast(ancestor.kind()) + { + if in_column_list { + return Some(NameRefClass::SelectColumn); + } + return Some(NameRefClass::View); + } + if ast::CreatePolicy::can_cast(ancestor.kind()) { + return Some(NameRefClass::Policy); + } + if ast::Declare::can_cast(ancestor.kind()) { + return Some(NameRefClass::Cursor); + } + if ast::Prepare::can_cast(ancestor.kind()) { + return Some(NameRefClass::PreparedStatement); + } + if ast::Listen::can_cast(ancestor.kind()) { + return Some(NameRefClass::Channel); + } + if ast::Alias::can_cast(ancestor.kind()) { + return Some(NameRefClass::FromTable); + } + if ast::AsName::can_cast(ancestor.kind()) + || ast::ParenSelect::can_cast(ancestor.kind()) + || ast::Values::can_cast(ancestor.kind()) + || ast::Select::can_cast(ancestor.kind()) + { + return Some(NameRefClass::SelectColumn); + } + } + None +} + #[test] fn special_function() { for kind in (0..SyntaxKind::__LAST as u16) diff --git a/crates/squawk_ide/src/code_actions.rs b/crates/squawk_ide/src/code_actions.rs index 6cf8b92a..a1e30caa 100644 --- a/crates/squawk_ide/src/code_actions.rs +++ b/crates/squawk_ide/src/code_actions.rs @@ -548,6 +548,7 @@ fn add_schema( } let position = token.text_range().start(); + // TODO: we should salsa this let binder = binder::bind(file); let schema = binder.search_path_at(position).first()?.to_string(); let replacement = format!("{}.", schema); diff --git a/crates/squawk_ide/src/completion.rs b/crates/squawk_ide/src/completion.rs index 313efcee..5290651e 100644 --- a/crates/squawk_ide/src/completion.rs +++ b/crates/squawk_ide/src/completion.rs @@ -45,6 +45,7 @@ fn select_completions( select_clause: ast::SelectClause, token: &SyntaxToken, ) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let mut completions = vec![]; let schema = schema_qualifier_at_token(token); @@ -246,6 +247,7 @@ fn select_clauses_completions(select: &ast::Select) -> Vec { } fn limit_completions(file: &ast::SourceFile, token: &SyntaxToken) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let schema = schema_qualifier_at_token(token); let position = token.text_range().start(); @@ -265,6 +267,7 @@ fn limit_completions(file: &ast::SourceFile, token: &SyntaxToken) -> Vec Vec { + // TODO: we should salsa this let binder = binder::bind(file); let schema = schema_qualifier_at_token(token); let position = token.text_range().start(); @@ -277,6 +280,7 @@ fn select_expr_completions( select: &ast::Select, token: &SyntaxToken, ) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let mut completions = vec![]; let schema = schema_qualifier_at_token(token); @@ -449,6 +453,7 @@ fn schema_completions(binder: &binder::Binder) -> Vec { } fn table_completions(file: &ast::SourceFile, token: &SyntaxToken) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let schema = schema_qualifier_at_token(token); let tables = binder.all_symbols_by_kind(SymbolKind::Table, schema.as_ref()); @@ -528,6 +533,7 @@ fn delete_expr_completions( delete: &ast::Delete, token: &SyntaxToken, ) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let mut completions = vec![]; @@ -738,6 +744,7 @@ fn file_with_completion_marker(file: &ast::SourceFile, offset: TextSize) -> ast: let offset = u32::from(offset) as usize; let offset = offset.min(sql.len()); sql.insert_str(offset, COMPLETION_MARKER); + // TODO: should this be cached ast::SourceFile::parse(&sql).tree() } diff --git a/crates/squawk_ide/src/document_symbols.rs b/crates/squawk_ide/src/document_symbols.rs index 6b98017b..e3f49561 100644 --- a/crates/squawk_ide/src/document_symbols.rs +++ b/crates/squawk_ide/src/document_symbols.rs @@ -50,6 +50,7 @@ pub struct DocumentSymbol { } pub fn document_symbols(file: &ast::SourceFile) -> Vec { + // TODO: we should salsa this let binder = binder::bind(file); let mut symbols = vec![]; diff --git a/crates/squawk_ide/src/find_references.rs b/crates/squawk_ide/src/find_references.rs index 6fa9a767..d314489d 100644 --- a/crates/squawk_ide/src/find_references.rs +++ b/crates/squawk_ide/src/find_references.rs @@ -12,9 +12,12 @@ use squawk_syntax::{ }; pub fn find_references(file: &ast::SourceFile, offset: TextSize) -> Vec { + // TODO: we should salsa this let current_binder = binder::bind(file); + // TODO: we should salsa this let builtins_tree = ast::SourceFile::parse(BUILTINS_SQL).tree(); + // TODO: we should salsa this let builtins_binder = binder::bind(&builtins_tree); let Some((target_file, target_defs)) = find_target_defs( diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index 3c4226cc..d7c9ef28 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -59,8 +59,10 @@ pub fn goto_definition(file: &ast::SourceFile, offset: TextSize) -> SmallVec<[Lo for file_id in [FileId::Current, FileId::Builtins] { let file = match file_id { FileId::Current => file, + // TODO: we should salsa this FileId::Builtins => &ast::SourceFile::parse(BUILTINS_SQL).tree(), }; + // TODO: we should salsa this let binder_output = binder::bind(file); let root = file.syntax(); if let Some(ptrs) = resolve::resolve_name_ref_ptrs(&binder_output, root, &name_ref) { @@ -89,8 +91,10 @@ pub fn goto_definition(file: &ast::SourceFile, offset: TextSize) -> SmallVec<[Lo for file_id in [FileId::Current, FileId::Builtins] { let file = match file_id { FileId::Current => file, + // TODO: we should salsa this FileId::Builtins => &ast::SourceFile::parse(BUILTINS_SQL).tree(), }; + // TODO: we should salsa this let binder_output = binder::bind(file); let position = token.text_range().start(); if let Some(ptr) = resolve::resolve_type_ptr_from_type(&binder_output, &ty, position) { diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index 0748c870..92337dbd 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -1,12 +1,12 @@ use crate::builtins::BUILTINS_SQL; -use crate::classify::{NameClass, NameRefClass, classify_name, classify_name_ref}; +use crate::classify::{NameClass, NameRefClass, classify_def_node, classify_name}; use crate::column_name::ColumnName; use crate::offsets::token_from_offset; -use crate::resolve; use crate::{ binder, symbols::{Name, Schema}, }; +use crate::{goto_definition, resolve}; use rowan::TextSize; use squawk_syntax::SyntaxNode; use squawk_syntax::{ @@ -19,6 +19,7 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option { let parent = token.parent()?; let root = file.syntax(); + // TODO: we should salsa this let binder = binder::bind(file); if token.kind() == SyntaxKind::STAR { @@ -44,15 +45,29 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option { } if let Some(name_ref) = ast::NameRef::cast(parent.clone()) { - if let Some(result) = hover_name_ref(root, &name_ref, &binder) { - return Some(result); - } + let definition = goto_definition::goto_definition(file, offset); + let def = definition.first()?; + + let (binder, root) = match def.file { + goto_definition::FileId::Current => (binder, root.clone()), + goto_definition::FileId::Builtins => { + let builtins_tree = ast::SourceFile::parse(BUILTINS_SQL); + let builtins_tree = builtins_tree.tree(); + // TODO: we should salsa this + let binder = binder::bind(&builtins_tree); + let tree = builtins_tree.syntax().clone(); + (binder, tree) + } + }; + + let def_node = match root.covering_element(def.range) { + rowan::NodeOrToken::Token(token) => token.parent()?, + rowan::NodeOrToken::Node(node) => node, + }; + + let context = classify_def_node(&def_node)?; - // Fall back to builtins - let builtins_tree = ast::SourceFile::parse(BUILTINS_SQL).tree(); - let builtins_binder = binder::bind(&builtins_tree); - let builtins_root = builtins_tree.syntax(); - return hover_name_ref(builtins_root, &name_ref, &builtins_binder); + return hover_name_ref(&root, &name_ref, &binder, context, &def_node); } if let Some(name) = ast::Name::cast(parent) { @@ -132,9 +147,12 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option { fn hover_name_ref( root: &SyntaxNode, name_ref: &ast::NameRef, + // TODO: we should pass in the file id along with the def_node and then use + // salsa to lookup the the correct binder. binder: &binder::Binder, + context: NameRefClass, + def_node: &SyntaxNode, ) -> Option { - let context = classify_name_ref(name_ref)?; match context { NameRefClass::CreateIndexColumn | NameRefClass::InsertColumn @@ -160,7 +178,7 @@ fn hover_name_ref( return Some(result); } // Finally try as table (handles case like `select t from t;` where t is the table) - hover_table(root, name_ref, binder) + hover_table(binder, def_node) } NameRefClass::DeleteQualifiedColumnTable | NameRefClass::ForeignKeyTable @@ -172,7 +190,7 @@ fn hover_name_ref( | NameRefClass::SelectQualifiedColumnTable | NameRefClass::Table | NameRefClass::UpdateQualifiedColumnTable - | NameRefClass::View => hover_table(root, name_ref, binder), + | NameRefClass::View => hover_table(binder, def_node), NameRefClass::Sequence => hover_sequence(root, name_ref, binder), NameRefClass::Trigger => hover_trigger(root, name_ref, binder), NameRefClass::Policy => hover_policy(root, name_ref, binder), @@ -406,34 +424,14 @@ fn hover_column_definition( )) } -fn hover_table( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - if let Some(result) = hover_subquery_table(name_ref) { - return Some(result); - } - - let table_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? - .into_iter() - .next()?; - - hover_table_from_ptr(root, &table_ptr, binder) -} - -fn hover_table_from_ptr( - root: &SyntaxNode, - table_ptr: &squawk_syntax::SyntaxNodePtr, - binder: &binder::Binder, -) -> Option { - let table_name_node = table_ptr.to_node(root); - - match resolve::find_table_source(&table_name_node)? { +// TODO: we should pass in the file id along with the def_node and then use +// salsa to lookup the the correct binder. +fn format_table_source(source: resolve::TableSource, binder: &binder::Binder) -> Option { + match source { resolve::TableSource::WithTable(with_table) => format_with_table(&with_table), resolve::TableSource::CreateView(create_view) => format_create_view(&create_view, binder), - resolve::TableSource::CreateMaterializedView(create_materialized_view) => { - format_create_materialized_view(&create_materialized_view, binder) + resolve::TableSource::CreateMaterializedView(mv) => { + format_create_materialized_view(&mv, binder) } resolve::TableSource::CreateTable(create_table) => { format_create_table(&create_table, binder) @@ -442,6 +440,18 @@ fn hover_table_from_ptr( } } +fn hover_table(binder: &binder::Binder, def_node: &SyntaxNode) -> Option { + if let Some(result) = hover_subquery_table(def_node) { + return Some(result); + } + + if let Some(source) = resolve::find_table_source(def_node) { + return format_table_source(source, binder); + } + + None +} + fn hover_qualified_star( root: &SyntaxNode, field_expr: &ast::FieldExpr, @@ -491,20 +501,16 @@ fn hover_unqualified_star_in_arg_list( Some(results.join("\n")) } -fn hover_subquery_table(name_ref: &ast::NameRef) -> Option { - let select = name_ref.syntax().ancestors().find_map(ast::Select::cast)?; - let from_clause = select.from_clause()?; - let qualifier = Name::from_node(name_ref); - let from_item = resolve::find_from_item_in_from_clause(&from_clause, &qualifier)?; +fn hover_subquery_table(def_node: &SyntaxNode) -> Option { + let alias = def_node.ancestors().find_map(ast::Alias::cast)?; + let name = Name::from_node(&alias.name()?); + let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?; let paren_select = from_item.paren_select()?; - format_subquery_table(name_ref, &paren_select) + format_subquery_table(&name, &paren_select) } -fn format_subquery_table( - name_ref: &ast::NameRef, - paren_select: &ast::ParenSelect, -) -> Option { - let name = name_ref.syntax().text().to_string(); +fn format_subquery_table(name: &Name, paren_select: &ast::ParenSelect) -> Option { + let name = name.to_string(); let query = paren_select.syntax().text().to_string(); Some(format!("subquery {} as {}", name, query)) } @@ -968,6 +974,8 @@ fn format_create_table( fn format_create_view(create_view: &ast::CreateView, binder: &binder::Binder) -> Option { let path = create_view.path()?; + // TODO: we use this to infer the schema, we should either rename this or + // create a different function let (schema, view_name) = resolve::resolve_view_info(binder, &path)?; let schema = schema.to_string(); diff --git a/crates/squawk_ide/src/inlay_hints.rs b/crates/squawk_ide/src/inlay_hints.rs index 2ba34114..482aecd7 100644 --- a/crates/squawk_ide/src/inlay_hints.rs +++ b/crates/squawk_ide/src/inlay_hints.rs @@ -58,6 +58,7 @@ fn inlay_hint_call_expr( let file = match location.file { goto_definition::FileId::Current => file, + // TODO: we should salsa this goto_definition::FileId::Builtins => &ast::SourceFile::parse(BUILTINS_SQL).tree(), }; @@ -106,6 +107,7 @@ fn inlay_hint_insert( let file = match location.as_ref().map(|x| x.file) { Some(goto_definition::FileId::Current) | None => file, + // TODO: we should salsa this Some(goto_definition::FileId::Builtins) => &ast::SourceFile::parse(BUILTINS_SQL).tree(), }; @@ -120,6 +122,7 @@ fn inlay_hint_insert( }) }; + // TODO: we should salsa this let binder = binder::bind(file); let columns = if let Some(column_list) = insert.column_list() { diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index 0ae0c243..96a803a6 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -27,12 +27,11 @@ pub(crate) fn resolve_name_ref_ptrs( root: &SyntaxNode, name_ref: &ast::NameRef, ) -> Option> { - let context = classify_name_ref(name_ref)?; + let context = classify_name_ref(name_ref.syntax())?; match context { NameRefClass::Table => { - let path = find_containing_path(name_ref)?; - let (table_name, schema) = extract_table_schema_from_path(&path)?; + let (table_name, schema) = extract_table_schema_from_name_ref(name_ref)?; let position = name_ref.syntax().text_range().start(); resolve_table_name_ptr(binder, &table_name, &schema, position).map(|ptr| smallvec![ptr]) } @@ -118,22 +117,18 @@ pub(crate) fn resolve_name_ref_ptrs( }; (type_name, schema) } else { - let path = find_containing_path(name_ref)?; - let (type_name, schema) = extract_table_schema_from_path(&path)?; - (type_name, schema) + extract_table_schema_from_name_ref(name_ref)? }; let position = name_ref.syntax().text_range().start(); resolve_type_name_ptr(binder, &type_name, &schema, position).map(|ptr| smallvec![ptr]) } NameRefClass::View => { - let path = find_containing_path(name_ref)?; - let (view_name, schema) = extract_table_schema_from_path(&path)?; + let (view_name, schema) = extract_table_schema_from_name_ref(name_ref)?; let position = name_ref.syntax().text_range().start(); resolve_view_name_ptr(binder, &view_name, &schema, position).map(|ptr| smallvec![ptr]) } NameRefClass::Sequence => { - let path = find_containing_path(name_ref)?; - let (sequence_name, schema) = extract_table_schema_from_path(&path)?; + let (sequence_name, schema) = extract_table_schema_from_name_ref(name_ref)?; let position = name_ref.syntax().text_range().start(); resolve_sequence_name_ptr(binder, &sequence_name, &schema, position) .map(|ptr| smallvec![ptr]) @@ -962,6 +957,11 @@ fn extract_table_schema_from_path(path: &ast::Path) -> Option<(Name, Option Option<(Name, Option)> { + let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; + extract_table_schema_from_path(&path) +} + fn resolve_select_qualified_column_ptr( binder: &Binder, root: &SyntaxNode, @@ -1780,15 +1780,6 @@ pub(crate) fn find_from_item_in_from_clause( None } -fn find_containing_path(name_ref: &ast::NameRef) -> Option { - for ancestor in name_ref.syntax().ancestors() { - if let Some(path) = ast::Path::cast(ancestor) { - return Some(path); - } - } - None -} - pub(crate) fn extract_table_name(path: &ast::Path) -> Option { let segment = path.segment()?; let name_ref = segment.name_ref()?; From 119e82559b031347617265d82af33fac3e0a2c02 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Wed, 25 Feb 2026 22:41:27 -0500 Subject: [PATCH 2/2] fix --- crates/squawk_ide/src/hover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index 92337dbd..092b3cfc 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -430,8 +430,8 @@ fn format_table_source(source: resolve::TableSource, binder: &binder::Binder) -> match source { resolve::TableSource::WithTable(with_table) => format_with_table(&with_table), resolve::TableSource::CreateView(create_view) => format_create_view(&create_view, binder), - resolve::TableSource::CreateMaterializedView(mv) => { - format_create_materialized_view(&mv, binder) + resolve::TableSource::CreateMaterializedView(create_materialized_view) => { + format_create_materialized_view(&create_materialized_view, binder) } resolve::TableSource::CreateTable(create_table) => { format_create_table(&create_table, binder)