From 5f316c5aee2e91b1fa6c183f43ee56f2b528874a Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sun, 19 Apr 2026 22:14:45 -0400 Subject: [PATCH] internal: refactor ide/server/wasm --- crates/squawk_ide/src/builtins.rs | 19 +- crates/squawk_ide/src/classify.rs | 68 +- crates/squawk_ide/src/code_actions.rs | 173 +-- crates/squawk_ide/src/completion.rs | 130 +- crates/squawk_ide/src/document_symbols.rs | 81 +- crates/squawk_ide/src/find_references.rs | 119 +- crates/squawk_ide/src/goto_definition.rs | 208 +-- crates/squawk_ide/src/hover.rs | 840 +++++------- crates/squawk_ide/src/inlay_hints.rs | 62 +- crates/squawk_ide/src/lib.rs | 1 + crates/squawk_ide/src/location.rs | 54 + crates/squawk_ide/src/offsets.rs | 17 +- crates/squawk_ide/src/resolve.rs | 1137 +++++++++-------- crates/squawk_ide/src/semantic_tokens.rs | 3 +- crates/squawk_server/src/global_state.rs | 27 +- .../src/handlers/goto_definition.rs | 2 +- .../squawk_server/src/handlers/inlay_hints.rs | 18 +- .../squawk_server/src/handlers/references.rs | 2 +- crates/squawk_server/src/lsp_utils.rs | 18 +- crates/squawk_wasm/src/lib.rs | 42 +- playground/src/providers.tsx | 3 + 21 files changed, 1450 insertions(+), 1574 deletions(-) create mode 100644 crates/squawk_ide/src/location.rs diff --git a/crates/squawk_ide/src/builtins.rs b/crates/squawk_ide/src/builtins.rs index a7810e0e..531abb07 100644 --- a/crates/squawk_ide/src/builtins.rs +++ b/crates/squawk_ide/src/builtins.rs @@ -1,29 +1,16 @@ #[cfg(not(target_arch = "wasm32"))] use etcetera::BaseStrategy; -use line_index::LineIndex; use salsa::Database as Db; -use squawk_syntax::{Parse, SourceFile}; #[cfg(not(target_arch = "wasm32"))] use url::Url; -use crate::binder::{self, Binder}; +use crate::db::File; pub(crate) const BUILTINS_SQL: &str = include_str!("generated/builtins.sql"); #[salsa::tracked] -pub fn parse_builtins(_db: &dyn Db) -> Parse { - SourceFile::parse(BUILTINS_SQL) -} - -#[salsa::tracked] -pub fn builtins_line_index(_db: &dyn Db) -> LineIndex { - LineIndex::new(BUILTINS_SQL) -} - -#[salsa::tracked] -pub fn builtins_binder(db: &dyn Db) -> Binder { - let builtins_tree = parse_builtins(db).tree(); - binder::bind(&builtins_tree) +pub fn builtins_file(db: &dyn Db) -> File { + File::new(db, BUILTINS_SQL.into()) } #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/squawk_ide/src/classify.rs b/crates/squawk_ide/src/classify.rs index c4230d5f..66d7a405 100644 --- a/crates/squawk_ide/src/classify.rs +++ b/crates/squawk_ide/src/classify.rs @@ -1,4 +1,4 @@ -use crate::symbols::Name; +use crate::{location::LocationKind, symbols::Name}; use squawk_syntax::{ SyntaxKind, SyntaxNode, ast::{self, AstNode}, @@ -884,7 +884,7 @@ pub(crate) fn classify_name(name: &ast::Name) -> Option { None } -pub(crate) fn classify_def_node(def_node: &SyntaxNode) -> Option { +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() { @@ -895,106 +895,106 @@ pub(crate) fn classify_def_node(def_node: &SyntaxNode) -> Option { in_column_list = true; } if ast::Param::can_cast(ancestor.kind()) { - return Some(NameRefClass::NamedArgParameter); + return Some(LocationKind::NamedArgParameter); } if ast::CreateTableLike::can_cast(ancestor.kind()) { if in_column { - return Some(NameRefClass::SelectColumn); + return Some(LocationKind::Column); } - return Some(NameRefClass::Table); + return Some(LocationKind::Table); } if ast::CreateType::can_cast(ancestor.kind()) { if in_column { - return Some(NameRefClass::CompositeTypeField); + return Some(LocationKind::Column); } - return Some(NameRefClass::Type); + return Some(LocationKind::Type); } if ast::CreateFunction::can_cast(ancestor.kind()) { - return Some(NameRefClass::Function); + return Some(LocationKind::Function); } if ast::CreateProcedure::can_cast(ancestor.kind()) { - return Some(NameRefClass::Procedure); + return Some(LocationKind::Procedure); } if ast::WithTable::can_cast(ancestor.kind()) { if in_column_list { - return Some(NameRefClass::SelectColumn); + return Some(LocationKind::Column); } - return Some(NameRefClass::Table); + return Some(LocationKind::Table); } if ast::CreateTableAs::can_cast(ancestor.kind()) { - return Some(NameRefClass::Table); + return Some(LocationKind::Table); } if ast::CreateIndex::can_cast(ancestor.kind()) { - return Some(NameRefClass::Index); + return Some(LocationKind::Index); } if ast::CreateSequence::can_cast(ancestor.kind()) { - return Some(NameRefClass::Sequence); + return Some(LocationKind::Sequence); } if ast::CreateTrigger::can_cast(ancestor.kind()) { - return Some(NameRefClass::Trigger); + return Some(LocationKind::Trigger); } if ast::CreateEventTrigger::can_cast(ancestor.kind()) { - return Some(NameRefClass::EventTrigger); + return Some(LocationKind::EventTrigger); } if ast::CreateTablespace::can_cast(ancestor.kind()) { - return Some(NameRefClass::Tablespace); + return Some(LocationKind::Tablespace); } if ast::CreateDatabase::can_cast(ancestor.kind()) { - return Some(NameRefClass::Database); + return Some(LocationKind::Database); } if ast::CreateServer::can_cast(ancestor.kind()) { - return Some(NameRefClass::Server); + return Some(LocationKind::Server); } if ast::CreateExtension::can_cast(ancestor.kind()) { - return Some(NameRefClass::Extension); + return Some(LocationKind::Extension); } if ast::CreateRole::can_cast(ancestor.kind()) { - return Some(NameRefClass::Role); + return Some(LocationKind::Role); } if ast::CreateAggregate::can_cast(ancestor.kind()) { - return Some(NameRefClass::Aggregate); + return Some(LocationKind::Aggregate); } if ast::CreateSchema::can_cast(ancestor.kind()) { - return Some(NameRefClass::Schema); + return Some(LocationKind::Schema); } if ast::CreateView::can_cast(ancestor.kind()) || ast::CreateMaterializedView::can_cast(ancestor.kind()) { if in_column_list { - return Some(NameRefClass::SelectColumn); + return Some(LocationKind::Column); } - return Some(NameRefClass::View); + return Some(LocationKind::View); } if ast::CreatePolicy::can_cast(ancestor.kind()) { - return Some(NameRefClass::Policy); + return Some(LocationKind::Policy); } if ast::CreatePropertyGraph::can_cast(ancestor.kind()) { - return Some(NameRefClass::PropertyGraph); + return Some(LocationKind::PropertyGraph); } if ast::Declare::can_cast(ancestor.kind()) { - return Some(NameRefClass::Cursor); + return Some(LocationKind::Cursor); } if ast::Prepare::can_cast(ancestor.kind()) { - return Some(NameRefClass::PreparedStatement); + return Some(LocationKind::PreparedStatement); } if ast::Listen::can_cast(ancestor.kind()) { - return Some(NameRefClass::Channel); + return Some(LocationKind::Channel); } if ast::Alias::can_cast(ancestor.kind()) { if in_column { - return Some(NameRefClass::SelectColumn); + return Some(LocationKind::Column); } - return Some(NameRefClass::FromTable); + return Some(LocationKind::Table); } if ast::WindowDef::can_cast(ancestor.kind()) { - return Some(NameRefClass::Window); + return Some(LocationKind::Window); } 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); + return Some(LocationKind::Column); } } None diff --git a/crates/squawk_ide/src/code_actions.rs b/crates/squawk_ide/src/code_actions.rs index 5d67d6ef..7dcf77e0 100644 --- a/crates/squawk_ide/src/code_actions.rs +++ b/crates/squawk_ide/src/code_actions.rs @@ -9,9 +9,8 @@ use squawk_syntax::{ use std::iter; use crate::{ - binder, column_name::ColumnName, - db::{File, parse}, + db::{File, bind, parse}, offsets::token_from_offset, quote::{quote_column_alias, unquote_ident}, symbols::Name, @@ -32,38 +31,37 @@ pub struct CodeAction { #[salsa::tracked] pub fn code_actions(db: &dyn Db, file: File, offset: TextSize) -> Option> { - let parse = parse(db, file); - let source_file = parse.tree(); - let mut actions = vec![]; - rewrite_as_regular_string(&mut actions, &source_file, offset); - rewrite_as_dollar_quoted_string(&mut actions, &source_file, offset); - remove_else_clause(&mut actions, &source_file, offset); - rewrite_table_as_select(&mut actions, &source_file, offset); - rewrite_select_as_table(&mut actions, &source_file, offset); - rewrite_from(&mut actions, &source_file, offset); - rewrite_leading_from(&mut actions, &source_file, offset); - rewrite_values_as_select(&mut actions, &source_file, offset); - rewrite_select_as_values(&mut actions, &source_file, offset); - add_schema(&mut actions, &source_file, offset); - quote_identifier(&mut actions, &source_file, offset); - unquote_identifier(&mut actions, &source_file, offset); - add_explicit_alias(&mut actions, &source_file, offset); - remove_redundant_alias(&mut actions, &source_file, offset); - rewrite_cast_to_double_colon(&mut actions, &source_file, offset); - rewrite_double_colon_to_cast(&mut actions, &source_file, offset); - rewrite_between_as_binary_expression(&mut actions, &source_file, offset); - rewrite_not_equals_operator(&mut actions, &source_file, offset); - rewrite_timestamp_type(&mut actions, &source_file, offset); + rewrite_as_regular_string(db, file, &mut actions, offset); + rewrite_as_dollar_quoted_string(db, file, &mut actions, offset); + remove_else_clause(db, file, &mut actions, offset); + rewrite_table_as_select(db, file, &mut actions, offset); + rewrite_select_as_table(db, file, &mut actions, offset); + rewrite_from(db, file, &mut actions, offset); + rewrite_leading_from(db, file, &mut actions, offset); + rewrite_values_as_select(db, file, &mut actions, offset); + rewrite_select_as_values(db, file, &mut actions, offset); + add_schema(db, file, &mut actions, offset); + quote_identifier(db, file, &mut actions, offset); + unquote_identifier(db, file, &mut actions, offset); + add_explicit_alias(db, file, &mut actions, offset); + remove_redundant_alias(db, file, &mut actions, offset); + rewrite_cast_to_double_colon(db, file, &mut actions, offset); + rewrite_double_colon_to_cast(db, file, &mut actions, offset); + rewrite_between_as_binary_expression(db, file, &mut actions, offset); + rewrite_not_equals_operator(db, file, &mut actions, offset); + rewrite_timestamp_type(db, file, &mut actions, offset); Some(actions) } fn rewrite_as_regular_string( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let dollar_string = file + let dollar_string = parse(db, file) + .tree() .syntax() .token_at_offset(offset) .find(|token| token.kind() == SyntaxKind::DOLLAR_QUOTED_STRING)?; @@ -79,11 +77,13 @@ fn rewrite_as_regular_string( } fn rewrite_as_dollar_quoted_string( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let string = file + let string = parse(db, file) + .tree() .syntax() .token_at_offset(offset) .find(|token| token.kind() == SyntaxKind::STRING)?; @@ -160,11 +160,14 @@ fn dollar_delimiter(content: &str) -> Option { } fn remove_else_clause( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let else_token = file + let source_file = parse(db, file).tree(); + + let else_token = source_file .syntax() .token_at_offset(offset) .find(|x| x.kind() == SyntaxKind::ELSE_KW)?; @@ -188,11 +191,12 @@ fn remove_else_clause( } fn rewrite_table_as_select( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let table = token.parent_ancestors().find_map(ast::Table::cast)?; let relation_name = table.relation_name()?; @@ -210,11 +214,12 @@ fn rewrite_table_as_select( } fn rewrite_select_as_table( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; if !can_transform_select_to_table(&select) { @@ -244,11 +249,12 @@ fn rewrite_select_as_table( } fn rewrite_from( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; if select.select_clause().is_some() { @@ -270,11 +276,12 @@ fn rewrite_from( } fn rewrite_leading_from( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; let from_clause = select.from_clause()?; @@ -390,11 +397,12 @@ fn can_transform_select_to_table(select: &ast::Select) -> bool { } fn quote_identifier( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let parent = token.parent()?; let name_node = if let Some(name) = ast::Name::cast(parent.clone()) { @@ -423,11 +431,12 @@ fn quote_identifier( } fn unquote_identifier( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let parent = token.parent()?; let name_node = if let Some(name) = ast::Name::cast(parent.clone()) { @@ -453,11 +462,12 @@ fn unquote_identifier( // Postgres' parser calls this a column label. // Third-party docs call these aliases, so going with that. fn add_explicit_alias( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let target = token.parent_ancestors().find_map(ast::Target::cast)?; if target.as_name().is_some() { @@ -489,11 +499,12 @@ fn add_explicit_alias( } fn remove_redundant_alias( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let target = token.parent_ancestors().find_map(ast::Target::cast)?; let as_name = target.as_name()?; @@ -526,11 +537,12 @@ fn remove_redundant_alias( } fn add_schema( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let range = token.parent_ancestors().find_map(|node| { if let Some(path) = ast::Path::cast(node.clone()) { if path.qualifier().is_some() { @@ -556,8 +568,7 @@ fn add_schema( } let position = token.text_range().start(); - // TODO: we should salsa this - let binder = binder::bind(file); + let binder = bind(db, file); // TODO: we don't need the search path at the current position, we need to // lookup the definition of the item and see what the definition's search // path is. @@ -580,11 +591,12 @@ fn add_schema( } fn rewrite_cast_to_double_colon( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let cast_expr = token.parent_ancestors().find_map(ast::CastExpr::cast)?; if cast_expr.colon_colon().is_some() { @@ -609,11 +621,12 @@ fn rewrite_cast_to_double_colon( } fn rewrite_double_colon_to_cast( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let cast_expr = token.parent_ancestors().find_map(ast::CastExpr::cast)?; if cast_expr.cast_token().is_some() { @@ -638,11 +651,12 @@ fn rewrite_double_colon_to_cast( } fn rewrite_between_as_binary_expression( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let between_expr = token.parent_ancestors().find_map(ast::BetweenExpr::cast)?; let target = between_expr.target()?; @@ -684,11 +698,12 @@ fn rewrite_between_as_binary_expression( } fn rewrite_not_equals_operator( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let bin_expr = token.parent_ancestors().find_map(ast::BinExpr::cast)?; let (op_token, replacement, title) = match bin_expr.op()? { @@ -707,11 +722,12 @@ fn rewrite_not_equals_operator( } fn rewrite_timestamp_type( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let time_type = token.parent_ancestors().find_map(ast::TimeType::cast)?; let replacement = match time_type.timezone()? { @@ -741,11 +757,12 @@ fn rewrite_timestamp_type( } fn rewrite_values_as_select( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let values = token.parent_ancestors().find_map(ast::Values::cast)?; let value_token_start = values.values_token().map(|x| x.text_range().start())?; @@ -849,11 +866,12 @@ impl SelectContext { } fn rewrite_select_as_values( + db: &dyn Db, + file: File, actions: &mut Vec, - file: &ast::SourceFile, offset: TextSize, ) -> Option<()> { - let token = token_from_offset(file, offset)?; + let token = token_from_offset(db, file, offset)?; let parent = find_select_parent(token)?; @@ -927,23 +945,25 @@ fn find_select_parent(token: SyntaxToken) -> Option { #[cfg(test)] mod test { use super::*; + use crate::db::{Database, File}; use crate::test_utils::fixture; use insta::assert_snapshot; use rowan::TextSize; use squawk_syntax::ast; fn apply_code_action( - f: impl Fn(&mut Vec, &ast::SourceFile, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, sql: &str, ) -> String { let (mut offset, sql) = fixture(sql); - let parse = ast::SourceFile::parse(&sql); - let file: ast::SourceFile = parse.tree(); + let db = Database::default(); + let file = File::new(&db, sql.clone().into()); + let parse_result = parse(&db, file); offset = offset.checked_sub(1.into()).unwrap_or_default(); let mut actions = vec![]; - f(&mut actions, &file, offset); + f(&db, file, &mut actions, offset); assert!( !actions.is_empty(), @@ -957,7 +977,7 @@ mod test { // Quickfixes can fix syntax errors so we don't assert } ActionKind::RefactorRewrite => { - assert_eq!(parse.errors(), vec![]); + assert_eq!(parse_result.errors(), vec![]); } } @@ -1012,31 +1032,32 @@ mod test { } fn code_action_not_applicable_( - f: impl Fn(&mut Vec, &ast::SourceFile, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, sql: &str, allow_errors: bool, ) -> bool { let (offset, sql) = fixture(sql); - let parse = ast::SourceFile::parse(&sql); + let db = Database::default(); + let file = File::new(&db, sql.clone().into()); + let parse_result = parse(&db, file); if !allow_errors { - assert_eq!(parse.errors(), vec![]); + assert_eq!(parse_result.errors(), vec![]); } - let file: ast::SourceFile = parse.tree(); let mut actions = vec![]; - f(&mut actions, &file, offset); + f(&db, file, &mut actions, offset); actions.is_empty() } fn code_action_not_applicable( - f: impl Fn(&mut Vec, &ast::SourceFile, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, sql: &str, ) -> bool { code_action_not_applicable_(f, sql, false) } fn code_action_not_applicable_with_errors( - f: impl Fn(&mut Vec, &ast::SourceFile, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, sql: &str, ) -> bool { code_action_not_applicable_(f, sql, true) diff --git a/crates/squawk_ide/src/completion.rs b/crates/squawk_ide/src/completion.rs index ca591e1c..18b3a29d 100644 --- a/crates/squawk_ide/src/completion.rs +++ b/crates/squawk_ide/src/completion.rs @@ -1,17 +1,16 @@ use rowan::TextSize; use salsa::Database as Db; use squawk_syntax::ast::{self, AstNode}; -use squawk_syntax::{SyntaxKind, SyntaxToken}; +use squawk_syntax::{SyntaxKind, SyntaxNode, SyntaxToken}; use crate::binder; -use crate::db::{File, parse}; +use crate::db::{File, bind, parse}; use crate::resolve; use crate::symbols::{Name, Schema, SymbolKind}; use crate::tokens::is_string_or_comment; const COMPLETION_MARKER: &str = "squawkCompletionMarker"; -#[salsa::tracked] pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec { let parse = parse(db, file); let source_file = parse.tree(); @@ -33,35 +32,33 @@ pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec table_completions(&marker_file, &token), CompletionContext::Default => default_completions(), CompletionContext::SelectClause(select_clause) => { - select_completions(&marker_file, select_clause, &token) + select_completions(db, file, select_clause, &token) } CompletionContext::SelectClauses(select) => select_clauses_completions(&select), - CompletionContext::SelectExpr(select) => { - select_expr_completions(&marker_file, &select, &token) - } - CompletionContext::LimitClause => limit_completions(&marker_file, &token), - CompletionContext::OffsetClause => offset_completions(&marker_file, &token), + CompletionContext::SelectExpr(select) => select_expr_completions(db, file, &select, &token), + CompletionContext::LimitClause => limit_completions(db, file, &token), + CompletionContext::OffsetClause => offset_completions(db, file, &token), CompletionContext::DeleteClauses(delete) => { delete_clauses_completions(&marker_file, &delete, &token) } CompletionContext::DeleteExpr(delete) => { - delete_expr_completions(&marker_file, &delete, &token) + delete_expr_completions(db, file, &marker_file, &delete, &token) } } } fn select_completions( - file: &ast::SourceFile, + db: &dyn Db, + file: File, select_clause: ast::SelectClause, token: &SyntaxToken, ) -> Vec { - // TODO: we should salsa this - let binder = binder::bind(file); + let binder = bind(db, file); let mut completions = vec![]; let schema = schema_qualifier_at_token(token); let position = token.text_range().start(); - completions.extend(function_completions(&binder, file, &schema, position)); + completions.extend(function_completions(db, file, &schema, position)); let tables = binder.all_symbols_by_kind(SymbolKind::Table, schema.as_ref()); completions.extend(tables.into_iter().map(|name| CompletionItem { @@ -91,7 +88,7 @@ fn select_completions( trigger_completion_after_insert: false, sort_text: None, }); - completions.extend(column_completions_from_clause(&binder, file, &from_clause)); + completions.extend(column_completions_from_clause(db, file, &from_clause)); } else if schema.is_none() { completions.extend(select_clauses_completions(&select)); } @@ -256,9 +253,7 @@ fn select_clauses_completions(select: &ast::Select) -> Vec { completions } -fn limit_completions(file: &ast::SourceFile, token: &SyntaxToken) -> Vec { - // TODO: we should salsa this - let binder = binder::bind(file); +fn limit_completions(db: &dyn Db, file: File, token: &SyntaxToken) -> Vec { let schema = schema_qualifier_at_token(token); let position = token.text_range().start(); @@ -272,31 +267,28 @@ fn limit_completions(file: &ast::SourceFile, token: &SyntaxToken) -> Vec Vec { - // TODO: we should salsa this - let binder = binder::bind(file); +fn offset_completions(db: &dyn Db, file: File, token: &SyntaxToken) -> Vec { let schema = schema_qualifier_at_token(token); let position = token.text_range().start(); - function_completions(&binder, file, &schema, position) + function_completions(db, file, &schema, position) } fn select_expr_completions( - file: &ast::SourceFile, + db: &dyn Db, + file: File, 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); let position = token.text_range().start(); - completions.extend(function_completions(&binder, file, &schema, position)); + completions.extend(function_completions(db, file, &schema, position)); if let Some(from_clause) = select.from_clause() { for from_item in from_clause.from_items() { @@ -313,25 +305,26 @@ fn select_expr_completions( } } - completions.extend(column_completions_from_clause(&binder, file, &from_clause)); + completions.extend(column_completions_from_clause(db, file, &from_clause)); } completions } fn function_completions( - binder: &binder::Binder, - file: &ast::SourceFile, + db: &dyn Db, + file: File, schema: &Option, position: TextSize, ) -> Vec { + let binder = bind(db, file); binder .all_symbols_by_kind(SymbolKind::Function, schema.as_ref()) .into_iter() .map(|name| CompletionItem { label: format!("{name}()"), kind: CompletionItemKind::Function, - detail: function_detail(binder, file, name, schema, position), + detail: function_detail(db, file, name, schema, position), insert_text: None, insert_text_format: None, trigger_completion_after_insert: false, @@ -341,16 +334,17 @@ fn function_completions( } fn column_completions_from_clause( - binder: &binder::Binder, - file: &ast::SourceFile, + db: &dyn Db, + file: File, from_clause: &ast::FromClause, ) -> Vec { let mut completions = vec![]; - for table_ptr in resolve::table_ptrs_from_clause(binder, from_clause) { - let table_node = table_ptr.to_node(file.syntax()); + let syntax_root = from_clause.syntax().ancestors().last().unwrap(); + for table_ptr in resolve::table_ptrs_from_clause(db, file, from_clause) { + let table_node = table_ptr.to_node(&syntax_root); match resolve::find_table_source(&table_node) { Some(resolve::TableSource::CreateTable(create_table)) => { - let columns = resolve::collect_table_columns(binder, file.syntax(), &create_table); + let columns = resolve::collect_table_columns(db, file, &create_table); completions.extend(columns.into_iter().filter_map(|column| { let name = column.name()?; let detail = column.ty().map(|t| t.syntax().text().to_string()); @@ -366,11 +360,7 @@ fn column_completions_from_clause( })); } Some(resolve::TableSource::WithTable(with_table)) => { - let columns = resolve::collect_with_table_columns_with_types( - binder, - file.syntax(), - &with_table, - ); + let columns = resolve::collect_with_table_columns_with_types(db, file, with_table); completions.extend(columns.into_iter().map(|(name, ty)| CompletionItem { label: name.to_string(), kind: CompletionItemKind::Column, @@ -394,9 +384,8 @@ fn column_completions_from_clause( })); } Some(resolve::TableSource::CreateMaterializedView(create_materialized_view)) => { - let columns = resolve::collect_materialized_view_columns_with_types( - &create_materialized_view, - ); + let columns = + resolve::collect_materialized_view_columns_with_types(create_materialized_view); completions.extend(columns.into_iter().map(|(name, ty)| CompletionItem { label: name.to_string(), kind: CompletionItemKind::Column, @@ -415,7 +404,7 @@ fn column_completions_from_clause( .filter_map(|column| column.name().map(|name| Name::from_node(&name))) .collect(); - let base_columns = alias_base_columns_with_types(binder, file, &alias); + let base_columns = alias_base_columns_with_types(db, file, &syntax_root, &alias); for (idx, alias_column) in alias_columns.iter().enumerate() { completions.push(CompletionItem { @@ -446,11 +435,8 @@ fn column_completions_from_clause( ); } Some(resolve::TableSource::ParenSelect(paren_select)) => { - let columns = resolve::collect_paren_select_columns_with_types( - binder, - file.syntax(), - &paren_select, - ); + let columns = + resolve::collect_paren_select_columns_with_types(db, file, &paren_select); completions.extend(columns.into_iter().map(|(name, ty)| CompletionItem { label: name.to_string(), kind: CompletionItemKind::Column, @@ -468,22 +454,23 @@ fn column_completions_from_clause( } fn alias_base_columns_with_types( - binder: &binder::Binder, - file: &ast::SourceFile, + db: &dyn Db, + file: File, + syntax_root: &SyntaxNode, alias: &ast::Alias, ) -> Vec<(Name, Option)> { let Some(from_item) = alias.syntax().ancestors().find_map(ast::FromItem::cast) else { return vec![]; }; - let Some(table_ptr) = resolve::table_ptr_from_from_item(binder, &from_item) else { + let Some(table_ptr) = resolve::table_ptr_from_from_item(db, file, &from_item) else { return vec![]; }; - let table_node = table_ptr.to_node(file.syntax()); + let table_node = table_ptr.to_node(syntax_root); match resolve::find_table_source(&table_node) { Some(resolve::TableSource::CreateTable(create_table)) => { - resolve::collect_table_columns(binder, file.syntax(), &create_table) + resolve::collect_table_columns(db, file, &create_table) .into_iter() .filter_map(|column| { let name = column.name()?; @@ -493,7 +480,7 @@ fn alias_base_columns_with_types( .collect() } Some(resolve::TableSource::WithTable(with_table)) => { - resolve::collect_with_table_columns_with_types(binder, file.syntax(), &with_table) + resolve::collect_with_table_columns_with_types(db, file, with_table) .into_iter() .map(|(name, ty)| (name, ty.map(|t| t.to_string()))) .collect() @@ -505,13 +492,13 @@ fn alias_base_columns_with_types( .collect() } Some(resolve::TableSource::CreateMaterializedView(create_materialized_view)) => { - resolve::collect_materialized_view_columns_with_types(&create_materialized_view) + resolve::collect_materialized_view_columns_with_types(create_materialized_view) .into_iter() .map(|(name, ty)| (name, ty.map(|t| t.to_string()))) .collect() } Some(resolve::TableSource::ParenSelect(paren_select)) => { - resolve::collect_paren_select_columns_with_types(binder, file.syntax(), &paren_select) + resolve::collect_paren_select_columns_with_types(db, file, &paren_select) .into_iter() .map(|(name, ty)| (name, ty.map(|t| t.to_string()))) .collect() @@ -558,7 +545,6 @@ 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()); @@ -634,12 +620,13 @@ fn delete_clauses_completions( } fn delete_expr_completions( - file: &ast::SourceFile, + db: &dyn Db, + file: File, + source_file: &ast::SourceFile, delete: &ast::Delete, token: &SyntaxToken, ) -> Vec { - // TODO: we should salsa this - let binder = binder::bind(file); + let binder = binder::bind(source_file); let mut completions = vec![]; let Some(path) = delete.relation_name().and_then(|r| r.path()) else { @@ -659,7 +646,7 @@ fn delete_expr_completions( completions.extend(functions.into_iter().map(|name| CompletionItem { label: name.to_string(), kind: CompletionItemKind::Function, - detail: function_detail(&binder, file, name, &schema, position), + detail: function_detail(db, file, name, &schema, position), insert_text: None, insert_text_format: None, trigger_completion_after_insert: false, @@ -670,7 +657,7 @@ fn delete_expr_completions( completions.extend(functions.into_iter().map(|name| CompletionItem { label: format!("{name}()"), kind: CompletionItemKind::Function, - detail: function_detail(&binder, file, name, &schema, position), + detail: function_detail(db, file, name, &schema, position), insert_text: None, insert_text_format: None, trigger_completion_after_insert: false, @@ -692,11 +679,11 @@ fn delete_expr_completions( if let Some(table_ptr) = binder.lookup_with(&delete_table_name, SymbolKind::Table, position, &schema) && let Some(create_table) = table_ptr - .to_node(file.syntax()) + .to_node(source_file.syntax()) .ancestors() .find_map(ast::CreateTableLike::cast) { - let columns = resolve::collect_table_columns(&binder, file.syntax(), &create_table); + let columns = resolve::collect_table_columns(db, file, &create_table); completions.extend(columns.into_iter().filter_map(|column| { let name = column.name()?; let detail = column.ty().map(|t| t.syntax().text().to_string()); @@ -849,7 +836,6 @@ 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() } @@ -858,19 +844,21 @@ fn schema_qualifier_at_token(token: &SyntaxToken) -> Option { } fn function_detail( - binder: &binder::Binder, - file: &ast::SourceFile, + db: &dyn Db, + file: File, function_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); + let source_file = parse(db, file).tree(); let create_function = binder .lookup_with(function_name, SymbolKind::Function, position, schema)? - .to_node(file.syntax()) + .to_node(source_file.syntax()) .ancestors() .find_map(ast::CreateFunction::cast)?; let path = create_function.path()?; - let (schema, function_name) = resolve::resolve_function_info(binder, &path)?; + let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?; let param_list = create_function.param_list()?; let params = param_list.syntax().text().to_string(); diff --git a/crates/squawk_ide/src/document_symbols.rs b/crates/squawk_ide/src/document_symbols.rs index 1feac484..116f7907 100644 --- a/crates/squawk_ide/src/document_symbols.rs +++ b/crates/squawk_ide/src/document_symbols.rs @@ -2,7 +2,7 @@ use rowan::TextRange; use salsa::Database as Db; use squawk_syntax::ast::{self, AstNode}; -use crate::binder::{self, extract_string_literal}; +use crate::binder::extract_string_literal; use crate::db::{File, parse}; use crate::resolve::{ resolve_aggregate_info, resolve_function_info, resolve_procedure_info, resolve_sequence_info, @@ -54,14 +54,9 @@ pub struct DocumentSymbol { #[salsa::tracked] pub fn document_symbols(db: &dyn Db, file: File) -> Vec { - let parse = parse(db, file); - let source_file = parse.tree(); - - // TODO: we should salsa this - let binder = binder::bind(&source_file); let mut symbols = vec![]; - for stmt in source_file.stmts() { + for stmt in parse(db, file).tree().stmts() { match stmt { ast::Stmt::CreateSchema(create_schema) => { if let Some(symbol) = create_schema_symbol(create_schema) { @@ -69,32 +64,32 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateTable(create_table) => { - if let Some(symbol) = create_table_symbol(&binder, create_table) { + if let Some(symbol) = create_table_symbol(db, file, create_table) { symbols.push(symbol); } } ast::Stmt::CreateTableAs(create_table_as) => { - if let Some(symbol) = create_table_as_symbol(&binder, create_table_as) { + if let Some(symbol) = create_table_as_symbol(db, file, create_table_as) { symbols.push(symbol); } } ast::Stmt::CreateForeignTable(create_foreign_table) => { - if let Some(symbol) = create_table_symbol(&binder, create_foreign_table) { + if let Some(symbol) = create_table_symbol(db, file, create_foreign_table) { symbols.push(symbol); } } ast::Stmt::CreateFunction(create_function) => { - if let Some(symbol) = create_function_symbol(&binder, create_function) { + if let Some(symbol) = create_function_symbol(db, file, create_function) { symbols.push(symbol); } } ast::Stmt::CreateAggregate(create_aggregate) => { - if let Some(symbol) = create_aggregate_symbol(&binder, create_aggregate) { + if let Some(symbol) = create_aggregate_symbol(db, file, create_aggregate) { symbols.push(symbol); } } ast::Stmt::CreateProcedure(create_procedure) => { - if let Some(symbol) = create_procedure_symbol(&binder, create_procedure) { + if let Some(symbol) = create_procedure_symbol(db, file, create_procedure) { symbols.push(symbol); } } @@ -104,12 +99,12 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateDomain(create_domain) => { - if let Some(symbol) = create_domain_symbol(&binder, create_domain) { + if let Some(symbol) = create_domain_symbol(db, file, create_domain) { symbols.push(symbol); } } ast::Stmt::CreateSequence(create_sequence) => { - if let Some(symbol) = create_sequence_symbol(&binder, create_sequence) { + if let Some(symbol) = create_sequence_symbol(db, file, create_sequence) { symbols.push(symbol); } } @@ -159,17 +154,17 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateType(create_type) => { - if let Some(symbol) = create_type_symbol(&binder, create_type) { + if let Some(symbol) = create_type_symbol(db, file, create_type) { symbols.push(symbol); } } ast::Stmt::CreateView(create_view) => { - if let Some(symbol) = create_view_symbol(&binder, create_view) { + if let Some(symbol) = create_view_symbol(db, file, create_view) { symbols.push(symbol); } } ast::Stmt::CreateMaterializedView(create_view) => { - if let Some(symbol) = create_materialized_view_symbol(&binder, create_view) { + if let Some(symbol) = create_materialized_view_symbol(db, file, create_view) { symbols.push(symbol); } } @@ -273,14 +268,15 @@ fn create_schema_symbol(create_schema: ast::CreateSchema) -> Option Option { let path = create_table.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, table_name) = resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve_table_info(db, file, &path)?; let name = format!("{}.{}", schema.0, table_name); let full_range = create_table.syntax().text_range(); @@ -308,7 +304,8 @@ fn create_table_symbol( } fn create_table_as_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_table_as: ast::CreateTableAs, ) -> Option { let path = create_table_as.path()?; @@ -319,7 +316,7 @@ fn create_table_as_symbol( return None; }; - let (schema, table_name) = resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve_table_info(db, file, &path)?; let name = format!("{}.{}", schema.0, table_name); let full_range = create_table_as.syntax().text_range(); @@ -338,14 +335,15 @@ fn create_table_as_symbol( } fn create_view_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_view: ast::CreateView, ) -> Option { let path = create_view.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, view_name) = resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve_view_info(db, file, &path)?; let name = format!("{}.{}", schema.0, view_name); let full_range = create_view.syntax().text_range(); @@ -388,14 +386,15 @@ fn symbols_from_column_list( // TODO: combine with create_view_symbol fn create_materialized_view_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_view: ast::CreateMaterializedView, ) -> Option { let path = create_view.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, view_name) = resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve_view_info(db, file, &path)?; let name = format!("{}.{}", schema.0, view_name); let full_range = create_view.syntax().text_range(); @@ -411,14 +410,15 @@ fn create_materialized_view_symbol( } fn create_function_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_function: ast::CreateFunction, ) -> Option { let path = create_function.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, function_name) = resolve_function_info(binder, &path)?; + let (schema, function_name) = resolve_function_info(db, file, &path)?; let name = format!("{}.{}", schema.0, function_name); let full_range = create_function.syntax().text_range(); @@ -435,14 +435,15 @@ fn create_function_symbol( } fn create_aggregate_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_aggregate: ast::CreateAggregate, ) -> Option { let path = create_aggregate.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, aggregate_name) = resolve_aggregate_info(binder, &path)?; + let (schema, aggregate_name) = resolve_aggregate_info(db, file, &path)?; let name = format!("{}.{}", schema.0, aggregate_name); let full_range = create_aggregate.syntax().text_range(); @@ -459,14 +460,15 @@ fn create_aggregate_symbol( } fn create_procedure_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_procedure: ast::CreateProcedure, ) -> Option { let path = create_procedure.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, procedure_name) = resolve_procedure_info(binder, &path)?; + let (schema, procedure_name) = resolve_procedure_info(db, file, &path)?; let name = format!("{}.{}", schema.0, procedure_name); let full_range = create_procedure.syntax().text_range(); @@ -500,14 +502,15 @@ fn create_index_symbol(create_index: ast::CreateIndex) -> Option } fn create_domain_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_domain: ast::CreateDomain, ) -> Option { let path = create_domain.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, domain_name) = resolve_type_info(binder, &path)?; + let (schema, domain_name) = resolve_type_info(db, file, &path)?; let name = format!("{}.{}", schema.0, domain_name); let full_range = create_domain.syntax().text_range(); @@ -524,14 +527,15 @@ fn create_domain_symbol( } fn create_sequence_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_sequence: ast::CreateSequence, ) -> Option { let path = create_sequence.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, sequence_name) = resolve_sequence_info(binder, &path)?; + let (schema, sequence_name) = resolve_sequence_info(db, file, &path)?; let name = format!("{}.{}", schema.0, sequence_name); let full_range = create_sequence.syntax().text_range(); @@ -707,14 +711,15 @@ fn create_property_graph_symbol( } fn create_type_symbol( - binder: &binder::Binder, + db: &dyn Db, + file: File, create_type: ast::CreateType, ) -> Option { let path = create_type.path()?; let segment = path.segment()?; let name_node = segment.name()?; - let (schema, type_name) = resolve_type_info(binder, &path)?; + let (schema, type_name) = resolve_type_info(db, file, &path)?; let name = format!("{}.{}", schema.0, type_name); let full_range = create_type.syntax().text_range(); diff --git a/crates/squawk_ide/src/find_references.rs b/crates/squawk_ide/src/find_references.rs index 680a3c28..2be7fecb 100644 --- a/crates/squawk_ide/src/find_references.rs +++ b/crates/squawk_ide/src/find_references.rs @@ -1,8 +1,6 @@ -use crate::binder::Binder; -use crate::builtins::{builtins_binder, parse_builtins}; -use crate::classify::classify_def_node; -use crate::db::{File, bind, parse}; -use crate::goto_definition::{FileId, Location, LocationKind}; +use crate::builtins::builtins_file; +use crate::db::{File, parse}; +use crate::location::{Location, LocationKind}; use crate::offsets::token_from_offset; use crate::resolve; use rowan::TextSize; @@ -11,109 +9,65 @@ use smallvec::{SmallVec, smallvec}; use squawk_syntax::{ SyntaxNodePtr, ast::{self, AstNode}, - match_ast, }; #[salsa::tracked] pub fn find_references(db: &dyn Db, file: File, offset: TextSize) -> Vec { - let parse = parse(db, file); - let source_file = parse.tree(); - - let current_binder = bind(db, file); - - let builtins_tree = parse_builtins(db).tree(); - let builtins_binder = builtins_binder(db); - - let Some((target_file, target_defs, target_kind)) = find_target_defs( - &source_file, - offset, - ¤t_binder, - &builtins_tree, - &builtins_binder, - ) else { + let Some((target_file, target_defs, target_kind)) = find_target_defs(db, offset, file) else { return vec![]; }; - let (binder, root) = match target_file { - FileId::Current => (¤t_binder, source_file.syntax()), - FileId::Builtins => (&builtins_binder, builtins_tree.syntax()), - }; - let mut refs: Vec = vec![]; - if target_file == FileId::Builtins { - for ptr in &target_defs { - refs.push(Location { - file: FileId::Builtins, - range: ptr.to_node(builtins_tree.syntax()).text_range(), - kind: target_kind, - }); - } + for ptr in &target_defs { + refs.push(Location { + file: target_file, + range: ptr.text_range(), + kind: target_kind, + }); } - for node in source_file.syntax().descendants() { - match_ast! { - match node { - ast::NameRef(name_ref) => { - // Check if the ref matches one of the defs - if let Some(found_defs) = resolve::resolve_name_ref_ptrs(binder, root, &name_ref) - && found_defs.iter().any(|def| target_defs.contains(def)) - { - refs.push(Location { - file: FileId::Current, - range: name_ref.syntax().text_range(), - kind: target_kind, - }); - } - }, - ast::Name(name) => { - // Find refs also includes the defs so we have to check. - let found = SyntaxNodePtr::new(name.syntax()); - if target_defs.contains(&found) { - refs.push(Location { - file: FileId::Current, - range: name.syntax().text_range(), - kind: target_kind, - }); - } - }, - _ => (), + for node in parse(db, file).tree().syntax().descendants() { + if let Some(name_ref) = ast::NameRef::cast(node) { + // Check if the ref matches one of the defs + if let Some(found_defs) = resolve::resolve_name_ref_ptrs(db, target_file, &name_ref) + && found_defs.iter().any(|def| target_defs.contains(def)) + { + refs.push(Location { + file, + range: name_ref.syntax().text_range(), + kind: target_kind, + }); } } } - refs.sort_by_key(|loc| (loc.file, loc.range.start())); + refs.sort_by_key(|loc| (loc.file != file, loc.range.start())); refs } fn find_target_defs( - file: &ast::SourceFile, + db: &dyn Db, offset: TextSize, - current_binder: &Binder, - builtins_tree: &ast::SourceFile, - builtins_binder: &Binder, -) -> Option<(FileId, SmallVec<[SyntaxNodePtr; 1]>, LocationKind)> { - let token = token_from_offset(file, offset)?; + current_file: File, +) -> Option<(File, SmallVec<[SyntaxNodePtr; 1]>, LocationKind)> { + let token = token_from_offset(db, current_file, offset)?; let parent = token.parent()?; if let Some(name) = ast::Name::cast(parent.clone()) - && let Some(kind) = classify_def_node(name.syntax()).map(LocationKind::from) + && let Some(kind) = LocationKind::from_node(name.syntax()) { return Some(( - FileId::Current, + current_file, smallvec![SyntaxNodePtr::new(name.syntax())], kind, )); } if let Some(name_ref) = ast::NameRef::cast(parent.clone()) { - for file_id in [FileId::Current, FileId::Builtins] { - let (binder, root) = match file_id { - FileId::Current => (current_binder, file.syntax()), - FileId::Builtins => (builtins_binder, builtins_tree.syntax()), - }; - if let Some((ptrs, kind)) = resolve::resolve_name_ref(binder, root, &name_ref) { - return Some((file_id, ptrs, kind)); + for target_file in [current_file, builtins_file(db)] { + if let Some((ptrs, kind)) = resolve::resolve_name_ref(db, target_file, &name_ref) { + return Some((target_file, ptrs, kind)); } } } @@ -123,10 +77,9 @@ fn find_target_defs( #[cfg(test)] mod test { - use crate::builtins::BUILTINS_SQL; + use crate::builtins::{BUILTINS_SQL, builtins_file}; use crate::db::{Database, File}; use crate::find_references::find_references; - use crate::goto_definition::FileId; use crate::test_utils::fixture; use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle}; use insta::assert_snapshot; @@ -146,11 +99,13 @@ mod test { let mut current_refs = vec![]; let mut builtin_refs = vec![]; + let builtins_file = builtins_file(&db); for (i, location) in references.iter().enumerate() { let label_index = i + 1; - match location.file { - FileId::Current => current_refs.push((label_index, location.range)), - FileId::Builtins => builtin_refs.push((label_index, location.range)), + if location.file == file { + current_refs.push((label_index, location.range)); + } else if location.file == builtins_file { + builtin_refs.push((label_index, location.range)); } } diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index 6d4cb422..6d48a2b8 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -1,7 +1,6 @@ -use crate::binder; -use crate::builtins::parse_builtins; -use crate::classify::{NameRefClass, classify_def_node}; +use crate::builtins::builtins_file; use crate::db::{File, parse}; +use crate::location::{Location, LocationKind}; use crate::offsets::token_from_offset; use crate::resolve; use rowan::{TextRange, TextSize}; @@ -14,9 +13,7 @@ use squawk_syntax::{ #[salsa::tracked] pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[Location; 1]> { - let parse = parse(db, file); - let source_file = &parse.tree(); - let Some(token) = token_from_offset(source_file, offset) else { + let Some(token) = token_from_offset(db, file, offset) else { return smallvec![]; }; let Some(parent) = token.parent() else { @@ -33,6 +30,7 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L && let Some(case_token) = case_expr.case_token() { return smallvec![Location::current( + file, case_token.text_range(), LocationKind::CaseExpr )]; @@ -42,47 +40,47 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L // goto def on COMMIT -> BEGIN/START TRANSACTION if ast::Commit::can_cast(parent.kind()) - && let Some(begin_range) = find_preceding_begin(source_file, token.text_range().start()) + && let Some(begin_range) = find_preceding_begin(db, file, offset) { - return smallvec![Location::current(begin_range, LocationKind::CommitBegin)]; + return smallvec![Location::current( + file, + begin_range, + LocationKind::CommitBegin + )]; } // goto def on ROLLBACK -> BEGIN/START TRANSACTION if ast::Rollback::can_cast(parent.kind()) - && let Some(begin_range) = find_preceding_begin(source_file, token.text_range().start()) + && let Some(begin_range) = find_preceding_begin(db, file, offset) { - return smallvec![Location::current(begin_range, LocationKind::CommitBegin)]; + return smallvec![Location::current( + file, + begin_range, + LocationKind::CommitBegin + )]; } // goto def on BEGIN/START TRANSACTION -> COMMIT or ROLLBACK if ast::Begin::can_cast(parent.kind()) - && let Some(end_range) = - find_following_commit_or_rollback(source_file, token.text_range().end()) + && let Some(end_range) = find_following_commit_or_rollback(db, file, offset) { - return smallvec![Location::current(end_range, LocationKind::CommitEnd)]; + return smallvec![Location::current(file, end_range, LocationKind::CommitEnd)]; } if let Some(name) = ast::Name::cast(parent.clone()) - && let Some(kind) = classify_def_node(name.syntax()).map(LocationKind::from) + && let Some(kind) = LocationKind::from_node(name.syntax()) { - return smallvec![Location::current(name.syntax().text_range(), kind)]; + return smallvec![Location::current(file, name.syntax().text_range(), kind)]; } if let Some(name_ref) = ast::NameRef::cast(parent.clone()) { - for file_id in [FileId::Current, FileId::Builtins] { - let file = match file_id { - FileId::Current => source_file, - FileId::Builtins => &parse_builtins(db).tree(), - }; - // TODO: we should salsa this - let binder_output = binder::bind(file); - let root = file.syntax(); - if let Some((ptrs, kind)) = resolve::resolve_name_ref(&binder_output, root, &name_ref) { + for definition_file in [file, builtins_file(db)] { + if let Some((ptrs, kind)) = resolve::resolve_name_ref(db, definition_file, &name_ref) { let ranges = ptrs .iter() - .map(|ptr| ptr.to_node(file.syntax()).text_range()) + .map(|ptr| ptr.text_range()) .map(|range| Location { - file: file_id, + file: definition_file, range, kind, }) @@ -101,18 +99,14 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L } }); if let Some(ty) = type_node { - for file_id in [FileId::Current, FileId::Builtins] { - let file = match file_id { - FileId::Current => source_file, - FileId::Builtins => &parse_builtins(db).tree(), - }; - // TODO: we should salsa this - let binder_output = binder::bind(file); + for definition_file in [file, builtins_file(db)] { let position = token.text_range().start(); - if let Some(ptr) = resolve::resolve_type_ptr_from_type(&binder_output, &ty, position) { + if let Some(ptr) = + resolve::resolve_type_ptr_from_type(db, definition_file, &ty, position) + { return smallvec![Location { - file: file_id, - range: ptr.to_node(file.syntax()).text_range(), + file: definition_file, + range: ptr.text_range(), kind: LocationKind::Type, }]; } @@ -122,127 +116,9 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L smallvec![] } -#[derive(Debug, PartialEq, Clone, Copy, Eq, PartialOrd, Ord)] -pub enum FileId { - Current, - Builtins, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum LocationKind { - Aggregate, - CaseExpr, - Channel, - Column, - CommitBegin, - CommitEnd, - Cursor, - Database, - EventTrigger, - Extension, - Function, - Index, - NamedArgParameter, - Policy, - PreparedStatement, - Procedure, - PropertyGraph, - Role, - Schema, - Sequence, - Server, - Table, - Tablespace, - Trigger, - Type, - View, - Window, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Location { - pub file: FileId, - pub range: TextRange, - pub kind: LocationKind, -} - -impl Location { - fn current(range: TextRange, kind: LocationKind) -> Location { - Location { - file: FileId::Current, - range, - kind, - } - } -} - -impl From for LocationKind { - fn from(class: NameRefClass) -> Self { - match class { - NameRefClass::Aggregate => LocationKind::Aggregate, - NameRefClass::Channel => LocationKind::Channel, - NameRefClass::Cursor => LocationKind::Cursor, - NameRefClass::Database => LocationKind::Database, - NameRefClass::EventTrigger => LocationKind::EventTrigger, - NameRefClass::Extension => LocationKind::Extension, - NameRefClass::Index => LocationKind::Index, - NameRefClass::NamedArgParameter => LocationKind::NamedArgParameter, - NameRefClass::Policy => LocationKind::Policy, - NameRefClass::PreparedStatement => LocationKind::PreparedStatement, - NameRefClass::PropertyGraph => LocationKind::PropertyGraph, - NameRefClass::PropertyGraphColumn => LocationKind::Column, - NameRefClass::Role => LocationKind::Role, - NameRefClass::Schema => LocationKind::Schema, - NameRefClass::Sequence => LocationKind::Sequence, - NameRefClass::Server => LocationKind::Server, - NameRefClass::Tablespace => LocationKind::Tablespace, - NameRefClass::Trigger => LocationKind::Trigger, - NameRefClass::Type => LocationKind::Type, - NameRefClass::View => LocationKind::View, - NameRefClass::Window => LocationKind::Window, - - NameRefClass::CallProcedure | NameRefClass::Procedure | NameRefClass::ProcedureCall => { - LocationKind::Procedure - } - - NameRefClass::Function - | NameRefClass::FunctionCall - | NameRefClass::FunctionName - | NameRefClass::Routine - | NameRefClass::SelectFunctionCall => LocationKind::Function, - - NameRefClass::AlterColumn - | NameRefClass::CompositeTypeField - | NameRefClass::ConstraintColumn - | NameRefClass::CreateIndexColumn - | NameRefClass::DeleteColumn - | NameRefClass::ForeignKeyColumn - | NameRefClass::InsertColumn - | NameRefClass::JoinUsingColumn - | NameRefClass::MergeColumn - | NameRefClass::PolicyColumn - | NameRefClass::QualifiedColumn - | NameRefClass::SelectColumn - | NameRefClass::SelectQualifiedColumn - | NameRefClass::UpdateColumn => LocationKind::Column, - - NameRefClass::DeleteQualifiedColumnTable - | NameRefClass::ForeignKeyTable - | NameRefClass::FromTable - | NameRefClass::InsertQualifiedColumnTable - | NameRefClass::LikeTable - | NameRefClass::MergeQualifiedColumnTable - | NameRefClass::PolicyQualifiedColumnTable - | NameRefClass::SelectQualifiedColumnTable - | NameRefClass::Table - | NameRefClass::UpdateQualifiedColumnTable => LocationKind::Table, - } - } -} - -fn find_preceding_begin(file: &ast::SourceFile, before: TextSize) -> Option { +fn find_preceding_begin(db: &dyn Db, file: File, before: TextSize) -> Option { let mut last_begin: Option = None; - for stmt in file.stmts() { + for stmt in parse(db, file).tree().stmts() { if let ast::Stmt::Begin(begin) = stmt { let range = begin.syntax().text_range(); if range.end() <= before { @@ -253,8 +129,12 @@ fn find_preceding_begin(file: &ast::SourceFile, before: TextSize) -> Option Option { - for stmt in file.stmts() { +fn find_following_commit_or_rollback( + db: &dyn Db, + file: File, + after: TextSize, +) -> Option { + for stmt in parse(db, file).tree().stmts() { let range = match &stmt { ast::Stmt::Commit(commit) => commit.syntax().text_range(), ast::Stmt::Rollback(rollback) => rollback.syntax().text_range(), @@ -269,9 +149,9 @@ fn find_following_commit_or_rollback(file: &ast::SourceFile, after: TextSize) -> #[cfg(test)] mod test { - use crate::builtins::BUILTINS_SQL; + use crate::builtins::{BUILTINS_SQL, builtins_file}; use crate::db::{Database, File}; - use crate::goto_definition::{FileId, goto_definition}; + use crate::goto_definition::goto_definition; use crate::test_utils::fixture; use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle}; use insta::assert_snapshot; @@ -298,11 +178,13 @@ mod test { let offset: usize = offset.into(); let mut current_dests = vec![]; let mut builtin_dests = vec![]; + let builtins_file = builtins_file(&db); for (i, location) in results.iter().enumerate() { let label_index = i + 2; - match location.file { - FileId::Current => current_dests.push((label_index, location.range)), - FileId::Builtins => builtin_dests.push((label_index, location.range)), + if location.file == file { + current_dests.push((label_index, location.range)); + } else if location.file == builtins_file { + builtin_dests.push((label_index, location.range)); } } diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index 46752e25..1c210eea 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -1,21 +1,19 @@ -use crate::builtins::parse_builtins; -use crate::classify::{NameClass, NameRefClass, classify_def_node, classify_name}; +use crate::builtins::builtins_file; +use crate::classify::{NameClass, classify_name}; use crate::column_name::ColumnName; use crate::comments::preceding_comment; -use crate::db::{File, parse}; +use crate::db::{File, bind, parse}; +use crate::location::LocationKind; use crate::offsets::token_from_offset; -use crate::{ - binder, - symbols::{Name, Schema}, -}; +use crate::symbols::{Name, Schema}; use crate::{goto_definition, resolve}; use rowan::TextSize; use salsa::Database as Db; -use squawk_syntax::SyntaxNode; use squawk_syntax::{ SyntaxKind, ast::{self, AstNode}, }; +use squawk_syntax::{SyntaxNode, SyntaxNodePtr}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct Hover { @@ -76,60 +74,41 @@ fn merge_hovers(hovers: Vec) -> Option { #[salsa::tracked] pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option { - let parse = parse(db, file); - let source_file = parse.tree(); - - let token = token_from_offset(&source_file, offset)?; + let token = token_from_offset(db, file, offset)?; let parent = token.parent()?; - let root = source_file.syntax(); - // TODO: we should salsa this - let binder = binder::bind(&source_file); - if token.kind() == SyntaxKind::STAR { if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) && field_expr.star_token().is_some() - && let Some(result) = hover_qualified_star(db, root, &field_expr, &binder) + && let Some(result) = hover_qualified_star(db, file, field_expr) { return Some(result); } if let Some(arg_list) = ast::ArgList::cast(parent.clone()) - && let Some(result) = hover_unqualified_star_in_arg_list(db, root, &arg_list, &binder) + && let Some(result) = hover_unqualified_star_in_arg_list(db, file, arg_list) { return Some(result); } if let Some(target) = ast::Target::cast(parent.clone()) && target.star_token().is_some() - && let Some(result) = hover_unqualified_star(db, root, &target, &binder) + && let Some(result) = hover_unqualified_star(db, file, target) { return Some(result); } + return None; } if let Some(name_ref) = ast::NameRef::cast(parent.clone()) { let definition = goto_definition::goto_definition(db, 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 = parse_builtins(db).tree(); - let binder = binder::bind(&builtins_tree); - let tree = builtins_tree.syntax().clone(); - (binder, tree) - } - }; - - let def_node = match root.covering_element(def.range) { + let tree = parse(db, def.file).tree(); + let def_node = match tree.syntax().covering_element(def.range) { rowan::NodeOrToken::Token(token) => token.parent()?, rowan::NodeOrToken::Node(node) => node, }; - - let context = classify_def_node(&def_node)?; - - return hover_name_ref(db, &root, &name_ref, &binder, context, &def_node); + return hover_name_ref(db, def.file, &name_ref, def.kind, &def_node); } if let Some(name) = ast::Name::cast(parent) { @@ -138,70 +117,70 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option { NameClass::ColumnDefinition { create_table, column, - } => return hover_column_definition(&create_table, &column, &binder), + } => return hover_column_definition(db, file, create_table, column), NameClass::CreateTable(create_table) => { - return format_create_table(&create_table, &binder); + return format_create_table(db, file, create_table); } - NameClass::WithTable(with_table) => return format_with_table(&with_table), + NameClass::WithTable(with_table) => return format_with_table(with_table), NameClass::CreateIndex(create_index) => { - return format_create_index(&create_index, &binder); + return format_create_index(db, file, create_index); } NameClass::CreateSequence(create_sequence) => { - return format_create_sequence(&create_sequence, &binder); + return format_create_sequence(db, file, create_sequence); } NameClass::CreateTrigger(create_trigger) => { - return format_create_trigger(&create_trigger, &binder); + return format_create_trigger(db, file, create_trigger); } NameClass::CreateEventTrigger(create_event_trigger) => { - return format_create_event_trigger(&create_event_trigger); + return format_create_event_trigger(create_event_trigger); } NameClass::CreateTablespace(create_tablespace) => { - return format_create_tablespace(&create_tablespace); + return format_create_tablespace(create_tablespace); } NameClass::CreateDatabase(create_database) => { - return format_create_database(&create_database); + return format_create_database(create_database); } NameClass::CreateServer(create_server) => { - return format_create_server(&create_server); + return format_create_server(create_server); } NameClass::CreateExtension(create_extension) => { - return format_create_extension(&create_extension); + return format_create_extension(create_extension); } NameClass::CreateRole(create_role) => { - return format_create_role(&create_role); + return format_create_role(create_role); } NameClass::CreateType(create_type) => { - return format_create_type(&create_type, &binder); + return format_create_type(db, file, create_type); } NameClass::CreateFunction(create_function) => { - return format_create_function(&create_function, &binder); + return format_create_function(db, file, create_function); } NameClass::CreateAggregate(create_aggregate) => { - return format_create_aggregate(&create_aggregate, &binder); + return format_create_aggregate(db, file, create_aggregate); } NameClass::CreateProcedure(create_procedure) => { - return format_create_procedure(&create_procedure, &binder); + return format_create_procedure(db, file, create_procedure); } NameClass::CreateSchema(create_schema) => { - return format_create_schema(&create_schema); + return format_create_schema(create_schema); } NameClass::ViewColumnList { create_view, name } => { - return format_view_column(&create_view, Name::from_node(&name), &binder); + return format_view_column(db, file, create_view, Name::from_node(&name)); } NameClass::CreateView(create_view) => { - return format_create_view(&create_view, &binder); + return format_create_view(db, file, create_view); } NameClass::CreatePropertyGraph(create_property_graph) => { - return format_create_property_graph(&create_property_graph, &binder); + return format_create_property_graph(db, file, create_property_graph); } NameClass::DeclareCursor(declare) => { - return format_declare_cursor(&declare); + return format_declare_cursor(declare); } NameClass::PrepareStatement(prepare) => { - return format_prepare(&prepare); + return format_prepare(prepare); } NameClass::Listen(listen) => { - return format_listen(&listen); + return format_listen(listen); } } } @@ -211,90 +190,61 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option { fn hover_name_ref( db: &dyn Db, - root: &SyntaxNode, + file: File, 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, + context: LocationKind, def_node: &SyntaxNode, ) -> Option { match context { - NameRefClass::CreateIndexColumn - | NameRefClass::InsertColumn - | NameRefClass::DeleteColumn - | NameRefClass::UpdateColumn - | NameRefClass::MergeColumn - | NameRefClass::ConstraintColumn - | NameRefClass::JoinUsingColumn - | NameRefClass::ForeignKeyColumn - | NameRefClass::AlterColumn - | NameRefClass::QualifiedColumn => hover_column(root, name_ref, binder), - NameRefClass::Type => hover_type(root, name_ref, binder), - NameRefClass::CompositeTypeField => hover_composite_type_field(root, name_ref, binder), - NameRefClass::SelectColumn - | NameRefClass::SelectQualifiedColumn - | NameRefClass::PolicyColumn => { - // Try hover as column first - if let Some(result) = hover_column(root, name_ref, binder) { + LocationKind::Aggregate => hover_aggregate(db, file, name_ref), + LocationKind::CaseExpr | LocationKind::CommitBegin | LocationKind::CommitEnd => None, + LocationKind::Channel => hover_channel(db, file, name_ref), + LocationKind::Column => { + if let Some(result) = hover_composite_type_field(db, file, name_ref) { + return Some(result); + } + if let Some(result) = hover_column(db, file, name_ref) { return Some(result); } // If no column, try as function (handles field-style function calls like `t.b`) - if let Some(result) = hover_function(root, name_ref, binder) { + if let Some(result) = hover_function(db, file, name_ref) { return Some(result); } // Finally try as table (handles case like `select t from t;` where t is the table) - hover_table(db, binder, def_node) - } - NameRefClass::DeleteQualifiedColumnTable - | NameRefClass::ForeignKeyTable - | NameRefClass::FromTable - | NameRefClass::InsertQualifiedColumnTable - | NameRefClass::LikeTable - | NameRefClass::MergeQualifiedColumnTable - | NameRefClass::PolicyQualifiedColumnTable - | NameRefClass::SelectQualifiedColumnTable - | NameRefClass::Table - | NameRefClass::UpdateQualifiedColumnTable - | NameRefClass::View => hover_table(db, 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), - NameRefClass::EventTrigger => hover_event_trigger(root, name_ref, binder), - NameRefClass::Database => hover_database(root, name_ref, binder), - NameRefClass::Server => hover_server(root, name_ref, binder), - NameRefClass::Extension => hover_extension(root, name_ref, binder), - NameRefClass::Role => hover_role(root, name_ref, binder), - NameRefClass::Tablespace => hover_tablespace(root, name_ref, binder), - NameRefClass::Index => hover_index(root, name_ref, binder), - NameRefClass::Function | NameRefClass::FunctionCall | NameRefClass::FunctionName => { - hover_function(root, name_ref, binder) + hover_table(db, file, def_node) } - NameRefClass::Aggregate => hover_aggregate(root, name_ref, binder), - NameRefClass::Procedure | NameRefClass::CallProcedure | NameRefClass::ProcedureCall => { - hover_procedure(root, name_ref, binder) - } - NameRefClass::Routine => hover_routine(root, name_ref, binder), - NameRefClass::SelectFunctionCall => { - // Try function first, but fall back to column if no function found - // (handles function-call-style column access like `select a(t)`) - if let Some(result) = hover_function(root, name_ref, binder) { + LocationKind::Cursor => hover_cursor(db, file, name_ref), + LocationKind::Database => hover_database(db, file, name_ref), + LocationKind::EventTrigger => hover_event_trigger(db, file, name_ref), + LocationKind::Extension => hover_extension(db, file, name_ref), + LocationKind::Function => { + if let Some(result) = hover_function(db, file, name_ref) { + return Some(result); + } + if let Some(result) = hover_routine(db, file, name_ref) { return Some(result); } - hover_column(root, name_ref, binder) + hover_column(db, file, name_ref) } - NameRefClass::Schema => hover_schema(root, name_ref, binder), - NameRefClass::NamedArgParameter => hover_named_arg_parameter(root, name_ref, binder), - NameRefClass::Cursor => hover_cursor(root, name_ref, binder), - NameRefClass::PreparedStatement => hover_prepared_statement(root, name_ref, binder), - NameRefClass::Channel => hover_channel(root, name_ref, binder), - NameRefClass::Window => hover_window(root, name_ref, binder), - NameRefClass::PropertyGraph => hover_property_graph(root, name_ref, binder), - NameRefClass::PropertyGraphColumn => hover_column(root, name_ref, binder), + LocationKind::Index => hover_index(db, file, name_ref), + LocationKind::NamedArgParameter => hover_named_arg_parameter(db, file, name_ref), + LocationKind::Policy => hover_policy(db, file, name_ref), + LocationKind::PreparedStatement => hover_prepared_statement(db, file, name_ref), + LocationKind::Procedure => hover_procedure(db, file, name_ref), + LocationKind::PropertyGraph => hover_property_graph(db, file, name_ref), + LocationKind::Role => hover_role(db, file, name_ref), + LocationKind::Schema => hover_schema(db, file, name_ref), + LocationKind::Sequence => hover_sequence(db, file, name_ref), + LocationKind::Server => hover_server(db, file, name_ref), + LocationKind::Table | LocationKind::View => hover_table(db, file, def_node), + LocationKind::Tablespace => hover_tablespace(db, file, name_ref), + LocationKind::Trigger => hover_trigger(db, file, name_ref), + LocationKind::Type => hover_type(db, file, name_ref), + LocationKind::Window => hover_window(db, file, name_ref), } } -struct ColumnHover {} +struct ColumnHover; impl ColumnHover { fn table_column(table_name: &str, column_name: &str) -> String { format!("column {table_name}.{column_name}") @@ -319,30 +269,28 @@ impl ColumnHover { } } -fn hover_column( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let column_ptrs = resolve::resolve_name_ref_ptrs(binder, root, name_ref)?; +fn hover_column(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let column_ptrs = resolve::resolve_name_ref_ptrs(db, file, name_ref)?; let results: Vec = column_ptrs - .iter() + .into_iter() .filter_map(|column_ptr| { - let column_name_node = column_ptr.to_node(root); - format_hover_for_column_node(binder, root, &column_name_node, name_ref) + format_hover_for_column_ptr(db, file, column_ptr, name_ref.clone()) }) .collect(); merge_hovers(results) } -fn format_hover_for_column_node( - binder: &binder::Binder, - root: &SyntaxNode, - column_name_node: &squawk_syntax::SyntaxNode, - name_ref: &ast::NameRef, +fn format_hover_for_column_ptr( + db: &dyn Db, + file: File, + column_ptr: SyntaxNodePtr, + name_ref: ast::NameRef, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let column_name_node = column_ptr.to_node(root); for a in column_name_node.ancestors() { if let Some(with_table) = ast::WithTable::cast(a.clone()) { let cte_name = with_table.name()?; @@ -350,7 +298,7 @@ fn format_hover_for_column_node( .ancestors() .any(|a| ast::Values::can_cast(a.kind())) { - Name::from_node(name_ref) + Name::from_node(&name_ref) } else { Name::from_string(column_name_node.text().to_string()) }; @@ -379,11 +327,11 @@ fn format_hover_for_column_node( .ancestors() .any(|a| ast::Values::can_cast(a.kind())) { - Name::from_node(name_ref) + Name::from_node(&name_ref) } else { Name::from_string(column_name_node.text().to_string()) }; - let ty = resolve::collect_paren_select_columns_with_types(binder, root, &paren_select) + let ty = resolve::collect_paren_select_columns_with_types(db, file, &paren_select) .into_iter() .find(|(name, _)| *name == column_name) .and_then(|(_, ty)| ty)?; @@ -400,7 +348,7 @@ fn format_hover_for_column_node( && let Some(column_name) = ast::Name::cast(column_name_node.clone()).map(|name| Name::from_node(&name)) { - return format_view_column(&create_view, column_name, binder); + return format_view_column(db, file, create_view, column_name); } if let Some(alias) = ast::Alias::cast(a.clone()) @@ -422,7 +370,7 @@ fn format_hover_for_column_node( continue; }; let path = create_table_as.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; return Some(Hover::snippet(ColumnHover::schema_table_column( &schema.to_string(), &table_name, @@ -440,7 +388,7 @@ fn format_hover_for_column_node( .ancestors() .find_map(ast::CreateTableLike::cast)?; let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; let schema = schema.to_string(); let column_name = Name::from_node(&column_name); @@ -453,12 +401,10 @@ fn format_hover_for_column_node( ))) } -fn hover_composite_type_field( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let field_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_composite_type_field(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let field_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let field_name_node = field_ptr.to_node(root); @@ -472,7 +418,7 @@ fn hover_composite_type_field( .ancestors() .find_map(ast::CreateType::cast)?; let type_path = create_type.path()?; - let (schema, type_name) = resolve::resolve_type_info(binder, &type_path)?; + let (schema, type_name) = resolve::resolve_type_info(db, file, &type_path)?; Some(Hover::snippet(format!( "field {}.{}.{} {}", @@ -484,14 +430,15 @@ fn hover_composite_type_field( } fn hover_column_definition( - create_table: &impl ast::HasCreateTable, - column: &ast::Column, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_table: impl ast::HasCreateTable, + column: ast::Column, ) -> Option { let column_name = column.name()?.syntax().text().to_string(); let ty = column.ty()?; let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; let ty = ty.syntax().text().to_string(); Some(Hover::snippet(ColumnHover::schema_table_column_type( &schema.to_string(), @@ -501,50 +448,34 @@ fn hover_column_definition( ))) } -// 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( - db: &dyn Db, - root: &SyntaxNode, - source: resolve::TableSource, - binder: &binder::Binder, -) -> Option { +fn format_table_source(db: &dyn Db, file: File, source: resolve::TableSource) -> Option { match source { - resolve::TableSource::Alias(alias) => { - format_alias_with_column_list(db, root, &alias, binder) - } - resolve::TableSource::WithTable(with_table) => format_with_table(&with_table), - resolve::TableSource::CreateView(create_view) => format_create_view(&create_view, binder), + resolve::TableSource::Alias(alias) => format_alias_with_column_list(db, file, alias), + resolve::TableSource::WithTable(with_table) => format_with_table(with_table), + resolve::TableSource::CreateView(create_view) => format_create_view(db, file, create_view), resolve::TableSource::CreateMaterializedView(create_materialized_view) => { - format_create_materialized_view(&create_materialized_view, binder) + format_create_materialized_view(db, file, create_materialized_view) } resolve::TableSource::CreateTable(create_table) => { - format_create_table(&create_table, binder) + format_create_table(db, file, create_table) } - resolve::TableSource::ParenSelect(paren_select) => format_paren_select(&paren_select), + resolve::TableSource::ParenSelect(paren_select) => format_paren_select(paren_select), } } -fn hover_table(db: &dyn Db, binder: &binder::Binder, def_node: &SyntaxNode) -> Option { +fn hover_table(db: &dyn Db, file: File, 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) - && let Some(root) = def_node.ancestors().last() - { - return format_table_source(db, &root, source, binder); + if let Some(source) = resolve::find_table_source(def_node) { + return format_table_source(db, file, source); } None } -fn format_alias_with_column_list( - db: &dyn Db, - root: &SyntaxNode, - alias: &ast::Alias, - binder: &binder::Binder, -) -> Option { +fn format_alias_with_column_list(db: &dyn Db, file: File, alias: ast::Alias) -> Option { let alias_name = alias.name()?; let name = Name::from_node(&alias_name); @@ -559,9 +490,9 @@ fn format_alias_with_column_list( .collect(); if let Some(from_item) = alias.syntax().ancestors().find_map(ast::FromItem::cast) - && let Some(table_ptr) = resolve::table_ptr_from_from_item(binder, &from_item) + && let Some(table_ptr) = resolve::table_ptr_from_from_item(db, file, &from_item) { - let base_columns = collect_star_column_names(db, root, &table_ptr, binder); + let base_columns = collect_star_column_names(db, file, &table_ptr); for column in base_columns.iter().skip(columns.len()) { columns.push(column.clone()); } @@ -575,49 +506,27 @@ fn format_alias_with_column_list( Some(Hover::snippet(format!("table {}({})", name, columns))) } -fn hover_qualified_star( - db: &dyn Db, - root: &SyntaxNode, - field_expr: &ast::FieldExpr, - binder: &binder::Binder, -) -> Option { - let table_ptr = resolve::resolve_qualified_star_table_ptr(binder, field_expr)?; - hover_qualified_star_columns(db, root, &table_ptr, binder) +fn hover_qualified_star(db: &dyn Db, file: File, field_expr: ast::FieldExpr) -> Option { + let table_ptr = resolve::resolve_qualified_star_table_ptr(db, file, field_expr)?; + hover_qualified_star_columns(db, file, &table_ptr) } -fn hover_unqualified_star( - db: &dyn Db, - root: &SyntaxNode, - target: &ast::Target, - binder: &binder::Binder, -) -> Option { - let mut results = hover_unqualified_star_with_binder(db, root, target, binder); - - if results.is_empty() && target_has_schema_qualified_from_item(target) { - let builtins_tree = parse_builtins(db).tree(); - let builtins_binder = binder::bind(&builtins_tree); - results = hover_unqualified_star_with_binder( - db, - builtins_tree.syntax(), - target, - &builtins_binder, - ); +fn hover_unqualified_star(db: &dyn Db, file: File, target: ast::Target) -> Option { + let mut results = hover_unqualified_star_with_binder(db, file, &target); + + if results.is_empty() && target_has_schema_qualified_from_item(&target) { + results = hover_unqualified_star_with_binder(db, builtins_file(db), &target); } merge_hovers(results) } -fn hover_unqualified_star_with_binder( - db: &dyn Db, - root: &SyntaxNode, - target: &ast::Target, - binder: &binder::Binder, -) -> Vec { +fn hover_unqualified_star_with_binder(db: &dyn Db, file: File, target: &ast::Target) -> Vec { let mut results = vec![]; - if let Some(table_ptrs) = resolve::resolve_unqualified_star_table_ptrs(binder, target) { + if let Some(table_ptrs) = resolve::resolve_unqualified_star_table_ptrs(db, file, target) { for table_ptr in table_ptrs { - if let Some(columns) = hover_qualified_star_columns(db, root, &table_ptr, binder) { + if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { results.push(columns); } } @@ -645,14 +554,13 @@ fn target_has_schema_qualified_from_item(target: &ast::Target) -> bool { fn hover_unqualified_star_in_arg_list( db: &dyn Db, - root: &SyntaxNode, - arg_list: &ast::ArgList, - binder: &binder::Binder, + file: File, + arg_list: ast::ArgList, ) -> Option { - let table_ptrs = resolve::resolve_unqualified_star_in_arg_list_ptrs(binder, arg_list)?; + let table_ptrs = resolve::resolve_unqualified_star_in_arg_list_ptrs(db, file, &arg_list)?; let mut results = vec![]; for table_ptr in table_ptrs { - if let Some(columns) = hover_qualified_star_columns(db, root, &table_ptr, binder) { + if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { results.push(columns); } } @@ -668,10 +576,10 @@ fn hover_subquery_table(def_node: &SyntaxNode) -> Option { 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, &paren_select) + format_subquery_table(name, paren_select) } -fn format_subquery_table(name: &Name, paren_select: &ast::ParenSelect) -> Option { +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(Hover::snippet(format!("subquery {} as {}", name, query))) @@ -679,43 +587,43 @@ fn format_subquery_table(name: &Name, paren_select: &ast::ParenSelect) -> Option fn hover_qualified_star_columns( db: &dyn Db, - root: &SyntaxNode, + file: File, table_ptr: &squawk_syntax::SyntaxNodePtr, - binder: &binder::Binder, ) -> Option { + let source_file = parse(db, file).tree(); + let root = source_file.syntax(); let table_name_node = table_ptr.to_node(root); if let Some(paren_select) = ast::ParenSelect::cast(table_name_node.clone()) { - return hover_qualified_star_columns_from_subquery(db, root, &paren_select, binder); + return hover_qualified_star_columns_from_subquery(db, file, &paren_select); } match resolve::find_table_source(&table_name_node)? { resolve::TableSource::Alias(alias) => { - hover_qualified_star_columns_from_alias(db, root, &alias, binder) + hover_qualified_star_columns_from_alias(db, file, &alias) } resolve::TableSource::WithTable(with_table) => { - hover_qualified_star_columns_from_cte(root, &with_table, binder) + hover_qualified_star_columns_from_cte(db, file, with_table) } resolve::TableSource::CreateTable(create_table) => { - hover_qualified_star_columns_from_table(root, &create_table, binder) + hover_qualified_star_columns_from_table(db, file, create_table) } resolve::TableSource::CreateView(create_view) => { - hover_qualified_star_columns_from_view(&create_view, binder) + hover_qualified_star_columns_from_view(db, file, &create_view) } resolve::TableSource::CreateMaterializedView(create_materialized_view) => { - hover_qualified_star_columns_from_materialized_view(&create_materialized_view, binder) + hover_qualified_star_columns_from_materialized_view(db, file, create_materialized_view) } resolve::TableSource::ParenSelect(paren_select) => { - hover_qualified_star_columns_from_subquery(db, root, &paren_select, binder) + hover_qualified_star_columns_from_subquery(db, file, &paren_select) } } } fn hover_qualified_star_columns_from_alias( db: &dyn Db, - root: &SyntaxNode, + file: File, alias: &ast::Alias, - binder: &binder::Binder, ) -> Option { let alias_name = Name::from_node(&alias.name()?); let alias_columns: Vec = alias @@ -739,8 +647,8 @@ fn hover_qualified_star_columns_from_alias( .collect(); let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?; - let table_ptr = resolve::table_ptr_from_from_item(binder, &from_item)?; - let base_column_names = collect_star_column_names(db, root, &table_ptr, binder); + let table_ptr = resolve::table_ptr_from_from_item(db, file, &from_item)?; + let base_column_names = collect_star_column_names(db, file, &table_ptr); for column_name in base_column_names.iter().skip(alias_columns.len()) { results.push(Hover::snippet(ColumnHover::table_column( @@ -754,22 +662,23 @@ fn hover_qualified_star_columns_from_alias( fn collect_star_column_names( db: &dyn Db, - root: &SyntaxNode, + file: File, table_ptr: &squawk_syntax::SyntaxNodePtr, - binder: &binder::Binder, ) -> Vec { + let source_file = parse(db, file).tree(); + let root = source_file.syntax(); let table_name_node = table_ptr.to_node(root); if let Some(paren_select) = ast::ParenSelect::cast(table_name_node.clone()) { let columns: Vec = - resolve::collect_paren_select_columns_with_types(binder, root, &paren_select) + resolve::collect_paren_select_columns_with_types(db, file, &paren_select) .into_iter() .map(|(name, _ty)| name) .collect(); if !columns.is_empty() { return columns; } - return collect_star_column_names_from_paren_select(db, root, &paren_select, binder); + return collect_star_column_names_from_paren_select(db, file, &paren_select); } match resolve::find_table_source(&table_name_node) { @@ -780,21 +689,15 @@ fn collect_star_column_names( .filter_map(|column| column.name().map(|name| Name::from_node(&name))) .collect(), Some(resolve::TableSource::WithTable(with_table)) => { - let columns = resolve::collect_with_table_column_names(binder, root, &with_table); + let columns = resolve::collect_with_table_column_names(db, file, with_table.clone()); if !columns.is_empty() { return columns; } - let builtins_tree = parse_builtins(db).tree(); - let builtins_binder = binder::bind(&builtins_tree); - resolve::collect_with_table_column_names( - &builtins_binder, - builtins_tree.syntax(), - &with_table, - ) + resolve::collect_with_table_column_names(db, builtins_file(db), with_table) } Some(resolve::TableSource::CreateTable(create_table)) => { - resolve::collect_table_columns(binder, root, &create_table) + resolve::collect_table_columns(db, file, &create_table) .into_iter() .filter_map(|column| column.name().map(|name| Name::from_node(&name))) .collect() @@ -803,10 +706,10 @@ fn collect_star_column_names( resolve::collect_view_column_names(&create_view) } Some(resolve::TableSource::CreateMaterializedView(create_materialized_view)) => { - resolve::collect_materialized_view_column_names(&create_materialized_view) + resolve::collect_materialized_view_column_names(create_materialized_view) } Some(resolve::TableSource::ParenSelect(paren_select)) => { - resolve::collect_paren_select_columns_with_types(binder, root, &paren_select) + resolve::collect_paren_select_columns_with_types(db, file, &paren_select) .into_iter() .map(|(name, _ty)| name) .collect() @@ -817,9 +720,8 @@ fn collect_star_column_names( fn collect_star_column_names_from_paren_select( db: &dyn Db, - root: &SyntaxNode, + file: File, paren_select: &ast::ParenSelect, - binder: &binder::Binder, ) -> Vec { let Some(select_variant) = paren_select.select() else { return vec![]; @@ -832,22 +734,22 @@ fn collect_star_column_names_from_paren_select( }; let mut columns = vec![]; for from_item in from_clause.from_items() { - if let Some(table_ptr) = resolve::table_ptr_from_from_item(binder, &from_item) { - columns.extend(collect_star_column_names(db, root, &table_ptr, binder)); + if let Some(table_ptr) = resolve::table_ptr_from_from_item(db, file, &from_item) { + columns.extend(collect_star_column_names(db, file, &table_ptr)); } } columns } fn hover_qualified_star_columns_from_table( - root: &SyntaxNode, - create_table: &impl ast::HasCreateTable, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_table: impl ast::HasCreateTable, ) -> Option { let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; let schema = schema.to_string(); - let results: Vec = resolve::collect_table_columns(binder, root, create_table) + let results: Vec = resolve::collect_table_columns(db, file, &create_table) .into_iter() .filter_map(|column| { let column_name = Name::from_node(&column.name()?); @@ -866,12 +768,12 @@ fn hover_qualified_star_columns_from_table( } fn hover_qualified_star_columns_from_cte( - root: &SyntaxNode, - with_table: &ast::WithTable, - binder: &binder::Binder, + db: &dyn Db, + file: File, + with_table: ast::WithTable, ) -> Option { let cte_name = Name::from_node(&with_table.name()?); - let column_names = resolve::collect_with_table_column_names(binder, root, with_table); + let column_names = resolve::collect_with_table_column_names(db, file, with_table); let results: Vec = column_names .iter() .map(|column_name| { @@ -886,11 +788,12 @@ fn hover_qualified_star_columns_from_cte( } fn hover_qualified_star_columns_from_view( + db: &dyn Db, + file: File, create_view: &ast::CreateView, - binder: &binder::Binder, ) -> Option { let path = create_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; let schema_str = schema.to_string(); let column_names = resolve::collect_view_column_names(create_view); @@ -909,11 +812,12 @@ fn hover_qualified_star_columns_from_view( } fn hover_qualified_star_columns_from_materialized_view( - create_materialized_view: &ast::CreateMaterializedView, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_materialized_view: ast::CreateMaterializedView, ) -> Option { let path = create_materialized_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; let schema_str = schema.to_string(); let column_names = resolve::collect_materialized_view_column_names(create_materialized_view); @@ -933,9 +837,8 @@ fn hover_qualified_star_columns_from_materialized_view( fn hover_qualified_star_columns_from_subquery( db: &dyn Db, - root: &SyntaxNode, + file: File, paren_select: &ast::ParenSelect, - binder: &binder::Binder, ) -> Option { let select_variant = paren_select.select()?; @@ -948,11 +851,9 @@ fn hover_qualified_star_columns_from_subquery( for target in target_list.targets() { if target.star_token().is_some() { - let table_ptrs = resolve::resolve_unqualified_star_table_ptrs(binder, &target)?; + let table_ptrs = resolve::resolve_unqualified_star_table_ptrs(db, file, &target)?; for table_ptr in table_ptrs { - if let Some(columns) = - hover_qualified_star_columns(db, root, &table_ptr, binder) - { + if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { results.push(columns) } } @@ -960,7 +861,7 @@ fn hover_qualified_star_columns_from_subquery( } if let Some(result) = - hover_subquery_target_column(root, &target, subquery_alias.as_ref(), binder) + hover_subquery_target_column(db, file, &target, subquery_alias.as_ref()) { results.push(result); } @@ -971,7 +872,7 @@ fn hover_qualified_star_columns_from_subquery( let subquery_alias = subquery_alias_name(paren_select); let results: Vec = - resolve::collect_paren_select_columns_with_types(binder, root, paren_select) + resolve::collect_paren_select_columns_with_types(db, file, paren_select) .into_iter() .map(|(column_name, ty)| { if let Some(alias) = &subquery_alias { @@ -1003,10 +904,10 @@ fn subquery_alias_name(paren_select: &ast::ParenSelect) -> Option { } fn hover_subquery_target_column( - root: &SyntaxNode, + db: &dyn Db, + file: File, target: &ast::Target, subquery_alias: Option<&Name>, - binder: &binder::Binder, ) -> Option { if let Some(alias) = subquery_alias && let Some((col_name, _node)) = ColumnName::from_target(target.clone()) @@ -1019,10 +920,10 @@ fn hover_subquery_target_column( } let result = match target.expr()? { - ast::Expr::NameRef(name_ref) => hover_column(root, &name_ref, binder), + ast::Expr::NameRef(name_ref) => hover_column(db, file, &name_ref), ast::Expr::FieldExpr(field_expr) => { let field = field_expr.field()?; - hover_column(root, &field, binder) + hover_column(db, file, &field) } _ => None, }; @@ -1040,30 +941,26 @@ fn hover_subquery_target_column( None } -fn hover_index( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let index_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_index(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let index_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; + let source_file = parse(db, file).tree(); + let root = source_file.syntax(); let index_name_node = index_ptr.to_node(root); let create_index = index_name_node .ancestors() .find_map(ast::CreateIndex::cast)?; - format_create_index(&create_index, binder) + format_create_index(db, file, create_index) } -fn hover_sequence( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let sequence_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_sequence(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let sequence_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1073,15 +970,13 @@ fn hover_sequence( .ancestors() .find_map(ast::CreateSequence::cast)?; - format_create_sequence(&create_sequence, binder) + format_create_sequence(db, file, create_sequence) } -fn hover_trigger( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let trigger_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_trigger(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let trigger_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1091,15 +986,13 @@ fn hover_trigger( .ancestors() .find_map(ast::CreateTrigger::cast)?; - format_create_trigger(&create_trigger, binder) + format_create_trigger(db, file, create_trigger) } -fn hover_policy( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let policy_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_policy(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let policy_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1109,15 +1002,13 @@ fn hover_policy( .ancestors() .find_map(ast::CreatePolicy::cast)?; - format_create_policy(&create_policy, binder) + format_create_policy(db, file, create_policy) } -fn hover_property_graph( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let property_graph_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_property_graph(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let property_graph_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1127,15 +1018,13 @@ fn hover_property_graph( .ancestors() .find_map(ast::CreatePropertyGraph::cast)?; - format_create_property_graph(&create_property_graph, binder) + format_create_property_graph(db, file, create_property_graph) } -fn hover_event_trigger( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let event_trigger_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_event_trigger(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let event_trigger_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1145,15 +1034,13 @@ fn hover_event_trigger( .ancestors() .find_map(ast::CreateEventTrigger::cast)?; - format_create_event_trigger(&create_event_trigger) + format_create_event_trigger(create_event_trigger) } -fn hover_tablespace( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let tablespace_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_tablespace(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let tablespace_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let tablespace_name_node = tablespace_ptr.to_node(root); @@ -1163,12 +1050,10 @@ fn hover_tablespace( ))) } -fn hover_database( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let database_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_database(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let database_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let database_name_node = database_ptr.to_node(root); @@ -1178,12 +1063,10 @@ fn hover_database( ))) } -fn hover_server( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let server_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_server(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let server_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let server_name_node = server_ptr.to_node(root); @@ -1193,12 +1076,10 @@ fn hover_server( ))) } -fn hover_extension( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let extension_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_extension(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let extension_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let extension_name_node = extension_ptr.to_node(root); @@ -1208,65 +1089,55 @@ fn hover_extension( ))) } -fn hover_role( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let role_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_role(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let role_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let role_name_node = role_ptr.to_node(root); Some(Hover::snippet(format!("role {}", role_name_node.text()))) } -fn hover_cursor( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let cursor_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_cursor(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let cursor_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let cursor_name_node = cursor_ptr.to_node(root); let declare = cursor_name_node.ancestors().find_map(ast::Declare::cast)?; - format_declare_cursor(&declare) + format_declare_cursor(declare) } -fn hover_prepared_statement( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let statement_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_prepared_statement(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let statement_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let statement_name_node = statement_ptr.to_node(root); let prepare = statement_name_node .ancestors() .find_map(ast::Prepare::cast)?; - format_prepare(&prepare) + format_prepare(prepare) } -fn hover_channel( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let channel_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_channel(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let channel_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let channel_name_node = channel_ptr.to_node(root); let listen = channel_name_node.ancestors().find_map(ast::Listen::cast)?; - format_listen(&listen) + format_listen(listen) } -fn hover_window( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let window_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_window(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let window_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let window_name_node = window_ptr.to_node(root); @@ -1280,12 +1151,10 @@ fn hover_window( ))) } -fn hover_type( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let type_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_type(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let type_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1293,10 +1162,10 @@ fn hover_type( let create_type = type_name_node.ancestors().find_map(ast::CreateType::cast)?; - format_create_type(&create_type, binder) + format_create_type(db, file, create_type) } -fn format_declare_cursor(declare: &ast::Declare) -> Option { +fn format_declare_cursor(declare: ast::Declare) -> Option { let name = declare.name()?; let query = declare.query()?; Some(Hover::snippet(format!( @@ -1306,7 +1175,7 @@ fn format_declare_cursor(declare: &ast::Declare) -> Option { ))) } -fn format_prepare(prepare: &ast::Prepare) -> Option { +fn format_prepare(prepare: ast::Prepare) -> Option { let name = prepare.name()?; let stmt = prepare.preparable_stmt()?; Some(Hover::snippet(format!( @@ -1316,17 +1185,18 @@ fn format_prepare(prepare: &ast::Prepare) -> Option { ))) } -fn format_listen(listen: &ast::Listen) -> Option { +fn format_listen(listen: ast::Listen) -> Option { let name = listen.name()?; Some(Hover::snippet(format!("listen {}", name.syntax().text()))) } fn format_create_table( - create_table: &impl ast::HasCreateTable, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_table: impl ast::HasCreateTable, ) -> Option { let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; let schema = schema.to_string(); let args = create_table.table_arg_list()?.syntax().text().to_string(); @@ -1341,11 +1211,11 @@ fn format_create_table( ))) } -fn format_create_view(create_view: &ast::CreateView, binder: &binder::Binder) -> Option { +fn format_create_view(db: &dyn Db, file: File, create_view: ast::CreateView) -> 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, view_name) = resolve::resolve_view_info(db, file, &path)?; let schema = schema.to_string(); let column_list = create_view @@ -1362,11 +1232,12 @@ fn format_create_view(create_view: &ast::CreateView, binder: &binder::Binder) -> } fn format_create_materialized_view( - create_materialized_view: &ast::CreateMaterializedView, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_materialized_view: ast::CreateMaterializedView, ) -> Option { let path = create_materialized_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; let schema = schema.to_string(); let column_list = create_materialized_view @@ -1387,12 +1258,13 @@ fn format_create_materialized_view( } fn format_view_column( - create_view: &ast::CreateView, + db: &dyn Db, + file: File, + create_view: ast::CreateView, column_name: Name, - binder: &binder::Binder, ) -> Option { let path = create_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(binder, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; Some(Hover::snippet(ColumnHover::schema_table_column( &schema.to_string(), &view_name, @@ -1400,25 +1272,25 @@ fn format_view_column( ))) } -fn format_with_table(with_table: &ast::WithTable) -> Option { +fn format_with_table(with_table: ast::WithTable) -> Option { let name = with_table.name()?.syntax().text().to_string(); let query = with_table.query()?.syntax().text().to_string(); Some(Hover::snippet(format!("with {} as ({})", name, query))) } -fn format_paren_select(paren_select: &ast::ParenSelect) -> Option { +fn format_paren_select(paren_select: ast::ParenSelect) -> Option { let query = paren_select.select()?.syntax().text().to_string(); Some(Hover::snippet(format!("({})", query))) } -fn format_create_index(create_index: &ast::CreateIndex, binder: &binder::Binder) -> Option { +fn format_create_index(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option { let index_name = create_index.name()?.syntax().text().to_string(); - let index_schema = index_schema(create_index, binder)?; + let index_schema = index_schema(db, file, create_index.clone())?; let relation_name = create_index.relation_name()?; let path = relation_name.path()?; - let (table_schema, table_name) = resolve::resolve_table_info(binder, &path)?; + let (table_schema, table_name) = resolve::resolve_table_info(db, file, &path)?; let partition_item_list = create_index.partition_item_list()?; let columns = partition_item_list.syntax().text().to_string(); @@ -1430,11 +1302,12 @@ fn format_create_index(create_index: &ast::CreateIndex, binder: &binder::Binder) } fn format_create_sequence( - create_sequence: &ast::CreateSequence, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_sequence: ast::CreateSequence, ) -> Option { let path = create_sequence.path()?; - let (schema, sequence_name) = resolve::resolve_sequence_info(binder, &path)?; + let (schema, sequence_name) = resolve::resolve_sequence_info(db, file, &path)?; Some(Hover::snippet(format!( "sequence {}.{}", @@ -1443,13 +1316,14 @@ fn format_create_sequence( } fn format_create_trigger( - create_trigger: &ast::CreateTrigger, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_trigger: ast::CreateTrigger, ) -> Option { let trigger_name = create_trigger.name()?.syntax().text().to_string(); let on_table_path = create_trigger.on_table()?.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &on_table_path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &on_table_path)?; Some(Hover::snippet(format!( "trigger {}.{} on {}.{}", schema, trigger_name, schema, table_name @@ -1457,13 +1331,14 @@ fn format_create_trigger( } fn format_create_policy( - create_policy: &ast::CreatePolicy, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_policy: ast::CreatePolicy, ) -> Option { let policy_name = create_policy.name()?.syntax().text().to_string(); let on_table_path = create_policy.on_table()?.path()?; - let (schema, table_name) = resolve::resolve_table_info(binder, &on_table_path)?; + let (schema, table_name) = resolve::resolve_table_info(db, file, &on_table_path)?; Some(Hover::snippet(format!( "policy {}.{} on {}.{}", schema, policy_name, schema, table_name @@ -1471,56 +1346,58 @@ fn format_create_policy( } fn format_create_property_graph( - create_property_graph: &ast::CreatePropertyGraph, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_property_graph: ast::CreatePropertyGraph, ) -> Option { let path = create_property_graph.path()?; - let (schema, name) = resolve::resolve_property_graph_info(binder, &path)?; + let (schema, name) = resolve::resolve_property_graph_info(db, file, &path)?; Some(Hover::snippet(format!( "property graph {}.{}", schema, name ))) } -fn format_create_event_trigger(create_event_trigger: &ast::CreateEventTrigger) -> Option { +fn format_create_event_trigger(create_event_trigger: ast::CreateEventTrigger) -> Option { let name = create_event_trigger.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("event trigger {}", name))) } -fn format_create_tablespace(create_tablespace: &ast::CreateTablespace) -> Option { +fn format_create_tablespace(create_tablespace: ast::CreateTablespace) -> Option { let name = create_tablespace.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("tablespace {}", name))) } -fn format_create_database(create_database: &ast::CreateDatabase) -> Option { +fn format_create_database(create_database: ast::CreateDatabase) -> Option { let name = create_database.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("database {}", name))) } -fn format_create_server(create_server: &ast::CreateServer) -> Option { +fn format_create_server(create_server: ast::CreateServer) -> Option { let name = create_server.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("server {}", name))) } -fn format_create_extension(create_extension: &ast::CreateExtension) -> Option { +fn format_create_extension(create_extension: ast::CreateExtension) -> Option { let name = create_extension.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("extension {}", name))) } -fn format_create_role(create_role: &ast::CreateRole) -> Option { +fn format_create_role(create_role: ast::CreateRole) -> Option { let name = create_role.name()?.syntax().text().to_string(); Some(Hover::snippet(format!("role {}", name))) } -fn index_schema(create_index: &ast::CreateIndex, binder: &binder::Binder) -> Option { +fn index_schema(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option { + let binder = bind(db, file); let position = create_index.syntax().text_range().start(); let search_path = binder.search_path_at(position); search_path.first().map(|s| s.to_string()) } -fn format_create_type(create_type: &ast::CreateType, binder: &binder::Binder) -> Option { +fn format_create_type(db: &dyn Db, file: File, create_type: ast::CreateType) -> Option { let path = create_type.path()?; - let (schema, type_name) = resolve::resolve_type_info(binder, &path)?; + let (schema, type_name) = resolve::resolve_type_info(db, file, &path)?; if let Some(variant_list) = create_type.variant_list() { let variants = variant_list.syntax().text().to_string(); @@ -1549,12 +1426,10 @@ fn format_create_type(create_type: &ast::CreateType, binder: &binder::Binder) -> Some(Hover::snippet(format!("type {}.{}", schema, type_name))) } -fn hover_schema( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let schema_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_schema(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let schema_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1564,10 +1439,10 @@ fn hover_schema( .ancestors() .find_map(ast::CreateSchema::cast)?; - format_create_schema(&create_schema) + format_create_schema(create_schema) } -fn create_schema_name(create_schema: &ast::CreateSchema) -> Option { +fn create_schema_name(create_schema: ast::CreateSchema) -> Option { if let Some(schema_name) = create_schema.name() { return Some(schema_name.syntax().text().to_string()); } @@ -1578,17 +1453,15 @@ fn create_schema_name(create_schema: &ast::CreateSchema) -> Option { .map(|n| n.syntax().text().to_string()) } -fn format_create_schema(create_schema: &ast::CreateSchema) -> Option { +fn format_create_schema(create_schema: ast::CreateSchema) -> Option { let schema_name = create_schema_name(create_schema)?; Some(Hover::snippet(format!("schema {}", schema_name))) } -fn hover_function( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let function_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_function(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let function_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1598,15 +1471,13 @@ fn hover_function( .ancestors() .find_map(ast::CreateFunction::cast)?; - format_create_function(&create_function, binder) + format_create_function(db, file, create_function) } -fn hover_named_arg_parameter( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let param_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_named_arg_parameter(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let param_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let param_name_node = param_ptr.to_node(root); @@ -1617,7 +1488,7 @@ fn hover_named_arg_parameter( for ancestor in param_name_node.ancestors() { if let Some(create_function) = ast::CreateFunction::cast(ancestor.clone()) { let path = create_function.path()?; - let (schema, function_name) = resolve::resolve_function_info(binder, &path)?; + let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?; return Some(format_param_hover( schema, function_name, @@ -1627,7 +1498,7 @@ fn hover_named_arg_parameter( } if let Some(create_procedure) = ast::CreateProcedure::cast(ancestor.clone()) { let path = create_procedure.path()?; - let (schema, procedure_name) = resolve::resolve_procedure_info(binder, &path)?; + let (schema, procedure_name) = resolve::resolve_procedure_info(db, file, &path)?; return Some(format_param_hover( schema, procedure_name, @@ -1637,7 +1508,7 @@ fn hover_named_arg_parameter( } if let Some(create_aggregate) = ast::CreateAggregate::cast(ancestor) { let path = create_aggregate.path()?; - let (schema, aggregate_name) = resolve::resolve_aggregate_info(binder, &path)?; + let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, file, &path)?; return Some(format_param_hover( schema, aggregate_name, @@ -1670,11 +1541,12 @@ fn format_param_hover( } fn format_create_function( - create_function: &ast::CreateFunction, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_function: ast::CreateFunction, ) -> Option { let path = create_function.path()?; - let (schema, function_name) = resolve::resolve_function_info(binder, &path)?; + let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?; let params = create_function.param_list()?.syntax().text().to_string(); let return_type = create_function.ret_type()?.syntax().text().to_string(); @@ -1690,12 +1562,10 @@ fn format_create_function( Some(Hover::snippet(snippet)) } -fn hover_aggregate( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let aggregate_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_aggregate(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let aggregate_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1705,15 +1575,16 @@ fn hover_aggregate( .ancestors() .find_map(ast::CreateAggregate::cast)?; - format_create_aggregate(&create_aggregate, binder) + format_create_aggregate(db, file, create_aggregate) } fn format_create_aggregate( - create_aggregate: &ast::CreateAggregate, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_aggregate: ast::CreateAggregate, ) -> Option { let path = create_aggregate.path()?; - let (schema, aggregate_name) = resolve::resolve_aggregate_info(binder, &path)?; + let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, file, &path)?; let param_list = create_aggregate.param_list()?; let params = param_list.syntax().text().to_string(); @@ -1724,12 +1595,10 @@ fn format_create_aggregate( ))) } -fn hover_procedure( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let procedure_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_procedure(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let procedure_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; @@ -1739,15 +1608,16 @@ fn hover_procedure( .ancestors() .find_map(ast::CreateProcedure::cast)?; - format_create_procedure(&create_procedure, binder) + format_create_procedure(db, file, create_procedure) } fn format_create_procedure( - create_procedure: &ast::CreateProcedure, - binder: &binder::Binder, + db: &dyn Db, + file: File, + create_procedure: ast::CreateProcedure, ) -> Option { let path = create_procedure.path()?; - let (schema, procedure_name) = resolve::resolve_procedure_info(binder, &path)?; + let (schema, procedure_name) = resolve::resolve_procedure_info(db, file, &path)?; let param_list = create_procedure.param_list()?; let params = param_list.syntax().text().to_string(); @@ -1758,25 +1628,23 @@ fn format_create_procedure( ))) } -fn hover_routine( - root: &SyntaxNode, - name_ref: &ast::NameRef, - binder: &binder::Binder, -) -> Option { - let routine_ptr = resolve::resolve_name_ref_ptrs(binder, root, name_ref)? +fn hover_routine(db: &dyn Db, file: File, name_ref: &ast::NameRef) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + let routine_ptr = resolve::resolve_name_ref_ptrs(db, file, name_ref)? .into_iter() .next()?; let routine_name = routine_ptr.to_node(root); for a in routine_name.ancestors() { if let Some(create_function) = ast::CreateFunction::cast(a.clone()) { - return format_create_function(&create_function, binder); + return format_create_function(db, file, create_function); } if let Some(create_aggregate) = ast::CreateAggregate::cast(a.clone()) { - return format_create_aggregate(&create_aggregate, binder); + return format_create_aggregate(db, file, create_aggregate); } if let Some(create_procedure) = ast::CreateProcedure::cast(a) { - return format_create_procedure(&create_procedure, binder); + return format_create_procedure(db, file, create_procedure); } } diff --git a/crates/squawk_ide/src/inlay_hints.rs b/crates/squawk_ide/src/inlay_hints.rs index da604534..75483c52 100644 --- a/crates/squawk_ide/src/inlay_hints.rs +++ b/crates/squawk_ide/src/inlay_hints.rs @@ -1,9 +1,7 @@ -use crate::builtins::parse_builtins; use crate::db::{File, parse}; -use crate::goto_definition::FileId; +use crate::goto_definition; use crate::resolve; use crate::symbols::Name; -use crate::{binder, goto_definition}; use rowan::{TextRange, TextSize}; use salsa::Database as Db; use squawk_syntax::ast::{self, AstNode}; @@ -15,7 +13,7 @@ pub enum InlayHintKind { Parameter, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct InlayHint { pub position: TextSize, pub label: String, @@ -25,20 +23,17 @@ pub struct InlayHint { // For example: `insert into t(a, b) values (1, 2)` pub target: Option, // TODO: combine with the target range above - pub file: Option, + pub file: Option, } #[salsa::tracked] pub fn inlay_hints(db: &dyn Db, file: File) -> Vec { - let parse = parse(db, file); - let source_file = parse.tree(); - let mut hints = vec![]; - for node in source_file.syntax().descendants() { + for node in parse(db, file).tree().syntax().descendants() { if let Some(call_expr) = ast::CallExpr::cast(node.clone()) { - inlay_hint_call_expr(db, &mut hints, file, &source_file, call_expr); + inlay_hint_call_expr(db, &mut hints, file, call_expr); } else if let Some(insert) = ast::Insert::cast(node) { - inlay_hint_insert(db, &mut hints, file, &source_file, insert); + inlay_hint_insert(db, &mut hints, file, insert); } } hints @@ -48,7 +43,6 @@ fn inlay_hint_call_expr( db: &dyn Db, hints: &mut Vec, file_id: File, - file: &ast::SourceFile, call_expr: ast::CallExpr, ) -> Option<()> { let arg_list = call_expr.arg_list()?; @@ -65,12 +59,9 @@ fn inlay_hint_call_expr( .into_iter() .next()?; - let file = match location.file { - goto_definition::FileId::Current => file, - goto_definition::FileId::Builtins => &parse_builtins(db).tree(), - }; + let def_file = parse(db, location.file).tree(); - let function_name_node = file.syntax().covering_element(location.range); + let function_name_node = def_file.syntax().covering_element(location.range); if let Some(create_function) = function_name_node .ancestors() @@ -99,7 +90,6 @@ fn inlay_hint_insert( db: &dyn Db, hints: &mut Vec, file_id: File, - file: &ast::SourceFile, insert: ast::Insert, ) -> Option<()> { let name_start = insert @@ -115,24 +105,16 @@ fn inlay_hint_insert( .into_iter() .next(); - let file = match location.as_ref().map(|x| x.file) { - Some(goto_definition::FileId::Current) | None => file, - Some(goto_definition::FileId::Builtins) => &parse_builtins(db).tree(), - }; - - let create_table = { - let range = location.as_ref().map(|x| x.range); - - range.and_then(|range| { - file.syntax() - .covering_element(range) - .ancestors() - .find_map(ast::CreateTableLike::cast) - }) - }; + let def_file = location.as_ref().map(|loc| loc.file).unwrap_or(file_id); + let def_tree = parse(db, def_file).tree(); - // TODO: we should salsa this - let binder = binder::bind(file); + let create_table = location.as_ref().and_then(|loc| { + def_tree + .syntax() + .covering_element(loc.range) + .ancestors() + .find_map(ast::CreateTableLike::cast) + }); let columns = if let Some(column_list) = insert.column_list() { // `insert into t(a, b, c) values (1, 2, 3)` @@ -142,19 +124,17 @@ fn inlay_hint_insert( let col_name = resolve::extract_column_name(&col)?; let target = create_table .as_ref() - .and_then(|x| { - resolve::find_column_in_create_table(&binder, file.syntax(), x, &col_name) - }) + .and_then(|x| resolve::find_column_in_create_table(db, def_file, x, &col_name)) .map(|x| x.text_range()); Some((col_name, target, location.as_ref().map(|x| x.file))) }) .collect() } else { // `insert into t values (1, 2, 3)` - resolve::collect_columns_from_create_table(&binder, file.syntax(), &create_table?) + resolve::collect_columns_from_create_table(db, def_file, &create_table?) .into_iter() .map(|(col_name, ptr)| { - let target = ptr.map(|p| p.to_node(file.syntax()).text_range()); + let target = ptr.map(|p| p.text_range()); (col_name, target, location.as_ref().map(|x| x.file)) }) .collect() @@ -183,7 +163,7 @@ fn inlay_hint_insert( fn inlay_hint_insert_select( hints: &mut Vec, - columns: Vec<(Name, Option, Option)>, + columns: Vec<(Name, Option, Option)>, stmt: ast::Stmt, ) -> Option<()> { let target_list = match stmt { diff --git a/crates/squawk_ide/src/lib.rs b/crates/squawk_ide/src/lib.rs index e95fa437..b2296366 100644 --- a/crates/squawk_ide/src/lib.rs +++ b/crates/squawk_ide/src/lib.rs @@ -15,6 +15,7 @@ pub mod goto_definition; pub mod hover; mod infer; pub mod inlay_hints; +pub mod location; mod offsets; mod quote; mod resolve; diff --git a/crates/squawk_ide/src/location.rs b/crates/squawk_ide/src/location.rs new file mode 100644 index 00000000..9a56187e --- /dev/null +++ b/crates/squawk_ide/src/location.rs @@ -0,0 +1,54 @@ +use rowan::TextRange; +use squawk_syntax::SyntaxNode; + +use crate::{classify::classify_def_node, db::File}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LocationKind { + Aggregate, + CaseExpr, + Channel, + Column, + CommitBegin, + CommitEnd, + Cursor, + Database, + EventTrigger, + Extension, + Function, + Index, + NamedArgParameter, + Policy, + PreparedStatement, + Procedure, + PropertyGraph, + Role, + Schema, + Sequence, + Server, + Table, + Tablespace, + Trigger, + Type, + View, + Window, +} + +impl LocationKind { + pub(crate) fn from_node(node: &SyntaxNode) -> Option { + classify_def_node(node) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Location { + pub file: File, + pub range: TextRange, + pub kind: LocationKind, +} + +impl Location { + pub(crate) fn current(file: File, range: TextRange, kind: LocationKind) -> Location { + Location { file, range, kind } + } +} diff --git a/crates/squawk_ide/src/offsets.rs b/crates/squawk_ide/src/offsets.rs index 129c7ba2..14722c0f 100644 --- a/crates/squawk_ide/src/offsets.rs +++ b/crates/squawk_ide/src/offsets.rs @@ -1,11 +1,14 @@ +use crate::db::{File, parse}; use rowan::TextSize; -use squawk_syntax::{ - SyntaxKind, SyntaxToken, - ast::{self, AstNode}, -}; +use salsa::Database as Db; +use squawk_syntax::{SyntaxKind, SyntaxToken, ast::AstNode}; -pub(crate) fn token_from_offset(file: &ast::SourceFile, offset: TextSize) -> Option { - let mut token = file.syntax().token_at_offset(offset).right_biased()?; +pub(crate) fn token_from_offset(db: &dyn Db, file: File, offset: TextSize) -> Option { + let mut token = parse(db, file) + .tree() + .syntax() + .token_at_offset(offset) + .right_biased()?; // want to be lenient in case someone clicks: // - the trailing `;` of a line // - the `,` in a target list, like `select a, b, c` @@ -26,5 +29,5 @@ pub(crate) fn token_from_offset(file: &ast::SourceFile, offset: TextSize) -> Opt ) { token = token.prev_token()?; } - return Some(token); + Some(token) } diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index 62281669..45b76bd7 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -5,13 +5,17 @@ use squawk_syntax::{ ast::{self, AstNode, SelectVariant}, }; -use crate::binder::Binder; -use crate::classify::{NameRefClass, classify_def_node, classify_name_ref}; use crate::column_name::ColumnName; -use crate::goto_definition::LocationKind; +use crate::db::File; use crate::infer::{Type, infer_type_from_expr, infer_type_from_ty}; +use crate::location::LocationKind; pub(crate) use crate::symbols::Schema; use crate::symbols::{Name, SymbolKind}; +use crate::{ + classify::{NameRefClass, classify_name_ref}, + db::{bind, parse}, +}; +use salsa::Database as Db; /// Resolves a name reference to its definition(s). /// @@ -24,18 +28,19 @@ use crate::symbols::{Name, SymbolKind}; /// /// since `col` is defined in both `t` and `u`. pub(crate) fn resolve_name_ref_ptrs( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, ) -> Option> { - resolve_name_ref(binder, root, name_ref).map(|(ptrs, _)| ptrs) + resolve_name_ref(db, file, name_ref).map(|(ptrs, _)| ptrs) } pub(crate) fn resolve_name_ref( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, ) -> Option<(SmallVec<[SyntaxNodePtr; 1]>, LocationKind)> { + let binder = bind(db, file); let context = classify_name_ref(name_ref.syntax())?; match context { @@ -49,7 +54,7 @@ pub(crate) fn resolve_name_ref( return Some((smallvec![cte_ptr], LocationKind::Table)); } - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Table)) } NameRefClass::NamedArgParameter => { @@ -67,7 +72,7 @@ pub(crate) fn resolve_name_ref( binder.lookup_with(&function_name, SymbolKind::Aggregate, position, &schema) })?; - let param_ptr = find_param_in_func_def(root, function_ptr, ¶m_name)?; + let param_ptr = find_param_in_func_def(db, file, function_ptr, ¶m_name)?; Some((smallvec![param_ptr], LocationKind::NamedArgParameter)) } NameRefClass::Cursor => { @@ -78,12 +83,12 @@ pub(crate) fn resolve_name_ref( } NameRefClass::PreparedStatement => { let statement_name = Name::from_node(name_ref); - resolve_prepared_statement_name_ptr(binder, &statement_name) + resolve_prepared_statement_name_ptr(db, file, &statement_name) .map(|ptr| (smallvec![ptr], LocationKind::PreparedStatement)) } NameRefClass::Channel => { let channel_name = Name::from_node(name_ref); - resolve_channel_name_ptr(binder, &channel_name) + resolve_channel_name_ptr(db, file, &channel_name) .map(|ptr| (smallvec![ptr], LocationKind::Channel)) } NameRefClass::FromTable => { @@ -107,18 +112,18 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); if let Some(table_name_ptr) = - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) { return Some((smallvec![table_name_ptr], LocationKind::Table)); } - resolve_view_name_ptr(binder, &table_name, &schema, position) + resolve_view_name_ptr(db, file, &table_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::View)) } NameRefClass::Index => { let position = name_ref.syntax().text_range().start(); let index_name = Name::from_node(name_ref); - resolve_index_name_ptr(binder, &index_name, &None, position) + resolve_index_name_ptr(db, file, &index_name, &None, position) .map(|ptr| (smallvec![ptr], LocationKind::Index)) } NameRefClass::Type => { @@ -142,13 +147,13 @@ pub(crate) fn resolve_name_ref( }; let type_name = resolve_float_precision(name_ref, type_name); let position = name_ref.syntax().text_range().start(); - resolve_type_name_ptr(binder, &type_name, &schema, position) + resolve_type_name_ptr(db, file, &type_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Type)) } NameRefClass::View => { 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) + resolve_view_name_ptr(db, file, &view_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::View)) } NameRefClass::Window => { @@ -157,7 +162,7 @@ pub(crate) fn resolve_name_ref( NameRefClass::Sequence => { 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) + resolve_sequence_name_ptr(db, file, &sequence_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Sequence)) } NameRefClass::Trigger => { @@ -175,7 +180,7 @@ pub(crate) fn resolve_name_ref( } let table_name = extract_table_name(&on_table_path)?; let position = name_ref.syntax().text_range().start(); - resolve_trigger_name_ptr(binder, &trigger_name, &schema, position, Some(table_name)) + resolve_trigger_name_ptr(db, file, &trigger_name, &schema, position, Some(table_name)) .map(|ptr| (smallvec![ptr], LocationKind::Trigger)) } NameRefClass::Policy => { @@ -190,60 +195,60 @@ pub(crate) fn resolve_name_ref( let on_table_path = on_table.and_then(|on_table| on_table.path())?; let (table_name, schema) = extract_table_schema_from_path(&on_table_path)?; let position = name_ref.syntax().text_range().start(); - resolve_policy_name_ptr(binder, &policy_name, &schema, position, table_name) + resolve_policy_name_ptr(db, file, &policy_name, &schema, position, table_name) .map(|ptr| (smallvec![ptr], LocationKind::Policy)) } NameRefClass::EventTrigger => { let event_trigger_name = Name::from_node(name_ref); - resolve_event_trigger_name_ptr(binder, &event_trigger_name) + resolve_event_trigger_name_ptr(db, file, &event_trigger_name) .map(|ptr| (smallvec![ptr], LocationKind::EventTrigger)) } NameRefClass::PropertyGraph => { let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; let (property_graph_name, schema) = extract_table_schema_from_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_property_graph_name_ptr(binder, &property_graph_name, &schema, position) + resolve_property_graph_name_ptr(db, file, &property_graph_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::PropertyGraph)) } NameRefClass::Database => { let database_name = Name::from_node(name_ref); - resolve_database_name_ptr(binder, &database_name) + resolve_database_name_ptr(db, file, &database_name) .map(|ptr| (smallvec![ptr], LocationKind::Database)) } NameRefClass::Server => { let server_name = Name::from_node(name_ref); - resolve_server_name_ptr(binder, &server_name) + resolve_server_name_ptr(db, file, &server_name) .map(|ptr| (smallvec![ptr], LocationKind::Server)) } NameRefClass::Extension => { let extension_name = Name::from_node(name_ref); - resolve_extension_name_ptr(binder, &extension_name) + resolve_extension_name_ptr(db, file, &extension_name) .map(|ptr| (smallvec![ptr], LocationKind::Extension)) } NameRefClass::Role => { let role_name = Name::from_node(name_ref); - resolve_role_name_ptr(binder, &role_name) + resolve_role_name_ptr(db, file, &role_name) .map(|ptr| (smallvec![ptr], LocationKind::Role)) } NameRefClass::QualifiedColumn => { let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; let column_name = Name::from_node(name_ref); let table_path = path.qualifier()?; - resolve_column_for_path(binder, root, &table_path, column_name).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + resolve_column_for_path(db, file, &table_path, column_name).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } NameRefClass::Tablespace => { let tablespace_name = Name::from_node(name_ref); - resolve_tablespace_name_ptr(binder, &tablespace_name) + resolve_tablespace_name_ptr(db, file, &tablespace_name) .map(|ptr| (smallvec![ptr], LocationKind::Tablespace)) } NameRefClass::ForeignKeyTable => { let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; let (table_name, schema) = extract_table_schema_from_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Table)) } NameRefClass::ForeignKeyColumn => { @@ -264,8 +269,8 @@ pub(crate) fn resolve_name_ref( return None; }; let column_name = Name::from_node(name_ref); - resolve_column_for_path(binder, root, &path, column_name).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + resolve_column_for_path(db, file, &path, column_name).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } @@ -273,17 +278,18 @@ pub(crate) fn resolve_name_ref( let column_name = Name::from_node(name_ref); for ancestor in name_ref.syntax().ancestors() { if let Some(create_table) = ast::CreateTableLike::cast(ancestor.clone()) { - return find_column_in_create_table(binder, root, &create_table, &column_name) - .map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + return find_column_in_create_table(db, file, &create_table, &column_name).map( + |ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) - }); + }, + ); } if let Some(alter_table) = ast::AlterTable::cast(ancestor) { let table_path = alter_table.relation_name()?.path()?; - return resolve_column_for_path(binder, root, &table_path, column_name).map( + return resolve_column_for_path(db, file, &table_path, column_name).map( |ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }, ); @@ -302,8 +308,8 @@ pub(crate) fn resolve_name_ref( } })?; let column_name = Name::from_node(name_ref); - resolve_column_for_path(binder, root, &on_table_path, column_name).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + resolve_column_for_path(db, file, &on_table_path, column_name).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } @@ -319,7 +325,7 @@ pub(crate) fn resolve_name_ref( })?; let (table_name, schema) = extract_table_schema_from_path(&on_table_path)?; let position = name_ref.syntax().text_range().start(); - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Table)) } NameRefClass::LikeTable => { @@ -330,7 +336,7 @@ pub(crate) fn resolve_name_ref( let path = like_clause.path()?; let (table_name, schema) = extract_table_schema_from_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) .map(|ptr| (smallvec![ptr], LocationKind::Table)) } NameRefClass::Function => { @@ -342,8 +348,15 @@ pub(crate) fn resolve_name_ref( let (function_name, schema) = extract_table_schema_from_path(&path)?; let params = extract_param_signature(&function_sig); let position = name_ref.syntax().text_range().start(); - resolve_function(binder, &function_name, &schema, params.as_deref(), position) - .map(|ptr| (smallvec![ptr], LocationKind::Function)) + resolve_function( + db, + file, + &function_name, + &schema, + params.as_deref(), + position, + ) + .map(|ptr| (smallvec![ptr], LocationKind::Function)) } NameRefClass::Aggregate => { let aggregate = name_ref @@ -355,7 +368,8 @@ pub(crate) fn resolve_name_ref( let params = extract_param_signature(&aggregate); let position = name_ref.syntax().text_range().start(); resolve_aggregate( - binder, + db, + file, &aggregate_name, &schema, params.as_deref(), @@ -373,7 +387,8 @@ pub(crate) fn resolve_name_ref( let params = extract_param_signature(&function_sig); let position = name_ref.syntax().text_range().start(); resolve_procedure( - binder, + db, + file, &procedure_name, &schema, params.as_deref(), @@ -391,32 +406,49 @@ pub(crate) fn resolve_name_ref( let params = extract_param_signature(&function_sig); let position = name_ref.syntax().text_range().start(); - if let Some(ptr) = - resolve_function(binder, &routine_name, &schema, params.as_deref(), position) - { + if let Some(ptr) = resolve_function( + db, + file, + &routine_name, + &schema, + params.as_deref(), + position, + ) { return Some((smallvec![ptr], LocationKind::Function)); } - if let Some(ptr) = - resolve_aggregate(binder, &routine_name, &schema, params.as_deref(), position) - { + if let Some(ptr) = resolve_aggregate( + db, + file, + &routine_name, + &schema, + params.as_deref(), + position, + ) { return Some((smallvec![ptr], LocationKind::Aggregate)); } - resolve_procedure(binder, &routine_name, &schema, params.as_deref(), position) - .map(|ptr| (smallvec![ptr], LocationKind::Procedure)) + resolve_procedure( + db, + file, + &routine_name, + &schema, + params.as_deref(), + position, + ) + .map(|ptr| (smallvec![ptr], LocationKind::Procedure)) } NameRefClass::CallProcedure => { let call = name_ref.syntax().ancestors().find_map(ast::Call::cast)?; let path = call.path()?; let (procedure_name, schema) = extract_table_schema_from_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_procedure(binder, &procedure_name, &schema, None, position) + resolve_procedure(db, file, &procedure_name, &schema, None, position) .map(|ptr| (smallvec![ptr], LocationKind::Procedure)) } NameRefClass::Schema => { let schema_name = Name::from_node(name_ref); - resolve_schema(binder, &schema_name).map(|ptr| (smallvec![ptr], LocationKind::Schema)) + resolve_schema(db, file, &schema_name).map(|ptr| (smallvec![ptr], LocationKind::Schema)) } NameRefClass::FunctionCall => { let schema = if let Some(parent_node) = name_ref.syntax().parent() @@ -430,7 +462,7 @@ pub(crate) fn resolve_name_ref( }; let function_name = Name::from_node(name_ref); let position = name_ref.syntax().text_range().start(); - resolve_function(binder, &function_name, &schema, None, position) + resolve_function(db, file, &function_name, &schema, None, position) .map(|ptr| (smallvec![ptr], LocationKind::Function)) } NameRefClass::ProcedureCall => { @@ -446,7 +478,7 @@ pub(crate) fn resolve_name_ref( let procedure_name = Name::from_node(name_ref); let position = name_ref.syntax().text_range().start(); - resolve_procedure(binder, &procedure_name, &schema, None, position) + resolve_procedure(db, file, &procedure_name, &schema, None, position) .map(|ptr| (smallvec![ptr], LocationKind::Procedure)) } NameRefClass::FunctionName => { @@ -457,7 +489,7 @@ pub(crate) fn resolve_name_ref( let path = path_type.path()?; let (function_name, schema) = extract_table_schema_from_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_function(binder, &function_name, &schema, None, position) + resolve_function(db, file, &function_name, &schema, None, position) .map(|ptr| (smallvec![ptr], LocationKind::Function)) } NameRefClass::SelectFunctionCall => { @@ -474,12 +506,13 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); // functions take precedence - if let Some(ptr) = resolve_function(binder, &function_name, &schema, None, position) { + if let Some(ptr) = resolve_function(db, file, &function_name, &schema, None, position) { return Some((smallvec![ptr], LocationKind::Function)); } // aggregates take precedence over function-call-style column access - if let Some(ptr) = resolve_aggregate(binder, &function_name, &schema, None, position) { + if let Some(ptr) = resolve_aggregate(db, file, &function_name, &schema, None, position) + { return Some((smallvec![ptr], LocationKind::Aggregate)); } @@ -489,76 +522,73 @@ pub(crate) fn resolve_name_ref( // select a(t) from t; // ``` if schema.is_none() - && let Some(ptr) = resolve_fn_call_column(binder, root, name_ref) + && let Some(ptr) = resolve_fn_call_column(db, file, name_ref) { return Some((smallvec![ptr], LocationKind::Column)); } None } - NameRefClass::CreateIndexColumn => resolve_create_index_column_ptr(binder, root, name_ref) - .map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); - (smallvec![ptr], kind) - }), - NameRefClass::SelectColumn => { - resolve_select_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + NameRefClass::CreateIndexColumn => { + resolve_create_index_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } + NameRefClass::SelectColumn => resolve_select_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); + (smallvec![ptr], kind) + }), NameRefClass::SelectQualifiedColumnTable => { - resolve_select_qualified_column_table_name_ptr(binder, name_ref) + resolve_select_qualified_column_table_name_ptr(db, file, name_ref) .map(|ptr| (smallvec![ptr], LocationKind::Table)) } NameRefClass::SelectQualifiedColumn => { - resolve_select_qualified_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + resolve_select_qualified_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } - NameRefClass::CompositeTypeField => { - resolve_composite_type_field_ptr(binder, root, name_ref) - .map(|ptr| (smallvec![ptr], LocationKind::Column)) - } - NameRefClass::InsertColumn => { - resolve_insert_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); - (smallvec![ptr], kind) - }) + NameRefClass::CompositeTypeField => resolve_composite_type_field_ptr(db, file, name_ref) + .map(|ptr| (smallvec![ptr], LocationKind::Column)), + NameRefClass::InsertColumn => resolve_insert_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); + (smallvec![ptr], kind) + }), + NameRefClass::InsertQualifiedColumnTable => { + resolve_insert_table_name_ptr(db, file, name_ref) + .map(|ptr| (smallvec![ptr], LocationKind::Table)) } - NameRefClass::InsertQualifiedColumnTable => resolve_insert_table_name_ptr(binder, name_ref) - .map(|ptr| (smallvec![ptr], LocationKind::Table)), - NameRefClass::DeleteColumn => { - resolve_delete_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); - (smallvec![ptr], kind) - }) + NameRefClass::DeleteColumn => resolve_delete_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); + (smallvec![ptr], kind) + }), + NameRefClass::DeleteQualifiedColumnTable => { + resolve_delete_table_name_ptr(db, file, name_ref) + .map(|ptr| (smallvec![ptr], LocationKind::Table)) } - NameRefClass::DeleteQualifiedColumnTable => resolve_delete_table_name_ptr(binder, name_ref) - .map(|ptr| (smallvec![ptr], LocationKind::Table)), - NameRefClass::UpdateColumn => { - resolve_update_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); - (smallvec![ptr], kind) - }) + NameRefClass::UpdateColumn => resolve_update_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); + (smallvec![ptr], kind) + }), + NameRefClass::UpdateQualifiedColumnTable => { + resolve_update_table_name_ptr(db, file, name_ref) + .map(|ptr| (smallvec![ptr], LocationKind::Table)) } - NameRefClass::UpdateQualifiedColumnTable => resolve_update_table_name_ptr(binder, name_ref) - .map(|ptr| (smallvec![ptr], LocationKind::Table)), - NameRefClass::MergeColumn => resolve_merge_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + NameRefClass::MergeColumn => resolve_merge_column_ptr(db, file, name_ref).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }), - NameRefClass::MergeQualifiedColumnTable => resolve_merge_table_name_ptr(binder, name_ref) + NameRefClass::MergeQualifiedColumnTable => resolve_merge_table_name_ptr(db, file, name_ref) .map(|ptr| (smallvec![ptr], LocationKind::Table)), - NameRefClass::JoinUsingColumn => resolve_join_using_columns(binder, root, name_ref) - .map(|ptrs| (ptrs, LocationKind::Column)), - NameRefClass::PropertyGraphColumn => { - resolve_property_graph_column_ptr(binder, root, name_ref).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); - (smallvec![ptr], kind) - }) + NameRefClass::JoinUsingColumn => { + resolve_join_using_columns(db, file, name_ref).map(|ptrs| (ptrs, LocationKind::Column)) } + NameRefClass::PropertyGraphColumn => resolve_property_graph_column_ptr(db, file, name_ref) + .map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); + (smallvec![ptr], kind) + }), NameRefClass::AlterColumn => { let column_name = Name::from_node(name_ref); let alter_table = name_ref @@ -566,49 +596,56 @@ pub(crate) fn resolve_name_ref( .ancestors() .find_map(ast::AlterTable::cast)?; let table_path = alter_table.relation_name()?.path()?; - resolve_column_for_path(binder, root, &table_path, column_name).map(|ptr| { - let kind = resolved_location_kind(root, &ptr, LocationKind::Column); + resolve_column_for_path(db, file, &table_path, column_name).map(|ptr| { + let kind = resolved_location_kind(db, file, &ptr, LocationKind::Column); (smallvec![ptr], kind) }) } } - .or_else(|| resolve_special_keyword_as_function(binder, name_ref)) + .or_else(|| resolve_special_keyword_as_function(db, file, name_ref)) } fn resolved_location_kind( - root: &SyntaxNode, + db: &dyn Db, + file: File, ptr: &SyntaxNodePtr, fallback: LocationKind, ) -> LocationKind { - classify_def_node(&ptr.to_node(root)) - .map(LocationKind::from) - .unwrap_or(fallback) + let tree = parse(db, file).tree(); + let root = tree.syntax(); + LocationKind::from_node(&ptr.to_node(root)).unwrap_or(fallback) } fn resolve_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); binder.lookup_with(table_name, SymbolKind::Table, position, schema) } fn resolve_index_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, index_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); binder.lookup_with(index_name, SymbolKind::Index, position, schema) } fn resolve_type_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, type_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); if let Some(ptr) = binder.lookup_with(type_name, SymbolKind::Type, position, schema) { return Some(ptr); } @@ -623,12 +660,13 @@ fn resolve_type_name_ptr( } pub(crate) fn resolve_type_ptr_from_type( - binder: &Binder, + db: &dyn Db, + file: File, ty: &ast::Type, position: TextSize, ) -> Option { let (type_name, schema) = type_name_and_schema_from_type(ty)?; - resolve_type_name_ptr(binder, &type_name, &schema, position) + resolve_type_name_ptr(db, file, &type_name, &schema, position) } fn type_name_and_schema_from_type(ty: &ast::Type) -> Option<(Name, Option)> { @@ -715,40 +753,48 @@ fn resolve_float_precision(name_ref: &ast::NameRef, type_name: Name) -> Name { } fn resolve_view_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, view_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); binder.lookup_with(view_name, SymbolKind::View, position, schema) } fn resolve_sequence_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, sequence_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); binder.lookup_with(sequence_name, SymbolKind::Sequence, position, schema) } fn resolve_trigger_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, trigger_name: &Name, schema: &Option, position: TextSize, table: Option, ) -> Option { + let binder = bind(db, file); binder.lookup_with_table(trigger_name, SymbolKind::Trigger, position, schema, &table) } fn resolve_policy_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, policy_name: &Name, schema: &Option, position: TextSize, table: Name, ) -> Option { + let binder = bind(db, file); binder.lookup_with_table( policy_name, SymbolKind::Policy, @@ -759,18 +805,22 @@ fn resolve_policy_name_ptr( } fn resolve_event_trigger_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, event_trigger_name: &Name, ) -> Option { + let binder = bind(db, file); binder.lookup(event_trigger_name, SymbolKind::EventTrigger) } fn resolve_property_graph_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, property_graph_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let binder = bind(db, file); binder.lookup_with( property_graph_name, SymbolKind::PropertyGraph, @@ -779,51 +829,74 @@ fn resolve_property_graph_name_ptr( ) } -fn resolve_tablespace_name_ptr(binder: &Binder, tablespace_name: &Name) -> Option { +fn resolve_tablespace_name_ptr( + db: &dyn Db, + file: File, + tablespace_name: &Name, +) -> Option { + let binder = bind(db, file); binder.lookup(tablespace_name, SymbolKind::Tablespace) } fn resolve_prepared_statement_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, statement_name: &Name, ) -> Option { + let binder = bind(db, file); binder.lookup(statement_name, SymbolKind::PreparedStatement) } -fn resolve_channel_name_ptr(binder: &Binder, channel_name: &Name) -> Option { +fn resolve_channel_name_ptr(db: &dyn Db, file: File, channel_name: &Name) -> Option { + let binder = bind(db, file); binder.lookup(channel_name, SymbolKind::Channel) } -fn resolve_database_name_ptr(binder: &Binder, database_name: &Name) -> Option { +fn resolve_database_name_ptr( + db: &dyn Db, + file: File, + database_name: &Name, +) -> Option { + let binder = bind(db, file); binder.lookup(database_name, SymbolKind::Database) } -fn resolve_server_name_ptr(binder: &Binder, server_name: &Name) -> Option { +fn resolve_server_name_ptr(db: &dyn Db, file: File, server_name: &Name) -> Option { + let binder = bind(db, file); binder.lookup(server_name, SymbolKind::Server) } -fn resolve_extension_name_ptr(binder: &Binder, extension_name: &Name) -> Option { +fn resolve_extension_name_ptr( + db: &dyn Db, + file: File, + extension_name: &Name, +) -> Option { + let binder = bind(db, file); binder.lookup(extension_name, SymbolKind::Extension) } -fn resolve_role_name_ptr(binder: &Binder, role_name: &Name) -> Option { +fn resolve_role_name_ptr(db: &dyn Db, file: File, role_name: &Name) -> Option { + let binder = bind(db, file); binder.lookup(role_name, SymbolKind::Role) } fn resolve_for_kind_with_params( - binder: &Binder, + db: &dyn Db, + file: File, name: &Name, schema: &Option, params: Option<&[Name]>, position: TextSize, kind: SymbolKind, ) -> Option { + let binder = bind(db, file); binder.lookup_with_params(name, kind, position, schema, params) } // some keywords behave as functions fn resolve_special_keyword_as_function( - binder: &Binder, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, ) -> Option<(SmallVec<[SyntaxNodePtr; 1]>, LocationKind)> { let function_name = name_ref @@ -838,19 +911,21 @@ fn resolve_special_keyword_as_function( })?; let function_name = Name::from_string(function_name); let position = name_ref.syntax().text_range().start(); - resolve_function(binder, &function_name, &None, None, position) + resolve_function(db, file, &function_name, &None, None, position) .map(|ptr| (smallvec![ptr], LocationKind::Function)) } fn resolve_function( - binder: &Binder, + db: &dyn Db, + file: File, function_name: &Name, schema: &Option, params: Option<&[Name]>, position: TextSize, ) -> Option { resolve_for_kind_with_params( - binder, + db, + file, function_name, schema, params, @@ -860,14 +935,16 @@ fn resolve_function( } fn resolve_aggregate( - binder: &Binder, + db: &dyn Db, + file: File, aggregate_name: &Name, schema: &Option, params: Option<&[Name]>, position: TextSize, ) -> Option { resolve_for_kind_with_params( - binder, + db, + file, aggregate_name, schema, params, @@ -877,14 +954,16 @@ fn resolve_aggregate( } fn resolve_procedure( - binder: &Binder, + db: &dyn Db, + file: File, procedure_name: &Name, schema: &Option, params: Option<&[Name]>, position: TextSize, ) -> Option { resolve_for_kind_with_params( - binder, + db, + file, procedure_name, schema, params, @@ -893,13 +972,14 @@ fn resolve_procedure( ) } -fn resolve_schema(binder: &Binder, schema_name: &Name) -> Option { +fn resolve_schema(db: &dyn Db, file: File, schema_name: &Name) -> Option { + let binder = bind(db, file); binder.lookup(schema_name, SymbolKind::Schema) } fn resolve_create_index_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -910,12 +990,12 @@ fn resolve_create_index_column_ptr( .find_map(ast::CreateIndex::cast)?; let path = create_index.relation_name()?.path()?; - resolve_column_for_path(binder, root, &path, column_name) + resolve_column_for_path(db, file, &path, column_name) } fn resolve_property_graph_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -927,24 +1007,17 @@ fn resolve_property_graph_column_ptr( if let Some(references_table) = ast::ReferencesTable::cast(column_list.syntax().parent()?) { let table_name = Name::from_node(&references_table.name_ref()?); let position = column_name_ref.syntax().text_range().start(); - return resolve_column_for_table( - binder, - root, - &table_name, - &None, - &column_name, - position, - ); + return resolve_column_for_table(db, file, &table_name, &None, &column_name, position); } else if let Some(edge_table_def) = column_list .syntax() .ancestors() .find_map(ast::EdgeTableDef::cast) { - return resolve_column_for_path(binder, root, &edge_table_def.path()?, column_name); + return resolve_column_for_path(db, file, &edge_table_def.path()?, column_name); } else if let Some(vertex_table_def) = ast::VertexTableDef::cast(column_list.syntax().parent()?) { - return resolve_column_for_path(binder, root, &vertex_table_def.path()?, column_name); + return resolve_column_for_path(db, file, &vertex_table_def.path()?, column_name); } } else if let Some(expr_as_name) = ast::ExprAsName::cast(parent) && let Some(expr_as_name_list) = ast::ExprAsNameList::cast(expr_as_name.syntax().parent()?) @@ -952,9 +1025,9 @@ fn resolve_property_graph_column_ptr( { let parent = properties.syntax().parent()?; if let Some(edge) = ast::EdgeTableDef::cast(parent.clone()) { - return resolve_column_for_path(binder, root, &edge.path()?, column_name); + return resolve_column_for_path(db, file, &edge.path()?, column_name); } else if let Some(vertex) = ast::VertexTableDef::cast(parent) { - return resolve_column_for_path(binder, root, &vertex.path()?, column_name); + return resolve_column_for_path(db, file, &vertex.path()?, column_name); } } @@ -962,20 +1035,20 @@ fn resolve_property_graph_column_ptr( } fn resolve_column_for_path( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, path: &ast::Path, column_name: Name, ) -> Option { let (table_name, schema) = extract_table_schema_from_path(path)?; let position = path.syntax().text_range().start(); - resolve_column_for_table(binder, root, &table_name, &schema, &column_name, position) + resolve_column_for_table(db, file, &table_name, &schema, &column_name, position) } fn resolve_insert_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -986,11 +1059,12 @@ fn resolve_insert_column_ptr( .find_map(ast::Insert::cast)?; let path = insert.path()?; - resolve_column_for_path(binder, root, &path, column_name) + resolve_column_for_path(db, file, &path, column_name) } fn resolve_select_qualified_column_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, ) -> Option { let table_name = Name::from_node(table_name_ref); @@ -1037,12 +1111,12 @@ fn resolve_select_qualified_column_table_name_ptr( && function_schema == explicit_schema { let position = table_name_ref.syntax().text_range().start(); - return resolve_function(binder, &function_name, &function_schema, None, position); + return resolve_function(db, file, &function_name, &function_schema, None, position); } if let Some(schema) = explicit_schema { let position = table_name_ref.syntax().text_range().start(); - return resolve_table_name_ptr(binder, &table_name, &Some(schema), position); + return resolve_table_name_ptr(db, file, &table_name, &Some(schema), position); } let (table_name, schema) = if let Some(name_ref_node) = from_item.name_ref() { @@ -1072,7 +1146,7 @@ fn resolve_select_qualified_column_table_name_ptr( }; let position = table_name_ref.syntax().text_range().start(); - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) } enum ReturningClauseMatch { @@ -1134,8 +1208,8 @@ fn extract_table_schema_from_name_ref(name_ref: &ast::NameRef) -> Option<(Name, } fn resolve_select_qualified_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -1229,8 +1303,8 @@ fn resolve_select_qualified_column_ptr( if let Some(call_expr) = from_item.call_expr() && let Some(ptr) = resolve_column_from_call_expr_return_table( - binder, - root, + db, + file, &call_expr, column_name_ref, &column_name, @@ -1248,8 +1322,8 @@ fn resolve_select_qualified_column_ptr( { if let Some(paren_select) = from_item.paren_select() { return resolve_subquery_column_ptr( - binder, - root, + db, + file, &paren_select, column_name_ref, &column_name, @@ -1259,8 +1333,8 @@ fn resolve_select_qualified_column_ptr( if let Some(paren_expr) = from_item.paren_expr() { return resolve_column_from_paren_expr( - binder, - root, + db, + file, &paren_expr, column_name_ref, &column_name, @@ -1284,13 +1358,7 @@ fn resolve_select_qualified_column_ptr( // ``` if let Some(name_ref_node) = from_item.name_ref() { let cte_name = Name::from_node(&name_ref_node); - return resolve_cte_column( - binder, - root, - column_name_ref, - &cte_name, - &column_name, - ); + return resolve_cte_column(db, file, column_name_ref, &cte_name, &column_name); } } @@ -1334,7 +1402,7 @@ fn resolve_select_qualified_column_ptr( if schema.is_none() { if resolve_cte_table(column_name_ref, &table_name).is_some() { if let Some(cte_column_ptr) = - resolve_cte_column(binder, root, column_name_ref, &table_name, &column_name) + resolve_cte_column(db, file, column_name_ref, &table_name, &column_name) { return Some(cte_column_ptr); } @@ -1345,41 +1413,41 @@ fn resolve_select_qualified_column_ptr( } } - resolve_column_for_table(binder, root, &table_name, &schema, &column_name, position) + resolve_column_for_table(db, file, &table_name, &schema, &column_name, position) } fn resolve_column_for_table( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, table_name: &Name, schema: &Option, column_name: &Name, position: TextSize, ) -> Option { - let resolved = resolve_table_name(binder, root, table_name, schema, position)?; + let resolved = resolve_table_name(db, file, table_name, schema, position)?; match resolved { ResolvedTableName::View(create_view) => { if let Some(ptr) = find_column_in_create_view(&create_view, column_name) { return Some(ptr); } - return resolve_function(binder, column_name, schema, None, position); + return resolve_function(db, file, column_name, schema, None, position); } ResolvedTableName::Table(create_table_like) => { // 1. Try to find a matching column (columns take precedence) if let Some(ptr) = - find_column_in_create_table(binder, root, &create_table_like, column_name) + find_column_in_create_table(db, file, &create_table_like, column_name) { return Some(ptr); } // 2. No column found, check for field-style function call // e.g., select t.b from t where b is a function that takes t as an argument - return resolve_function(binder, column_name, schema, None, position); + return resolve_function(db, file, column_name, schema, None, position); } ResolvedTableName::TableAs(create_table_as) => { if let Some(ptr) = find_column_in_create_table_as(&create_table_as, column_name) { return Some(ptr); } - return resolve_function(binder, column_name, schema, None, position); + return resolve_function(db, file, column_name, schema, None, position); } } } @@ -1390,14 +1458,16 @@ enum ResolvedTableName { TableAs(ast::CreateTableAs), } fn resolve_table_name( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, table_name: &Name, schema: &Option, position: TextSize, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); use ResolvedTableName::*; - if let Some(table_name_ptr) = resolve_table_name_ptr(binder, table_name, schema, position) { + if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, table_name, schema, position) { let table_name_node = table_name_ptr.to_node(root); if let Some(create_table) = table_name_node .ancestors() @@ -1413,7 +1483,7 @@ fn resolve_table_name( } } - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, table_name, schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, table_name, schema, position) { let view_name_node = view_name_ptr.to_node(root); if let Some(create_view) = view_name_node.ancestors().find_map(ast::CreateView::cast) { return Some(View(create_view)); @@ -1446,8 +1516,8 @@ fn find_parent_alias_from_item(syntax: &SyntaxNode) -> Option { } fn resolve_from_item_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_item: &ast::FromItem, column_name_ref: &ast::NameRef, ) -> Option { @@ -1455,8 +1525,8 @@ fn resolve_from_item_column_ptr( if let Some(paren_select) = from_item.paren_select() { let alias = from_item.alias(); if let Some(ptr) = resolve_subquery_column_ptr( - binder, - root, + db, + file, &paren_select, column_name_ref, &column_name, @@ -1487,7 +1557,7 @@ fn resolve_from_item_column_ptr( } if let Some(ptr) = - resolve_column_from_paren_expr(binder, root, &paren_expr, column_name_ref, &column_name) + resolve_column_from_paren_expr(db, file, &paren_expr, column_name_ref, &column_name) { return Some(ptr); } @@ -1517,8 +1587,8 @@ fn resolve_from_item_column_ptr( if let Some(call_expr) = from_item.call_expr() && let Some(ptr) = resolve_column_from_call_expr_return_table( - binder, - root, + db, + file, &call_expr, column_name_ref, &column_name, @@ -1534,7 +1604,7 @@ fn resolve_from_item_column_ptr( && let Some(cte_ptr) = resolve_cte_table(column_name_ref, &table_name) { if let Some(cte_column_ptr) = - resolve_cte_column(binder, root, column_name_ref, &table_name, &column_name) + resolve_cte_column(db, file, column_name_ref, &table_name, &column_name) { return Some(cte_column_ptr); } @@ -1551,8 +1621,8 @@ fn resolve_from_item_column_ptr( } if let Some(ptr) = resolve_column_from_table_or_view( - binder, - root, + db, + file, column_name_ref, &table_name, &schema, @@ -1572,33 +1642,27 @@ fn resolve_from_item_column_ptr( } fn resolve_column_from_table_or_view( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, table_name: &Name, schema: &Option, column_name: &Name, ) -> Option { - resolve_column_from_table_or_view_impl( - binder, - root, - name_ref, - table_name, - schema, - column_name, - 0, - ) + resolve_column_from_table_or_view_impl(db, file, name_ref, table_name, schema, column_name, 0) } fn resolve_column_from_table_or_view_impl( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, table_name: &Name, schema: &Option, column_name: &Name, depth: u32, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); if depth > 40 { log::info!("max resolve depth reached, probably in a cycle"); return None; @@ -1606,7 +1670,7 @@ fn resolve_column_from_table_or_view_impl( let position = name_ref.syntax().text_range().start(); - if let Some(table_name_ptr) = resolve_table_name_ptr(binder, table_name, schema, position) { + if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, table_name, schema, position) { let table_name_node = table_name_ptr.to_node(root); if let Some(create_table) = table_name_node @@ -1614,8 +1678,7 @@ fn resolve_column_from_table_or_view_impl( .find_map(ast::CreateTableLike::cast) { // 1. try to find a matching column - if let Some(ptr) = find_column_in_create_table(binder, root, &create_table, column_name) - { + if let Some(ptr) = find_column_in_create_table(db, file, &create_table, column_name) { return Some(ptr); } @@ -1627,8 +1690,8 @@ fn resolve_column_from_table_or_view_impl( let (parent_table_name, parent_schema) = extract_table_schema_from_path(&parent_path)?; return resolve_column_from_table_or_view_impl( - binder, - root, + db, + file, name_ref, &parent_table_name, &parent_schema, @@ -1663,7 +1726,7 @@ fn resolve_column_from_table_or_view_impl( } // ditto as above but with view - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, table_name, schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, table_name, schema, position) { let view_name_node = view_name_ptr.to_node(root); if let Some(create_view) = view_name_node.ancestors().find_map(ast::CreateView::cast) { @@ -1681,8 +1744,8 @@ fn resolve_column_from_table_or_view_impl( } fn resolve_from_item_for_cte_star( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_item: &ast::FromItem, name_ref: &ast::NameRef, cte_name: &Name, @@ -1692,8 +1755,8 @@ fn resolve_from_item_for_cte_star( && table_name == *cte_name { return resolve_column_from_table_or_view( - binder, - root, + db, + file, name_ref, &table_name, &schema, @@ -1701,7 +1764,7 @@ fn resolve_from_item_for_cte_star( ); } - resolve_from_item_column_ptr(binder, root, from_item, name_ref) + resolve_from_item_column_ptr(db, file, from_item, name_ref) } fn resolve_from_join_expr(join_expr: &ast::JoinExpr, try_resolve: &F) -> Option @@ -1728,8 +1791,8 @@ where } fn resolve_select_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let select = column_name_ref @@ -1740,7 +1803,7 @@ fn resolve_select_column_ptr( for from_item in from_clause.from_items() { if let Some(column_ptr) = - resolve_from_item_column_ptr(binder, root, &from_item, column_name_ref) + resolve_from_item_column_ptr(db, file, &from_item, column_name_ref) { return Some(column_ptr); } @@ -1749,7 +1812,7 @@ fn resolve_select_column_ptr( for join_expr in from_clause.join_exprs() { if let Some(column_ptr) = resolve_from_join_expr(&join_expr, &|from_item: &ast::FromItem| { - resolve_from_item_column_ptr(binder, root, from_item, column_name_ref) + resolve_from_item_column_ptr(db, file, from_item, column_name_ref) }) { return Some(column_ptr); @@ -1776,8 +1839,8 @@ fn resolve_window_name_ptr(name_ref: &ast::NameRef) -> Option { } fn resolve_delete_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -1788,12 +1851,12 @@ fn resolve_delete_column_ptr( .find_map(ast::Delete::cast)?; let path = delete.relation_name()?.path()?; - resolve_column_for_path(binder, root, &path, column_name) + resolve_column_for_path(db, file, &path, column_name) } fn resolve_join_using_columns( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, ) -> Option> { let join_expr = name_ref @@ -1804,7 +1867,7 @@ fn resolve_join_using_columns( let mut results: SmallVec<[SyntaxNodePtr; 1]> = SmallVec::new(); collect_from_join_expr(&join_expr, &mut results, &|from_item: &ast::FromItem| { - resolve_from_item_column_ptr(binder, root, from_item, name_ref) + resolve_from_item_column_ptr(db, file, from_item, name_ref) }); (!results.is_empty()).then_some(results) @@ -1835,8 +1898,8 @@ fn collect_from_join_expr( } fn resolve_update_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -1850,7 +1913,7 @@ fn resolve_update_column_ptr( if let Some(from_clause) = update.from_clause() { for from_item in from_clause.from_items() { if let Some(result) = - resolve_from_item_column_ptr(binder, root, &from_item, column_name_ref) + resolve_from_item_column_ptr(db, file, &from_item, column_name_ref) { return Some(result); } @@ -1859,7 +1922,7 @@ fn resolve_update_column_ptr( for join_expr in from_clause.join_exprs() { if let Some(result) = resolve_from_join_expr(&join_expr, &|from_item: &ast::FromItem| { - resolve_from_item_column_ptr(binder, root, from_item, column_name_ref) + resolve_from_item_column_ptr(db, file, from_item, column_name_ref) }) { return Some(result); @@ -1870,12 +1933,12 @@ fn resolve_update_column_ptr( // `update t set a = b` let path = update.relation_name()?.path()?; - resolve_column_for_path(binder, root, &path, column_name) + resolve_column_for_path(db, file, &path, column_name) } fn resolve_fn_call_column( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(name_ref); @@ -1895,7 +1958,7 @@ fn resolve_fn_call_column( for from_item in from_clause.from_items() { if let Some(result) = - resolve_from_item_for_fn_call_column(binder, root, &from_item, &column_name, name_ref) + resolve_from_item_for_fn_call_column(db, file, &from_item, &column_name, name_ref) { return Some(result); } @@ -1903,7 +1966,7 @@ fn resolve_fn_call_column( for join_expr in from_clause.join_exprs() { if let Some(result) = resolve_from_join_expr(&join_expr, &|from_item: &ast::FromItem| { - resolve_from_item_for_fn_call_column(binder, root, from_item, &column_name, name_ref) + resolve_from_item_for_fn_call_column(db, file, from_item, &column_name, name_ref) }) { return Some(result); } @@ -1913,23 +1976,25 @@ fn resolve_fn_call_column( } fn resolve_from_item_for_fn_call_column( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_item: &ast::FromItem, column_name: &Name, name_ref: &ast::NameRef, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); let (table_name, schema) = table_and_schema_from_from_item(from_item)?; let position = name_ref.syntax().text_range().start(); - let table_ptr = resolve_table_name_ptr(binder, &table_name, &schema, position)?; + let table_ptr = resolve_table_name_ptr(db, file, &table_name, &schema, position)?; let table_name_node = table_ptr.to_node(root); let create_table = table_name_node .ancestors() .find_map(ast::CreateTableLike::cast)?; - find_column_in_create_table(binder, root, &create_table, column_name) + find_column_in_create_table(db, file, &create_table, column_name) } fn table_and_schema_from_from_item(from_item: &ast::FromItem) -> Option<(Name, Option)> { @@ -2073,18 +2138,18 @@ pub(crate) fn extract_column_name(col: &ast::Column) -> Option { } pub(crate) fn collect_columns_from_create_table( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &ast::CreateTableLike, ) -> Vec<(Name, Option)> { let mut columns = vec![]; - collect_columns_from_create_table_impl(binder, root, create_table, &mut columns, 0); + collect_columns_from_create_table_impl(db, file, create_table, &mut columns, 0); columns } fn collect_columns_from_create_table_impl( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &ast::CreateTableLike, columns: &mut Vec<(Name, Option)>, depth: usize, @@ -2099,11 +2164,11 @@ fn collect_columns_from_create_table_impl( if let Some((table_name, schema)) = extract_table_schema_from_path(&path) { let position = path.syntax().text_range().start(); if let Some(ResolvedTableName::Table(parent_table)) = - resolve_table_name(binder, root, &table_name, &schema, position) + resolve_table_name(db, file, &table_name, &schema, position) { collect_columns_from_create_table_impl( - binder, - root, + db, + file, &parent_table, columns, depth + 1, @@ -2128,11 +2193,11 @@ fn collect_columns_from_create_table_impl( { let position = path.syntax().text_range().start(); if let Some(ResolvedTableName::Table(source_table)) = - resolve_table_name(binder, root, &table_name, &schema, position) + resolve_table_name(db, file, &table_name, &schema, position) { collect_columns_from_create_table_impl( - binder, - root, + db, + file, &source_table, columns, depth + 1, @@ -2147,17 +2212,17 @@ fn collect_columns_from_create_table_impl( } pub(crate) fn find_column_in_create_table( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &impl ast::HasCreateTable, column_name: &Name, ) -> Option { - find_column_in_create_table_impl(binder, root, create_table, column_name, 0) + find_column_in_create_table_impl(db, file, create_table, column_name, 0) } fn find_column_in_create_table_impl( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &impl ast::HasCreateTable, column_name: &Name, depth: usize, @@ -2181,13 +2246,12 @@ fn find_column_in_create_table_impl( let (table_name, schema) = extract_table_schema_from_path(&path)?; let position = path.syntax().text_range().start(); - if let Some(resolved) = - resolve_table_name(binder, root, &table_name, &schema, position) + if let Some(resolved) = resolve_table_name(db, file, &table_name, &schema, position) { if let Some(ptr) = match resolved { ResolvedTableName::Table(source_table) => find_column_in_create_table_impl( - binder, - root, + db, + file, &source_table, column_name, depth + 1, @@ -2210,12 +2274,11 @@ fn find_column_in_create_table_impl( let (table_name, schema) = extract_table_schema_from_path(&path)?; let position = path.syntax().text_range().start(); - if let Some(resolved) = resolve_table_name(binder, root, &table_name, &schema, position) - { + if let Some(resolved) = resolve_table_name(db, file, &table_name, &schema, position) { if let Some(ptr) = match resolved { ResolvedTableName::Table(parent_table) => find_column_in_create_table_impl( - binder, - root, + db, + file, &parent_table, column_name, depth + 1, @@ -2325,21 +2388,23 @@ fn find_parent_with_clause(node: &SyntaxNode) -> Option { }) } -fn count_columns_for_path(binder: &Binder, root: &SyntaxNode, path: &ast::Path) -> Option { +fn count_columns_for_path(db: &dyn Db, file: File, path: &ast::Path) -> Option { let (table_name, schema) = extract_table_schema_from_path(path)?; let position = path.syntax().text_range().start(); - count_columns_for_table_name(binder, root, &table_name, &schema, position) + count_columns_for_table_name(db, file, &table_name, &schema, position) } fn count_columns_for_table_name( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, table_name: &Name, schema: &Option, position: TextSize, ) -> Option { - if let Some(table_name_ptr) = resolve_table_name_ptr(binder, table_name, schema, position) { + let tree = parse(db, file).tree(); + let root = tree.syntax(); + if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, table_name, schema, position) { let table_name_node = table_name_ptr.to_node(root); if let Some(create_table) = table_name_node @@ -2369,7 +2434,7 @@ fn count_columns_for_table_name( } } - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, table_name, schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, table_name, schema, position) { let view_name_node = view_name_ptr.to_node(root); if let Some(create_view) = view_name_node.ancestors().find_map(ast::CreateView::cast) { @@ -2418,8 +2483,8 @@ fn leftmost_select_from_variant(select_variant: ast::SelectVariant) -> Option Option { @@ -2601,7 +2666,7 @@ fn column_in_with_query( let mut column_index: usize = 0; for target in target_list.targets() { let target_column_count = if target.star_token().is_some() { - count_columns_for_path(binder, root, &path).unwrap_or(1) + count_columns_for_path(db, file, &path).unwrap_or(1) } else { 1 }; @@ -2619,7 +2684,7 @@ fn column_in_with_query( return Some(SyntaxNodePtr::new(&node)); } if matches!(col_name, ColumnName::Star) - && let Some(ptr) = resolve_column_for_path(binder, root, &path, column_name.clone()) + && let Some(ptr) = resolve_column_for_path(db, file, &path, column_name.clone()) { return Some(ptr); } @@ -2631,8 +2696,8 @@ fn column_in_with_query( } fn resolve_subquery_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, paren_select: &ast::ParenSelect, name_ref: &ast::NameRef, column_name: &Name, @@ -2665,14 +2730,14 @@ fn resolve_subquery_column_ptr( if schema.is_none() && let Some(cte_column_ptr) = - resolve_cte_column(binder, root, name_ref, &table_name, column_name) + resolve_cte_column(db, file, name_ref, &table_name, column_name) { return Some(cte_column_ptr); } return resolve_column_from_table_or_view( - binder, - root, + db, + file, name_ref, &table_name, &schema, @@ -2695,8 +2760,8 @@ fn resolve_subquery_column_ptr( let subquery_select = leftmost_select_from_variant(select_variant)?; resolve_column_from_select_targets( - binder, - root, + db, + file, &subquery_select, name_ref, column_name, @@ -2705,8 +2770,8 @@ fn resolve_subquery_column_ptr( } fn resolve_column_from_select_targets( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, select: &ast::Select, name_ref: &ast::NameRef, column_name: &Name, @@ -2731,7 +2796,7 @@ fn resolve_column_from_select_targets( { for from_item in from_clause.from_items() { if let Some(result) = - resolve_from_item_column_ptr(binder, root, &from_item, name_ref) + resolve_from_item_column_ptr(db, file, &from_item, name_ref) { return Some(result); } @@ -2740,7 +2805,7 @@ fn resolve_column_from_select_targets( for join_expr in from_clause.join_exprs() { if let Some(result) = resolve_from_join_expr(&join_expr, &|from_item: &ast::FromItem| { - resolve_from_item_column_ptr(binder, root, from_item, name_ref) + resolve_from_item_column_ptr(db, file, from_item, name_ref) }) { return Some(result); @@ -2754,7 +2819,7 @@ fn resolve_column_from_select_targets( && let Some(table_name) = qualified_star_table_name(&field_expr) && let Some(from_clause) = &from_clause && let Some(from_item) = find_from_item_in_from_clause(from_clause, &table_name) - && let Some(result) = resolve_from_item_column_ptr(binder, root, &from_item, name_ref) + && let Some(result) = resolve_from_item_column_ptr(db, file, &from_item, name_ref) { return Some(result); } @@ -2777,10 +2842,11 @@ fn qualified_star_table_name(field_expr: &ast::FieldExpr) -> Option { } pub(crate) fn resolve_qualified_star_table_ptr( - binder: &Binder, - field_expr: &ast::FieldExpr, + db: &dyn Db, + file: File, + field_expr: ast::FieldExpr, ) -> Option { - let table_name = qualified_star_table_name(field_expr)?; + let table_name = qualified_star_table_name(&field_expr)?; let position = field_expr.syntax().text_range().start(); for ancestor in field_expr.syntax().ancestors() { @@ -2797,13 +2863,13 @@ pub(crate) fn resolve_qualified_star_table_ptr( let (table_name, schema) = table_and_schema_from_from_item(&from_item)?; if let Some(table_name_ptr) = - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) { return Some(table_name_ptr); } if let Some(view_name_ptr) = - resolve_view_name_ptr(binder, &table_name, &schema, position) + resolve_view_name_ptr(db, file, &table_name, &schema, position) { return Some(view_name_ptr); } @@ -2819,28 +2885,28 @@ pub(crate) fn resolve_qualified_star_table_ptr( if let Some(update) = ast::Update::cast(ancestor.clone()) { let path = update.relation_name()?.path()?; - return resolve_table_or_view_or_cte_ptrs(binder, position, &path)? + return resolve_table_or_view_or_cte_ptrs(db, file, position, &path)? .into_iter() .next(); } if let Some(insert) = ast::Insert::cast(ancestor.clone()) { let path = insert.path()?; - return resolve_table_or_view_or_cte_ptrs(binder, position, &path)? + return resolve_table_or_view_or_cte_ptrs(db, file, position, &path)? .into_iter() .next(); } if let Some(delete) = ast::Delete::cast(ancestor.clone()) { let path = delete.relation_name()?.path()?; - return resolve_table_or_view_or_cte_ptrs(binder, position, &path)? + return resolve_table_or_view_or_cte_ptrs(db, file, position, &path)? .into_iter() .next(); } if let Some(merge) = ast::Merge::cast(ancestor) { let path = merge.relation_name()?.path()?; - return resolve_table_or_view_or_cte_ptrs(binder, position, &path)? + return resolve_table_or_view_or_cte_ptrs(db, file, position, &path)? .into_iter() .next(); } @@ -2850,7 +2916,8 @@ pub(crate) fn resolve_qualified_star_table_ptr( } fn resolve_table_or_view_or_cte_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, position: TextSize, path: &ast::Path, ) -> Option> { @@ -2858,11 +2925,11 @@ fn resolve_table_or_view_or_cte_ptrs( let mut results = vec![]; - if let Some(table_name_ptr) = resolve_table_name_ptr(binder, &table_name, &schema, position) { + if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, &table_name, &schema, position) { results.push(table_name_ptr); } - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, &table_name, &schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, &table_name, &schema, position) { results.push(view_name_ptr); } @@ -2882,43 +2949,48 @@ fn resolve_table_or_view_or_cte_ptrs( } fn resolve_table_from_update_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, position: TextSize, update: &ast::Update, ) -> Option> { let path = update.relation_name()?.path()?; - resolve_table_or_view_or_cte_ptrs(binder, position, &path) + resolve_table_or_view_or_cte_ptrs(db, file, position, &path) } fn resolve_table_from_insert_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, position: TextSize, insert: &ast::Insert, ) -> Option> { let path = insert.path()?; - resolve_table_or_view_or_cte_ptrs(binder, position, &path) + resolve_table_or_view_or_cte_ptrs(db, file, position, &path) } fn resolve_table_from_delete_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, position: TextSize, delete: &ast::Delete, ) -> Option> { let path = delete.relation_name()?.path()?; - resolve_table_or_view_or_cte_ptrs(binder, position, &path) + resolve_table_or_view_or_cte_ptrs(db, file, position, &path) } fn resolve_table_from_merge_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, position: TextSize, merge: &ast::Merge, ) -> Option> { let path = merge.relation_name()?.path()?; - resolve_table_or_view_or_cte_ptrs(binder, position, &path) + resolve_table_or_view_or_cte_ptrs(db, file, position, &path) } pub(crate) fn resolve_unqualified_star_table_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, target: &ast::Target, ) -> Option> { target.star_token()?; @@ -2928,7 +3000,7 @@ pub(crate) fn resolve_unqualified_star_table_ptrs( for ancestor in target.syntax().ancestors() { if let Some(select) = ast::Select::cast(ancestor.clone()) { let from_clause = select.from_clause()?; - let results = table_ptrs_from_clause(binder, &from_clause); + let results = table_ptrs_from_clause(db, file, &from_clause); if results.is_empty() { return None; } @@ -2937,19 +3009,19 @@ pub(crate) fn resolve_unqualified_star_table_ptrs( } if let Some(update) = ast::Update::cast(ancestor.clone()) { - return resolve_table_from_update_ptrs(binder, position, &update); + return resolve_table_from_update_ptrs(db, file, position, &update); } if let Some(insert) = ast::Insert::cast(ancestor.clone()) { - return resolve_table_from_insert_ptrs(binder, position, &insert); + return resolve_table_from_insert_ptrs(db, file, position, &insert); } if let Some(delete) = ast::Delete::cast(ancestor.clone()) { - return resolve_table_from_delete_ptrs(binder, position, &delete); + return resolve_table_from_delete_ptrs(db, file, position, &delete); } if let Some(merge) = ast::Merge::cast(ancestor) { - return resolve_table_from_merge_ptrs(binder, position, &merge); + return resolve_table_from_merge_ptrs(db, file, position, &merge); } } @@ -2957,12 +3029,13 @@ pub(crate) fn resolve_unqualified_star_table_ptrs( } pub(crate) fn resolve_unqualified_star_in_arg_list_ptrs( - binder: &Binder, + db: &dyn Db, + file: File, arg_list: &ast::ArgList, ) -> Option> { let select = arg_list.syntax().ancestors().find_map(ast::Select::cast)?; let from_clause = select.from_clause()?; - let results = table_ptrs_from_clause(binder, &from_clause); + let results = table_ptrs_from_clause(db, file, &from_clause); if results.is_empty() { return None; @@ -2971,25 +3044,29 @@ pub(crate) fn resolve_unqualified_star_in_arg_list_ptrs( Some(results) } +// TODO: I don't think we should be passing in file here and using it, we want +// to use the content from the updated file with the completion marker pub(crate) fn table_ptrs_from_clause( - binder: &Binder, + db: &dyn Db, + file: File, from_clause: &ast::FromClause, ) -> Vec { let mut results = vec![]; for from_item in from_clause.from_items() { - collect_tables_from_item(binder, &from_item, &mut results); + collect_tables_from_item(db, file, &from_item, &mut results); } for join_expr in from_clause.join_exprs() { - collect_table_ptrs_from_join_expr(binder, &join_expr, &mut results); + collect_table_ptrs_from_join_expr(db, file, &join_expr, &mut results); } results } pub(crate) fn table_ptr_from_from_item( - binder: &Binder, + db: &dyn Db, + file: File, from_item: &ast::FromItem, ) -> Option { if let Some(paren_select) = from_item.paren_select() { @@ -2997,17 +3074,17 @@ pub(crate) fn table_ptr_from_from_item( } if let Some(paren_expr) = from_item.paren_expr() { - return table_ptr_from_paren_expr(binder, &paren_expr); + return table_ptr_from_paren_expr(db, file, &paren_expr); } let (table_name, schema) = table_and_schema_from_from_item(from_item)?; let position = from_item.syntax().text_range().start(); - if let Some(table_ptr) = resolve_table_name_ptr(binder, &table_name, &schema, position) { + if let Some(table_ptr) = resolve_table_name_ptr(db, file, &table_name, &schema, position) { return Some(table_ptr); } - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, &table_name, &schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, &table_name, &schema, position) { return Some(view_name_ptr); } @@ -3022,40 +3099,43 @@ pub(crate) fn table_ptr_from_from_item( } fn table_ptr_from_paren_expr( - binder: &Binder, + db: &dyn Db, + file: File, paren_expr: &ast::ParenExpr, ) -> Option { if let Some(from_item) = paren_expr.from_item() { - return table_ptr_from_from_item(binder, &from_item); + return table_ptr_from_from_item(db, file, &from_item); } if let Some(ast::Expr::ParenExpr(inner)) = paren_expr.expr() { - return table_ptr_from_paren_expr(binder, &inner); + return table_ptr_from_paren_expr(db, file, &inner); } None } fn collect_table_ptrs_from_join_expr( - binder: &Binder, + db: &dyn Db, + file: File, join_expr: &ast::JoinExpr, results: &mut Vec, ) { if let Some(nested) = join_expr.join_expr() { - collect_table_ptrs_from_join_expr(binder, &nested, results); + collect_table_ptrs_from_join_expr(db, file, &nested, results); } if let Some(from_item) = join_expr.from_item() { - collect_tables_from_item(binder, &from_item, results); + collect_tables_from_item(db, file, &from_item, results); } if let Some(join) = join_expr.join() && let Some(from_item) = join.from_item() { - collect_tables_from_item(binder, &from_item, results); + collect_tables_from_item(db, file, &from_item, results); } } fn collect_tables_from_item( - binder: &Binder, + db: &dyn Db, + file: File, from_item: &ast::FromItem, results: &mut Vec, ) { @@ -3076,12 +3156,12 @@ fn collect_tables_from_item( }; let position = from_item.syntax().text_range().start(); - if let Some(table_name_ptr) = resolve_table_name_ptr(binder, &table_name, &schema, position) { + if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, &table_name, &schema, position) { results.push(table_name_ptr); return; } - if let Some(view_name_ptr) = resolve_view_name_ptr(binder, &table_name, &schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, &table_name, &schema, position) { results.push(view_name_ptr); return; } @@ -3153,14 +3233,14 @@ pub(crate) fn select_from_with_query(query: ast::WithQuery) -> Option Option { if target.star_token().is_some() { - return count_columns_for_from_clause(binder, root, name_ref, from_clause); + return count_columns_for_from_clause(db, file, name_ref, from_clause); } if let Some(expr) = target.expr() @@ -3168,15 +3248,15 @@ fn count_columns_for_target( && let Some(table_name) = qualified_star_table_name(&field_expr) && let Some(from_item) = find_from_item_in_from_clause(from_clause, &table_name) { - return count_columns_for_from_item(binder, root, name_ref, &from_item); + return count_columns_for_from_item(db, file, name_ref, &from_item); } Some(1) } fn count_columns_for_from_clause( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, from_clause: &ast::FromClause, ) -> Option { @@ -3184,14 +3264,14 @@ fn count_columns_for_from_clause( let mut found = false; for from_item in from_clause.from_items() { - if let Some(count) = count_columns_for_from_item(binder, root, name_ref, &from_item) { + if let Some(count) = count_columns_for_from_item(db, file, name_ref, &from_item) { total = total.saturating_add(count); found = true; } } for join_expr in from_clause.join_exprs() { - if let Some(count) = count_columns_for_join_expr(binder, root, name_ref, &join_expr) { + if let Some(count) = count_columns_for_join_expr(db, file, name_ref, &join_expr) { total = total.saturating_add(count); found = true; } @@ -3201,8 +3281,8 @@ fn count_columns_for_from_clause( } fn count_columns_for_join_expr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, join_expr: &ast::JoinExpr, ) -> Option { @@ -3210,14 +3290,14 @@ fn count_columns_for_join_expr( let mut found = false; if let Some(nested) = join_expr.join_expr() - && let Some(count) = count_columns_for_join_expr(binder, root, name_ref, &nested) + && let Some(count) = count_columns_for_join_expr(db, file, name_ref, &nested) { total = total.saturating_add(count); found = true; } if let Some(from_item) = join_expr.from_item() - && let Some(count) = count_columns_for_from_item(binder, root, name_ref, &from_item) + && let Some(count) = count_columns_for_from_item(db, file, name_ref, &from_item) { total = total.saturating_add(count); found = true; @@ -3225,7 +3305,7 @@ fn count_columns_for_join_expr( if let Some(join) = join_expr.join() && let Some(from_item) = join.from_item() - && let Some(count) = count_columns_for_from_item(binder, root, name_ref, &from_item) + && let Some(count) = count_columns_for_from_item(db, file, name_ref, &from_item) { total = total.saturating_add(count); found = true; @@ -3235,16 +3315,15 @@ fn count_columns_for_join_expr( } fn count_columns_for_from_item( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, from_item: &ast::FromItem, ) -> Option { let (table_name, schema) = table_and_schema_from_from_item(from_item)?; let position = name_ref.syntax().text_range().start(); - if let Some(count) = count_columns_for_table_name(binder, root, &table_name, &schema, position) - { + if let Some(count) = count_columns_for_table_name(db, file, &table_name, &schema, position) { return Some(count); } @@ -3301,29 +3380,24 @@ fn count_columns_for_cte(name_ref: &ast::NameRef, cte_name: &Name) -> Option Option { for from_item in from_clause.from_items() { - if let Some(result) = resolve_from_item_for_cte_star( - binder, - root, - &from_item, - name_ref, - cte_name, - column_name, - ) { + if let Some(result) = + resolve_from_item_for_cte_star(db, file, &from_item, name_ref, cte_name, column_name) + { return Some(result); } } for join_expr in from_clause.join_exprs() { if let Some(result) = resolve_from_join_expr(&join_expr, &|from_item: &ast::FromItem| { - resolve_from_item_for_cte_star(binder, root, from_item, name_ref, cte_name, column_name) + resolve_from_item_for_cte_star(db, file, from_item, name_ref, cte_name, column_name) }) { return Some(result); } @@ -3333,8 +3407,8 @@ fn resolve_from_clause_for_cte_star( } fn resolve_qualified_star_in_from_clause( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, name_ref: &ast::NameRef, cte_name: &Name, column_name: &Name, @@ -3342,24 +3416,24 @@ fn resolve_qualified_star_in_from_clause( table_name: &Name, ) -> Option { let from_item = find_from_item_in_from_clause(from_clause, table_name)?; - resolve_from_item_for_cte_star(binder, root, &from_item, name_ref, cte_name, column_name) + resolve_from_item_for_cte_star(db, file, &from_item, name_ref, cte_name, column_name) } fn resolve_column_from_paren_expr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, paren_expr: &ast::ParenExpr, name_ref: &ast::NameRef, column_name: &Name, ) -> Option { if let Some(select) = paren_expr.select() { - return resolve_column_from_select_targets(binder, root, &select, name_ref, column_name, 0); + return resolve_column_from_select_targets(db, file, &select, name_ref, column_name, 0); } if let Some(ast::Expr::CallExpr(call_expr)) = paren_expr.expr() && let Some(ptr) = resolve_column_from_call_expr_return_table( - binder, - root, + db, + file, &call_expr, name_ref, column_name, @@ -3370,15 +3444,15 @@ fn resolve_column_from_paren_expr( } if let Some(ast::Expr::ParenExpr(paren_expr)) = paren_expr.expr() { - return resolve_column_from_paren_expr(binder, root, &paren_expr, name_ref, column_name); + return resolve_column_from_paren_expr(db, file, &paren_expr, name_ref, column_name); } if let Some(from_item) = paren_expr.from_item() { if let Some(paren_select) = from_item.paren_select() { let alias = from_item.alias(); return resolve_subquery_column_ptr( - binder, - root, + db, + file, &paren_select, name_ref, column_name, @@ -3392,20 +3466,13 @@ fn resolve_column_from_paren_expr( .find_map(ast::SelectVariant::cast) { let select = leftmost_select_from_variant(select_variant)?; - return resolve_column_from_select_targets( - binder, - root, - &select, - name_ref, - column_name, - 0, - ); + return resolve_column_from_select_targets(db, file, &select, name_ref, column_name, 0); } if let Some(nested_paren_expr) = from_item.paren_expr() { return resolve_column_from_paren_expr( - binder, - root, + db, + file, &nested_paren_expr, name_ref, column_name, @@ -3417,25 +3484,26 @@ fn resolve_column_from_paren_expr( } fn resolve_column_from_call_expr_return_table( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, call_expr: &ast::CallExpr, name_ref: &ast::NameRef, column_name: &Name, min_index: usize, ) -> Option { let position = name_ref.syntax().text_range().start(); - let function_ptr = resolve_function_ptr_from_call_expr(binder, call_expr, position)?; - find_column_in_function_return_table_min_index(root, function_ptr, column_name, min_index) + let function_ptr = resolve_function_ptr_from_call_expr(db, file, call_expr, position)?; + find_column_in_function_return_table_min_index(db, file, function_ptr, column_name, min_index) } fn resolve_function_ptr_from_call_expr( - binder: &Binder, + db: &dyn Db, + file: File, call_expr: &ast::CallExpr, position: TextSize, ) -> Option { let (function_name, schema) = function_name_and_schema_from_call_expr(call_expr)?; - resolve_function(binder, &function_name, &schema, None, position) + resolve_function(db, file, &function_name, &schema, None, position) } fn function_name_and_schema_from_call_expr( @@ -3455,59 +3523,82 @@ fn function_name_and_schema_from_call_expr( } } -pub(crate) fn resolve_table_info(binder: &Binder, path: &ast::Path) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Table) +pub(crate) fn resolve_table_info( + db: &dyn Db, + file: File, + path: &ast::Path, +) -> Option<(Schema, String)> { + resolve_symbol_info(db, file, path, SymbolKind::Table) } -pub(crate) fn resolve_function_info(binder: &Binder, path: &ast::Path) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Function) +pub(crate) fn resolve_function_info( + db: &dyn Db, + file: File, + path: &ast::Path, +) -> Option<(Schema, String)> { + resolve_symbol_info(db, file, path, SymbolKind::Function) } pub(crate) fn resolve_aggregate_info( - binder: &Binder, + db: &dyn Db, + file: File, path: &ast::Path, ) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Aggregate) + resolve_symbol_info(db, file, path, SymbolKind::Aggregate) } pub(crate) fn resolve_procedure_info( - binder: &Binder, + db: &dyn Db, + file: File, path: &ast::Path, ) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Procedure) + resolve_symbol_info(db, file, path, SymbolKind::Procedure) } -pub(crate) fn resolve_type_info(binder: &Binder, path: &ast::Path) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Type) +pub(crate) fn resolve_type_info( + db: &dyn Db, + file: File, + path: &ast::Path, +) -> Option<(Schema, String)> { + resolve_symbol_info(db, file, path, SymbolKind::Type) } pub(crate) fn resolve_property_graph_info( - binder: &Binder, + db: &dyn Db, + file: File, path: &ast::Path, ) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::PropertyGraph) + resolve_symbol_info(db, file, path, SymbolKind::PropertyGraph) } -pub(crate) fn resolve_view_info(binder: &Binder, path: &ast::Path) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::View) +pub(crate) fn resolve_view_info( + db: &dyn Db, + file: File, + path: &ast::Path, +) -> Option<(Schema, String)> { + resolve_symbol_info(db, file, path, SymbolKind::View) } -pub(crate) fn resolve_sequence_info(binder: &Binder, path: &ast::Path) -> Option<(Schema, String)> { - resolve_symbol_info(binder, path, SymbolKind::Sequence) +pub(crate) fn resolve_sequence_info( + db: &dyn Db, + file: File, + path: &ast::Path, +) -> Option<(Schema, String)> { + resolve_symbol_info(db, file, path, SymbolKind::Sequence) } pub(crate) fn collect_table_columns( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &impl ast::HasCreateTable, ) -> Vec { - collect_table_columns_impl(binder, root, create_table, 0) + collect_table_columns_impl(db, file, create_table, 0) } // TODO: combine with find_column_in_create_table_impl fn collect_table_columns_impl( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, create_table: &impl ast::HasCreateTable, depth: usize, ) -> Vec { @@ -3523,10 +3614,10 @@ fn collect_table_columns_impl( if let Some((table_name, schema)) = extract_table_schema_from_path(&path) { let position = path.syntax().text_range().start(); if let Some(ResolvedTableName::Table(parent_table)) = - resolve_table_name(binder, root, &table_name, &schema, position) + resolve_table_name(db, file, &table_name, &schema, position) { let inherited_columns = - collect_table_columns_impl(binder, root, &parent_table, depth + 1); + collect_table_columns_impl(db, file, &parent_table, depth + 1); columns.extend(inherited_columns); } } @@ -3545,10 +3636,10 @@ fn collect_table_columns_impl( { let position = path.syntax().text_range().start(); if let Some(ResolvedTableName::Table(source_table)) = - resolve_table_name(binder, root, &table_name, &schema, position) + resolve_table_name(db, file, &table_name, &schema, position) { let like_columns = - collect_table_columns_impl(binder, root, &source_table, depth + 1); + collect_table_columns_impl(db, file, &source_table, depth + 1); columns.extend(like_columns); } } @@ -3599,7 +3690,7 @@ pub(crate) fn collect_view_columns_with_types( } pub(crate) fn collect_materialized_view_column_names( - create_materialized_view: &ast::CreateMaterializedView, + create_materialized_view: ast::CreateMaterializedView, ) -> Vec { if let Some(column_list) = create_materialized_view.column_list() { let columns = collect_column_names_from_column_list(&column_list); @@ -3622,7 +3713,7 @@ pub(crate) fn collect_materialized_view_column_names( } pub(crate) fn collect_materialized_view_columns_with_types( - create_materialized_view: &ast::CreateMaterializedView, + create_materialized_view: ast::CreateMaterializedView, ) -> Vec<(Name, Option)> { let Some(select) = select_from_materialized_view_query(create_materialized_view) else { return vec![]; @@ -3638,7 +3729,7 @@ pub(crate) fn collect_materialized_view_columns_with_types( } fn select_from_materialized_view_query( - create_materialized_view: &ast::CreateMaterializedView, + create_materialized_view: ast::CreateMaterializedView, ) -> Option { let query = create_materialized_view.query()?; match query { @@ -3648,21 +3739,23 @@ fn select_from_materialized_view_query( } pub(crate) fn collect_with_table_column_names( - binder: &Binder, - root: &SyntaxNode, - with_table: &ast::WithTable, + db: &dyn Db, + file: File, + with_table: ast::WithTable, ) -> Vec { - collect_with_table_columns_with_types(binder, root, with_table) + collect_with_table_columns_with_types(db, file, with_table) .into_iter() .map(|(name, _)| name) .collect() } fn resolve_symbol_info( - binder: &Binder, + db: &dyn Db, + file: File, path: &ast::Path, kind: SymbolKind, ) -> Option<(Schema, String)> { + let binder = bind(db, file); let name_str = extract_table_name_from_path(path)?; let schema = extract_schema_from_path(path); let position = path.syntax().text_range().start(); @@ -3692,12 +3785,10 @@ fn collect_target_list_column_names(target_list: &ast::TargetList) -> Vec } pub(crate) fn collect_with_table_columns_with_types( - binder: &Binder, - root: &SyntaxNode, - with_table: &ast::WithTable, + db: &dyn Db, + file: File, + with_table: ast::WithTable, ) -> Vec<(Name, Option)> { - let base_columns = collect_with_table_query_columns_with_types(binder, root, with_table); - let alias_columns: Vec = with_table .column_list() .into_iter() @@ -3705,6 +3796,8 @@ pub(crate) fn collect_with_table_columns_with_types( .filter_map(|column| column.name().map(|name| Name::from_node(&name))) .collect(); + let base_columns = collect_with_table_query_columns_with_types(db, file, with_table); + if alias_columns.is_empty() { return base_columns; } @@ -3724,9 +3817,9 @@ pub(crate) fn collect_with_table_columns_with_types( } fn collect_with_table_query_columns_with_types( - binder: &Binder, - root: &SyntaxNode, - with_table: &ast::WithTable, + db: &dyn Db, + file: File, + with_table: ast::WithTable, ) -> Vec<(Name, Option)> { let Some(query) = with_table.query() else { return vec![]; @@ -3774,11 +3867,7 @@ fn collect_with_table_query_columns_with_types( if target.star_token().is_some() && let Some(from_clause) = &from_clause { - columns.extend(collect_columns_for_star_from_clause( - binder, - root, - from_clause, - )); + columns.extend(collect_columns_for_star_from_clause(db, file, from_clause)); continue; } } @@ -3790,7 +3879,7 @@ fn collect_with_table_query_columns_with_types( && let Some(from_item) = find_from_item_in_from_clause(from_clause, &table_name) { columns.extend(collect_columns_for_star_from_from_item( - binder, root, &from_item, + db, file, &from_item, )); } } @@ -3824,22 +3913,22 @@ fn columns_from_returning_clause_with_types( } fn collect_columns_for_star_from_clause( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_clause: &ast::FromClause, ) -> Vec<(Name, Option)> { let mut columns = vec![]; for from_item in from_clause.from_items() { columns.extend(collect_columns_for_star_from_from_item( - binder, root, &from_item, + db, file, &from_item, )); } for join_expr in from_clause.join_exprs() { if let Some(from_item) = join_expr.from_item() { columns.extend(collect_columns_for_star_from_from_item( - binder, root, &from_item, + db, file, &from_item, )); } @@ -3847,7 +3936,7 @@ fn collect_columns_for_star_from_clause( && let Some(from_item) = join.from_item() { columns.extend(collect_columns_for_star_from_from_item( - binder, root, &from_item, + db, file, &from_item, )); } } @@ -3856,26 +3945,26 @@ fn collect_columns_for_star_from_clause( } fn collect_columns_for_star_from_from_item( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_item: &ast::FromItem, ) -> Vec<(Name, Option)> { if let Some(alias) = from_item.alias() && alias.column_list().is_some() { - return collect_columns_for_star_from_alias(binder, root, from_item, &alias); + return collect_columns_for_star_from_alias(db, file, from_item, &alias); } - let Some(table_ptr) = table_ptr_from_from_item(binder, from_item) else { + let Some(table_ptr) = table_ptr_from_from_item(db, file, from_item) else { return vec![]; }; - collect_columns_for_star_from_table_ptr(binder, root, &table_ptr) + collect_columns_for_star_from_table_ptr(db, file, &table_ptr) } fn collect_columns_for_star_from_alias( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, from_item: &ast::FromItem, alias: &ast::Alias, ) -> Vec<(Name, Option)> { @@ -3886,11 +3975,11 @@ fn collect_columns_for_star_from_alias( .filter_map(|column| column.name().map(|name| Name::from_node(&name))) .collect(); - let Some(table_ptr) = table_ptr_from_from_item(binder, from_item) else { + let Some(table_ptr) = table_ptr_from_from_item(db, file, from_item) else { return vec![]; }; - let base_columns = collect_columns_for_star_from_table_ptr(binder, root, &table_ptr); + let base_columns = collect_columns_for_star_from_table_ptr(db, file, &table_ptr); let mut results = vec![]; for (idx, alias_name) in alias_columns.iter().enumerate() { @@ -3906,14 +3995,16 @@ fn collect_columns_for_star_from_alias( } fn collect_columns_for_star_from_table_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, table_ptr: &SyntaxNodePtr, ) -> Vec<(Name, Option)> { + let tree = parse(db, file).tree(); + let root = tree.syntax(); let table_node = table_ptr.to_node(root); if let Some(paren_select) = ast::ParenSelect::cast(table_node.clone()) { - return collect_paren_select_columns_with_types(binder, root, &paren_select); + return collect_paren_select_columns_with_types(db, file, &paren_select); } match find_table_source(&table_node) { @@ -3921,13 +4012,13 @@ fn collect_columns_for_star_from_table_ptr( let Some(from_item) = alias.syntax().ancestors().find_map(ast::FromItem::cast) else { return vec![]; }; - collect_columns_for_star_from_alias(binder, root, &from_item, &alias) + collect_columns_for_star_from_alias(db, file, &from_item, &alias) } Some(TableSource::WithTable(with_table)) => { - collect_with_table_columns_with_types(binder, root, &with_table) + collect_with_table_columns_with_types(db, file, with_table) } Some(TableSource::CreateTable(create_table)) => { - collect_table_columns(binder, root, &create_table) + collect_table_columns(db, file, &create_table) .into_iter() .filter_map(|column| { let name = Name::from_node(&column.name()?); @@ -3938,10 +4029,10 @@ fn collect_columns_for_star_from_table_ptr( } Some(TableSource::CreateView(create_view)) => collect_view_columns_with_types(&create_view), Some(TableSource::CreateMaterializedView(create_materialized_view)) => { - collect_materialized_view_columns_with_types(&create_materialized_view) + collect_materialized_view_columns_with_types(create_materialized_view) } Some(TableSource::ParenSelect(paren_select)) => { - collect_paren_select_columns_with_types(binder, root, &paren_select) + collect_paren_select_columns_with_types(db, file, &paren_select) } None => vec![], } @@ -3963,21 +4054,24 @@ fn collect_target_list_columns_with_types( } pub(crate) fn collect_paren_select_columns_with_types( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, paren_select: &ast::ParenSelect, ) -> Vec<(Name, Option)> { let Some(select_variant) = paren_select.select() else { return vec![]; }; - collect_select_variant_columns_with_types(binder, root, &select_variant) + collect_select_variant_columns_with_types(db, file, &select_variant) } fn collect_select_variant_columns_with_types( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, select_variant: &ast::SelectVariant, ) -> Vec<(Name, Option)> { + let binder = bind(db, file); + let tree = parse(db, file).tree(); + let root = tree.syntax(); match select_variant { ast::SelectVariant::Values(values) => { let mut results = vec![]; @@ -4011,13 +4105,13 @@ fn collect_select_variant_columns_with_types( collect_target_list_columns_with_types(&target_list) } ast::SelectVariant::ParenSelect(nested) => { - collect_paren_select_columns_with_types(binder, root, nested) + collect_paren_select_columns_with_types(db, file, nested) } ast::SelectVariant::CompoundSelect(compound) => { let Some(lhs) = compound.lhs() else { return vec![]; }; - collect_select_variant_columns_with_types(binder, root, &lhs) + collect_select_variant_columns_with_types(db, file, &lhs) } ast::SelectVariant::Table(table) => { let Some(path) = table.relation_name().and_then(|r| r.path()) else { @@ -4035,7 +4129,7 @@ fn collect_select_variant_columns_with_types( .ancestors() .find_map(ast::WithTable::cast) { - return collect_with_table_column_names(binder, root, &with_table) + return collect_with_table_column_names(db, file, with_table) .into_iter() .map(|name| (name, None)) .collect(); @@ -4054,7 +4148,7 @@ fn collect_select_variant_columns_with_types( else { return vec![]; }; - let columns = collect_table_columns(binder, root, &create_table); + let columns = collect_table_columns(db, file, &create_table); columns .into_iter() .filter_map(|col| { @@ -4132,10 +4226,12 @@ fn unwrap_paren_expr_to_name_ref(expr: ast::Expr) -> Option { } fn resolve_composite_type_field_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, field_name_ref: &ast::NameRef, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); let field_name = Name::from_node(field_name_ref); let field_expr = field_name_ref .syntax() @@ -4145,14 +4241,14 @@ fn resolve_composite_type_field_ptr( if let ast::Expr::ParenExpr(ref paren_expr) = base && let Some(ptr) = - resolve_column_from_paren_expr(binder, root, paren_expr, field_name_ref, &field_name) + resolve_column_from_paren_expr(db, file, paren_expr, field_name_ref, &field_name) { return Some(ptr); } let base_name_ref = unwrap_paren_expr_to_name_ref(base.clone())?; - let column_ptr = resolve_select_column_ptr(binder, root, &base_name_ref)?; + let column_ptr = resolve_select_column_ptr(db, file, &base_name_ref)?; let column_node = column_ptr.to_node(root); let (type_name, schema) = @@ -4163,7 +4259,7 @@ fn resolve_composite_type_field_ptr( }; let position = field_name_ref.syntax().text_range().start(); - let type_name_ptr = resolve_type_name_ptr(binder, &type_name, &schema, position)?; + let type_name_ptr = resolve_type_name_ptr(db, file, &type_name, &schema, position)?; let type_node = type_name_ptr.to_node(root); let create_type = type_node.ancestors().find_map(ast::CreateType::cast)?; @@ -4224,8 +4320,8 @@ fn extract_type_name_and_schema(ty: &ast::Type) -> Option<(Name, Option) } fn resolve_merge_column_ptr( - binder: &Binder, - root: &SyntaxNode, + db: &dyn Db, + file: File, column_name_ref: &ast::NameRef, ) -> Option { let column_name = Name::from_node(column_name_ref); @@ -4237,19 +4333,20 @@ fn resolve_merge_column_ptr( // Try resolving in source table first if let Some(using_on) = merge.using_on_clause() && let Some(from_item) = using_on.from_item() - && let Some(ptr) = resolve_from_item_column_ptr(binder, root, &from_item, column_name_ref) + && let Some(ptr) = resolve_from_item_column_ptr(db, file, &from_item, column_name_ref) { return Some(ptr); } let path = merge.relation_name()?.path()?; - resolve_column_for_path(binder, root, &path, column_name) + resolve_column_for_path(db, file, &path, column_name) } // TODO: I think we could use trait(s) here to simplify this and have the // callers pass in the stmt instead of the fields. fn resolve_table_in_returning_clause( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, alias: Option, path: &ast::Path, @@ -4269,16 +4366,17 @@ fn resolve_table_in_returning_clause( Some(SyntaxNodePtr::new(alias_name.syntax())) } ReturningClauseMatch::PseudoTable => { - resolve_table_name_ptr(binder, &stmt_table_name, &schema, position) + resolve_table_name_ptr(db, file, &stmt_table_name, &schema, position) } ReturningClauseMatch::Table => { - resolve_table_name_ptr(binder, &table_name, &schema, position) + resolve_table_name_ptr(db, file, &table_name, &schema, position) } } } fn resolve_insert_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, ) -> Option { let insert = table_name_ref @@ -4287,7 +4385,8 @@ fn resolve_insert_table_name_ptr( .find_map(ast::Insert::cast)?; let path = insert.path()?; resolve_table_in_returning_clause( - binder, + db, + file, table_name_ref, insert.alias(), &path, @@ -4296,7 +4395,8 @@ fn resolve_insert_table_name_ptr( } fn resolve_delete_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, ) -> Option { let delete = table_name_ref @@ -4305,7 +4405,8 @@ fn resolve_delete_table_name_ptr( .find_map(ast::Delete::cast)?; let path = delete.relation_name()?.path()?; resolve_table_in_returning_clause( - binder, + db, + file, table_name_ref, delete.alias(), &path, @@ -4314,7 +4415,8 @@ fn resolve_delete_table_name_ptr( } fn resolve_update_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, ) -> Option { let update = table_name_ref @@ -4323,7 +4425,8 @@ fn resolve_update_table_name_ptr( .find_map(ast::Update::cast)?; let path = update.relation_name()?.path()?; resolve_table_in_returning_clause( - binder, + db, + file, table_name_ref, update.alias(), &path, @@ -4332,7 +4435,8 @@ fn resolve_update_table_name_ptr( } fn resolve_merge_table_name_ptr( - binder: &Binder, + db: &dyn Db, + file: File, table_name_ref: &ast::NameRef, ) -> Option { let table_name = Name::from_node(table_name_ref); @@ -4354,7 +4458,7 @@ fn resolve_merge_table_name_ptr( return Some(cte_ptr); } let position = table_name_ref.syntax().text_range().start(); - return resolve_table_name_ptr(binder, &item_name, &None, position); + return resolve_table_name_ptr(db, file, &item_name, &None, position); } } // Check for aliased source tables @@ -4367,7 +4471,8 @@ fn resolve_merge_table_name_ptr( } resolve_table_in_returning_clause( - binder, + db, + file, table_name_ref, merge.alias(), &path, @@ -4409,10 +4514,13 @@ fn find_func_call_from_named_arg(name_ref: &ast::NameRef) -> Option<(Name, Optio } fn find_param_in_func_def( - root: &SyntaxNode, + db: &dyn Db, + file: File, function_ptr: SyntaxNodePtr, param_name: &Name, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); let function_node = function_ptr.to_node(root); let param_list = function_node.ancestors().find_map(|a| { @@ -4439,11 +4547,14 @@ fn find_param_in_func_def( } fn find_column_in_function_return_table_min_index( - root: &SyntaxNode, + db: &dyn Db, + file: File, function_ptr: SyntaxNodePtr, column_name: &Name, min_index: usize, ) -> Option { + let tree = parse(db, file).tree(); + let root = tree.syntax(); let function_node = function_ptr.to_node(root); let create_function = function_node .ancestors() diff --git a/crates/squawk_ide/src/semantic_tokens.rs b/crates/squawk_ide/src/semantic_tokens.rs index 3256c341..600860bd 100644 --- a/crates/squawk_ide/src/semantic_tokens.rs +++ b/crates/squawk_ide/src/semantic_tokens.rs @@ -6,7 +6,8 @@ use squawk_syntax::{ }; use crate::db::{File, parse}; -use crate::goto_definition::{LocationKind, goto_definition}; +use crate::goto_definition::goto_definition; +use crate::location::LocationKind; fn highlight_param_mode(out: &mut SemanticTokenBuilder, mode: ast::ParamMode) { match mode { diff --git a/crates/squawk_server/src/global_state.rs b/crates/squawk_server/src/global_state.rs index 5a24ad8a..84a29e29 100644 --- a/crates/squawk_server/src/global_state.rs +++ b/crates/squawk_server/src/global_state.rs @@ -10,6 +10,7 @@ use lsp_types::notification::{ }; use rustc_hash::FxHashMap; use salsa::Setter; +use squawk_ide::builtins::{builtins_file, builtins_url}; use squawk_ide::db::{Database, File}; use squawk_thread::TaskPool; @@ -38,6 +39,7 @@ pub(crate) struct Handle { pub(super) struct GlobalState { db: Database, files: Arc>, + uris: Arc>, req_queue: ReqQueue, sender: Sender, pub(crate) task_pool: Handle, Receiver>, @@ -52,9 +54,16 @@ impl GlobalState { let handle = TaskPool::new_with_threads(sender.clone(), threads); Handle { handle, receiver } }; + let db = Database::default(); + let mut uris = FxHashMap::default(); + if let Some(uri) = builtins_url(&db) { + uris.insert(builtins_file(&db), uri); + } + Self { - db: Database::default(), + db, files: Arc::new(FxHashMap::default()), + uris: Arc::new(uris), req_queue: ReqQueue::default(), task_pool, sender, @@ -67,6 +76,7 @@ impl GlobalState { Snapshot { db: self.db.clone(), files: self.files.clone(), + uris: self.uris.clone(), } } @@ -83,12 +93,18 @@ impl GlobalState { file.set_content(&mut self.db).to(content.into()); } else { let file = File::new(&self.db, content.into()); - Arc::make_mut(&mut self.files).insert(uri, file); + Arc::make_mut(&mut self.files).insert(uri.clone(), file); + Arc::make_mut(&mut self.uris).insert(file, uri); } } pub(crate) fn remove(&mut self, uri: &Url) { - Arc::make_mut(&mut self.files).remove(uri); + if let Some(file) = Arc::make_mut(&mut self.files).remove(uri) { + // We can't delete file inputs in Salsa, so just set the content to + // empty string. + file.set_content(&mut self.db).to("".into()); + Arc::make_mut(&mut self.uris).remove(&file); + } } /// Track the request time and support marking cancellation @@ -239,6 +255,7 @@ impl GlobalState { pub(crate) struct Snapshot { pub(crate) db: Database, pub(crate) files: Arc>, + pub(crate) uris: Arc>, } impl Snapshot { @@ -249,6 +266,10 @@ impl Snapshot { pub(crate) fn file(&self, uri: &Url) -> Option { self.files.get(uri).copied() } + + pub(crate) fn uri(&self, file: File) -> Option { + self.uris.get(&file).cloned() + } } enum Event { diff --git a/crates/squawk_server/src/handlers/goto_definition.rs b/crates/squawk_server/src/handlers/goto_definition.rs index 6369f94c..6287951f 100644 --- a/crates/squawk_server/src/handlers/goto_definition.rs +++ b/crates/squawk_server/src/handlers/goto_definition.rs @@ -25,7 +25,7 @@ pub(crate) fn handle_goto_definition( !location.range.contains(offset), "Our target destination range must not include the source range otherwise go to def won't work in vscode." ); - to_location(snapshot, &uri, location) + to_location(snapshot, location) }) .collect(); diff --git a/crates/squawk_server/src/handlers/inlay_hints.rs b/crates/squawk_server/src/handlers/inlay_hints.rs index 2140b25a..a8c3f884 100644 --- a/crates/squawk_server/src/handlers/inlay_hints.rs +++ b/crates/squawk_server/src/handlers/inlay_hints.rs @@ -2,7 +2,6 @@ use anyhow::Result; use lsp_types::{ InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Location, }; -use squawk_ide::builtins::{builtins_line_index, builtins_url}; use squawk_ide::db::line_index; use squawk_ide::inlay_hints::inlay_hints; @@ -17,25 +16,24 @@ pub(crate) fn handle_inlay_hints( let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); - let line_index = line_index(db, file); + let current_line_index = line_index(db, file); let hints = inlay_hints(db, file); let lsp_hints: Vec = hints .into_iter() .flat_map(|hint| { - let line_col = line_index.line_col(hint.position); + let line_col = current_line_index.line_col(hint.position); let position = lsp_types::Position::new(line_col.line, line_col.col); - let uri = match hint.file { - Some(squawk_ide::goto_definition::FileId::Current) | None => uri.clone(), - Some(squawk_ide::goto_definition::FileId::Builtins) => builtins_url(db)?, + let target_file = hint.file; + let uri = match target_file { + Some(target_file) => snapshot.uri(target_file)?, + None => uri.clone(), }; - let line_index = match hint.file { - Some(squawk_ide::goto_definition::FileId::Current) | None => &line_index, - Some(squawk_ide::goto_definition::FileId::Builtins) => &builtins_line_index(db), - }; + let target_line_index = target_file.map(|target_file| line_index(db, target_file)); + let line_index = target_line_index.as_ref().unwrap_or(¤t_line_index); let kind: InlayHintKind = match hint.kind { squawk_ide::inlay_hints::InlayHintKind::Parameter => InlayHintKind::PARAMETER, diff --git a/crates/squawk_server/src/handlers/references.rs b/crates/squawk_server/src/handlers/references.rs index 2d307533..d3194d51 100644 --- a/crates/squawk_server/src/handlers/references.rs +++ b/crates/squawk_server/src/handlers/references.rs @@ -24,7 +24,7 @@ pub(crate) fn handle_references( let locations: Vec = refs .into_iter() .filter(|loc| include_declaration || !loc.range.contains(offset)) - .filter_map(|loc| to_location(snapshot, &uri, loc)) + .filter_map(|loc| to_location(snapshot, loc)) .collect(); Ok(Some(locations)) diff --git a/crates/squawk_server/src/lsp_utils.rs b/crates/squawk_server/src/lsp_utils.rs index 20479b0b..912f9060 100644 --- a/crates/squawk_server/src/lsp_utils.rs +++ b/crates/squawk_server/src/lsp_utils.rs @@ -8,7 +8,6 @@ use lsp_types::{ CodeAction, CodeActionKind, FoldingRange, FoldingRangeKind as LspFoldingRangeKind, Location, SemanticToken, Url, WorkspaceEdit, }; -use squawk_ide::builtins::{builtins_line_index, builtins_url}; use squawk_ide::code_actions::ActionKind; use squawk_ide::db::line_index; use squawk_ide::folding_ranges::{Fold, FoldKind}; @@ -214,20 +213,13 @@ pub(crate) fn apply_incremental_changes( pub(crate) fn to_location( snapshot: &Snapshot, - uri: &Url, - loc: squawk_ide::goto_definition::Location, + loc: squawk_ide::location::Location, ) -> Option { let db = snapshot.db(); - let file = snapshot.file(uri).unwrap(); - let uri = match loc.file { - squawk_ide::goto_definition::FileId::Current => uri.clone(), - squawk_ide::goto_definition::FileId::Builtins => builtins_url(db)?, - }; - let line_index = match loc.file { - squawk_ide::goto_definition::FileId::Current => &line_index(db, file), - squawk_ide::goto_definition::FileId::Builtins => &builtins_line_index(db), - }; - let range = range(line_index, loc.range); + let uri = snapshot.uri(loc.file)?; + + let line_index = line_index(db, loc.file); + let range = range(&line_index, loc.range); Some(Location { uri, range }) } diff --git a/crates/squawk_wasm/src/lib.rs b/crates/squawk_wasm/src/lib.rs index c2b8b59d..ed0fd402 100644 --- a/crates/squawk_wasm/src/lib.rs +++ b/crates/squawk_wasm/src/lib.rs @@ -3,10 +3,9 @@ use log::info; use rowan::{TextRange, TextSize}; use salsa::Setter; use serde::{Deserialize, Serialize}; -use squawk_ide::builtins::builtins_line_index; +use squawk_ide::builtins::builtins_file; use squawk_ide::db::{self, Database, File}; use squawk_ide::folding_ranges::{FoldKind, folding_ranges}; -use squawk_ide::goto_definition::FileId; use squawk_ide::semantic_tokens::{SemanticTokenType, semantic_tokens}; use squawk_syntax::ast::AstNode; use wasm_bindgen::prelude::*; @@ -275,17 +274,14 @@ impl SquawkDatabase { let file = self.file()?; let current_line_index = db::line_index(&self.db, file); let offset = position_to_offset(¤t_line_index, line, col)?; - let builtins_li = builtins_line_index(&self.db); let result = squawk_ide::goto_definition::goto_definition(&self.db, file, offset); let response: Vec = result .into_iter() - .map(|location| { - let range = location.range; - let (file, line_index) = match location.file { - FileId::Current => ("current", ¤t_line_index), - FileId::Builtins => ("builtins", &builtins_li), - }; + .map(|loc| { + let range = loc.range; + let file = file_string(&self.db, loc.file); + let line_index = db::line_index(&self.db, loc.file); let start = line_index.line_col(range.start()); let end = line_index.line_col(range.end()); let start_wide = line_index @@ -327,18 +323,19 @@ impl SquawkDatabase { let line_index = db::line_index(&self.db, file); let offset = position_to_offset(&line_index, line, col)?; let references = squawk_ide::find_references::find_references(&self.db, file, offset); - let builtins_li = builtins_line_index(&self.db); let locations: Vec = references .iter() .map(|loc| { - let (li, file) = match loc.file { - FileId::Current => (&line_index, "current"), - FileId::Builtins => (&builtins_li, "builtins"), - }; - let start = li.line_col(loc.range.start()); - let end = li.line_col(loc.range.end()); - let start_wide = li.to_wide(line_index::WideEncoding::Utf16, start).unwrap(); - let end_wide = li.to_wide(line_index::WideEncoding::Utf16, end).unwrap(); + let file = file_string(&self.db, loc.file); + let line_index = db::line_index(&self.db, loc.file); + let start = line_index.line_col(loc.range.start()); + let end = line_index.line_col(loc.range.end()); + let start_wide = line_index + .to_wide(line_index::WideEncoding::Utf16, start) + .unwrap(); + let end_wide = line_index + .to_wide(line_index::WideEncoding::Utf16, end) + .unwrap(); LocationRange { file: file.to_string(), @@ -614,6 +611,15 @@ impl SquawkDatabase { } } +fn file_string(db: &Database, file: File) -> &'static str { + // TODO: this will need to change when we add extension support + if file == builtins_file(db) { + "builtins" + } else { + "current" + } +} + fn into_error(err: E) -> Error { Error::new(&err.to_string()) } diff --git a/playground/src/providers.tsx b/playground/src/providers.tsx index 69a87a55..d638a573 100644 --- a/playground/src/providers.tsx +++ b/playground/src/providers.tsx @@ -333,6 +333,9 @@ export const semanticTokensProvider: monaco.languages.DocumentSemanticTokensProv return semantic_tokens_legend() }, provideDocumentSemanticTokens(model) { + // TODO: figure out if we can make this faster, it currently takes like + // 10+ seconds on the builtins.sql + if (model.getLineCount() > 2000) return null const content = model.getValue() const version = model.getVersionId() if (!content) return null