From 30fc7aa92c9df765021fcbe9134bbec05075ec0f Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Fri, 15 May 2026 18:33:56 -0400 Subject: [PATCH] ide: use InFile --- crates/squawk_ide/src/binder.rs | 2 + .../src/code_actions/add_explicit_alias.rs | 7 +- .../squawk_ide/src/code_actions/add_schema.rs | 21 +- crates/squawk_ide/src/code_actions/mod.rs | 47 +- .../src/code_actions/quote_identifier.rs | 7 +- .../src/code_actions/remove_else_clause.rs | 8 +- .../code_actions/remove_redundant_alias.rs | 7 +- .../rewrite_as_dollar_quoted_string.rs | 8 +- .../code_actions/rewrite_as_regular_string.rs | 8 +- .../rewrite_between_as_binary_expression.rs | 7 +- .../rewrite_cast_to_double_colon.rs | 7 +- .../rewrite_create_table_as_as_select_into.rs | 7 +- .../rewrite_double_colon_to_cast.rs | 7 +- .../src/code_actions/rewrite_from.rs | 7 +- .../src/code_actions/rewrite_leading_from.rs | 7 +- .../rewrite_not_equals_operator.rs | 7 +- .../code_actions/rewrite_select_as_table.rs | 7 +- .../code_actions/rewrite_select_as_values.rs | 7 +- .../rewrite_select_into_as_create_table_as.rs | 7 +- .../code_actions/rewrite_table_as_select.rs | 7 +- .../code_actions/rewrite_timestamp_type.rs | 7 +- .../code_actions/rewrite_values_as_select.rs | 7 +- .../squawk_ide/src/code_actions/test_utils.rs | 18 +- .../src/code_actions/unquote_identifier.rs | 7 +- crates/squawk_ide/src/collect.rs | 101 +- crates/squawk_ide/src/completion.rs | 17 +- crates/squawk_ide/src/document_symbols.rs | 110 +- crates/squawk_ide/src/file.rs | 9 +- crates/squawk_ide/src/find_references.rs | 14 +- crates/squawk_ide/src/goto_definition.rs | 45 +- crates/squawk_ide/src/hover.rs | 367 ++++--- crates/squawk_ide/src/inlay_hints.rs | 88 +- crates/squawk_ide/src/lib.rs | 2 +- crates/squawk_ide/src/offsets.rs | 9 +- crates/squawk_ide/src/resolve.rs | 965 +++++++++++------- crates/squawk_ide/src/semantic_tokens.rs | 13 +- .../squawk_server/src/handlers/code_action.rs | 4 +- .../squawk_server/src/handlers/completion.rs | 6 +- .../src/handlers/goto_definition.rs | 8 +- crates/squawk_server/src/handlers/hover.rs | 6 +- .../squawk_server/src/handlers/inlay_hints.rs | 17 +- .../squawk_server/src/handlers/references.rs | 8 +- .../src/handlers/selection_range.rs | 3 +- crates/squawk_server/src/lsp_utils.rs | 20 +- crates/squawk_wasm/src/lib.rs | 38 +- 45 files changed, 1174 insertions(+), 907 deletions(-) diff --git a/crates/squawk_ide/src/binder.rs b/crates/squawk_ide/src/binder.rs index f5d802a9..91c6c46f 100644 --- a/crates/squawk_ide/src/binder.rs +++ b/crates/squawk_ide/src/binder.rs @@ -54,6 +54,8 @@ impl Binder { ) -> Option { let symbols = self.scope.get(name)?; + // TODO: we should just require Schema, rather than doing the lookup in + // here. Having to thread position is annoying. let search_paths = match schema { Some(s) => std::slice::from_ref(s), None => self.search_path_at(position), diff --git a/crates/squawk_ide/src/code_actions/add_explicit_alias.rs b/crates/squawk_ide/src/code_actions/add_explicit_alias.rs index 43abf8ce..08d6a25e 100644 --- a/crates/squawk_ide/src/code_actions/add_explicit_alias.rs +++ b/crates/squawk_ide/src/code_actions/add_explicit_alias.rs @@ -4,17 +4,16 @@ use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; use squawk_syntax::quote::quote_column_alias; -use crate::{column_name::ColumnName, db::File, offsets::token_from_offset}; +use crate::{column_name::ColumnName, file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn add_explicit_alias( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let target = token.parent_ancestors().find_map(ast::Target::cast)?; if target.as_name().is_some() { diff --git a/crates/squawk_ide/src/code_actions/add_schema.rs b/crates/squawk_ide/src/code_actions/add_schema.rs index 96a4de0f..66919636 100644 --- a/crates/squawk_ide/src/code_actions/add_schema.rs +++ b/crates/squawk_ide/src/code_actions/add_schema.rs @@ -4,18 +4,16 @@ use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; use super::{ActionKind, CodeAction}; -use crate::{ - db::{File, bind}, - offsets::token_from_offset, -}; +use crate::{db::bind, file::InFile, offsets::token_from_offset}; pub(super) fn add_schema( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let file = position.file_id; + let offset = position.value; + let token = token_from_offset(db, position)?; let range = token.parent_ancestors().find_map(|node| { if let Some(path) = ast::Path::cast(node.clone()) { if path.qualifier().is_some() { @@ -40,13 +38,16 @@ pub(super) fn add_schema( return None; } - let position = token.text_range().start(); - let schema = bind(db, file).search_path_at(position).first()?.to_string(); + let token_start = token.text_range().start(); + let schema = bind(db, file) + .search_path_at(token_start) + .first()? + .to_string(); let replacement = format!("{}.", schema); actions.push(CodeAction { title: "Add schema".to_owned(), - edits: vec![Edit::insert(replacement, position)], + edits: vec![Edit::insert(replacement, token_start)], kind: ActionKind::RefactorRewrite, }); diff --git a/crates/squawk_ide/src/code_actions/mod.rs b/crates/squawk_ide/src/code_actions/mod.rs index ed956aba..03b97947 100644 --- a/crates/squawk_ide/src/code_actions/mod.rs +++ b/crates/squawk_ide/src/code_actions/mod.rs @@ -2,7 +2,7 @@ use rowan::TextSize; use salsa::Database as Db; use squawk_linter::Edit; -use crate::db::File; +use crate::file::InFile; mod add_explicit_alias; mod add_schema; @@ -64,29 +64,28 @@ pub struct CodeAction { pub kind: ActionKind, } -#[salsa::tracked] -pub fn code_actions(db: &dyn Db, file: File, offset: TextSize) -> Option> { +pub fn code_actions(db: &dyn Db, position: InFile) -> Option> { let mut actions = vec![]; - 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); - rewrite_select_into_as_create_table_as(db, file, &mut actions, offset); - rewrite_create_table_as_as_select_into(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); + rewrite_as_regular_string(db, position, &mut actions); + rewrite_as_dollar_quoted_string(db, position, &mut actions); + remove_else_clause(db, position, &mut actions); + rewrite_table_as_select(db, position, &mut actions); + rewrite_select_as_table(db, position, &mut actions); + rewrite_from(db, position, &mut actions); + rewrite_leading_from(db, position, &mut actions); + rewrite_values_as_select(db, position, &mut actions); + rewrite_select_as_values(db, position, &mut actions); + rewrite_select_into_as_create_table_as(db, position, &mut actions); + rewrite_create_table_as_as_select_into(db, position, &mut actions); + add_schema(db, position, &mut actions); + quote_identifier(db, position, &mut actions); + unquote_identifier(db, position, &mut actions); + add_explicit_alias(db, position, &mut actions); + remove_redundant_alias(db, position, &mut actions); + rewrite_cast_to_double_colon(db, position, &mut actions); + rewrite_double_colon_to_cast(db, position, &mut actions); + rewrite_between_as_binary_expression(db, position, &mut actions); + rewrite_not_equals_operator(db, position, &mut actions); + rewrite_timestamp_type(db, position, &mut actions); Some(actions) } diff --git a/crates/squawk_ide/src/code_actions/quote_identifier.rs b/crates/squawk_ide/src/code_actions/quote_identifier.rs index 2374745d..d4ecdf26 100644 --- a/crates/squawk_ide/src/code_actions/quote_identifier.rs +++ b/crates/squawk_ide/src/code_actions/quote_identifier.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn quote_identifier( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let parent = token.parent()?; let (is_quoted, text, text_range) = if let Some(name) = ast::Name::cast(parent.clone()) { diff --git a/crates/squawk_ide/src/code_actions/remove_else_clause.rs b/crates/squawk_ide/src/code_actions/remove_else_clause.rs index ee94d5db..b9f04479 100644 --- a/crates/squawk_ide/src/code_actions/remove_else_clause.rs +++ b/crates/squawk_ide/src/code_actions/remove_else_clause.rs @@ -6,16 +6,18 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::db::{File, parse}; +use crate::db::parse; +use crate::file::InFile; use super::{ActionKind, CodeAction}; pub(super) fn remove_else_clause( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { + let file = position.file_id; + let offset = position.value; let source_file = parse(db, file).tree(); let else_token = source_file diff --git a/crates/squawk_ide/src/code_actions/remove_redundant_alias.rs b/crates/squawk_ide/src/code_actions/remove_redundant_alias.rs index 7c010997..48810ffa 100644 --- a/crates/squawk_ide/src/code_actions/remove_redundant_alias.rs +++ b/crates/squawk_ide/src/code_actions/remove_redundant_alias.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{column_name::ColumnName, db::File, offsets::token_from_offset, symbols::Name}; +use crate::{column_name::ColumnName, file::InFile, offsets::token_from_offset, symbols::Name}; use super::{ActionKind, CodeAction}; pub(super) fn remove_redundant_alias( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let target = token.parent_ancestors().find_map(ast::Target::cast)?; let as_name = target.as_name()?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_as_dollar_quoted_string.rs b/crates/squawk_ide/src/code_actions/rewrite_as_dollar_quoted_string.rs index c4097b6b..9ae75534 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_as_dollar_quoted_string.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_as_dollar_quoted_string.rs @@ -3,16 +3,18 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::{SyntaxKind, ast::AstNode}; -use crate::db::{File, parse}; +use crate::db::parse; +use crate::file::InFile; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_as_dollar_quoted_string( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { + let file = position.file_id; + let offset = position.value; let string = parse(db, file) .tree() .syntax() diff --git a/crates/squawk_ide/src/code_actions/rewrite_as_regular_string.rs b/crates/squawk_ide/src/code_actions/rewrite_as_regular_string.rs index b5dc1cf5..a9f387bc 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_as_regular_string.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_as_regular_string.rs @@ -3,16 +3,18 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::{SyntaxKind, ast::AstNode}; -use crate::db::{File, parse}; +use crate::db::parse; +use crate::file::InFile; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_as_regular_string( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { + let file = position.file_id; + let offset = position.value; let dollar_string = parse(db, file) .tree() .syntax() diff --git a/crates/squawk_ide/src/code_actions/rewrite_between_as_binary_expression.rs b/crates/squawk_ide/src/code_actions/rewrite_between_as_binary_expression.rs index 3b246fe1..253e82af 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_between_as_binary_expression.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_between_as_binary_expression.rs @@ -3,19 +3,18 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::AstNode; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_between_as_binary_expression( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { use squawk_syntax::ast; - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let between_expr = token.parent_ancestors().find_map(ast::BetweenExpr::cast)?; let target = between_expr.target()?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_cast_to_double_colon.rs b/crates/squawk_ide/src/code_actions/rewrite_cast_to_double_colon.rs index 6f859523..e93dbcb9 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_cast_to_double_colon.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_cast_to_double_colon.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_cast_to_double_colon( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let cast_expr = token.parent_ancestors().find_map(ast::CastExpr::cast)?; if cast_expr.colon_colon().is_some() { diff --git a/crates/squawk_ide/src/code_actions/rewrite_create_table_as_as_select_into.rs b/crates/squawk_ide/src/code_actions/rewrite_create_table_as_as_select_into.rs index a29d6119..41e75b3f 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_create_table_as_as_select_into.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_create_table_as_as_select_into.rs @@ -6,17 +6,16 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_create_table_as_as_select_into( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let create_table_as = token .parent_ancestors() .find_map(ast::CreateTableAs::cast)?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_double_colon_to_cast.rs b/crates/squawk_ide/src/code_actions/rewrite_double_colon_to_cast.rs index 4d88ecf7..e0fd4447 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_double_colon_to_cast.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_double_colon_to_cast.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_double_colon_to_cast( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let cast_expr = token.parent_ancestors().find_map(ast::CastExpr::cast)?; if cast_expr.cast_token().is_some() { diff --git a/crates/squawk_ide/src/code_actions/rewrite_from.rs b/crates/squawk_ide/src/code_actions/rewrite_from.rs index ad70f082..b0cfff9b 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_from.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_from.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_from( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; if select.select_clause().is_some() { diff --git a/crates/squawk_ide/src/code_actions/rewrite_leading_from.rs b/crates/squawk_ide/src/code_actions/rewrite_leading_from.rs index 561ac229..6d7bec67 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_leading_from.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_leading_from.rs @@ -6,17 +6,16 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_leading_from( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; let from_clause = select.from_clause()?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_not_equals_operator.rs b/crates/squawk_ide/src/code_actions/rewrite_not_equals_operator.rs index 678be99f..51fafcfe 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_not_equals_operator.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_not_equals_operator.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_not_equals_operator( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let bin_expr = token.parent_ancestors().find_map(ast::BinExpr::cast)?; let (op_token, replacement, title) = match bin_expr.op()? { diff --git a/crates/squawk_ide/src/code_actions/rewrite_select_as_table.rs b/crates/squawk_ide/src/code_actions/rewrite_select_as_table.rs index cc25b560..6f7bc235 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_select_as_table.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_select_as_table.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_select_as_table( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let select = token.parent_ancestors().find_map(ast::Select::cast)?; if !can_transform_select_to_table(&select) { diff --git a/crates/squawk_ide/src/code_actions/rewrite_select_as_values.rs b/crates/squawk_ide/src/code_actions/rewrite_select_as_values.rs index 1fd4b27c..2a61a21f 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_select_as_values.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_select_as_values.rs @@ -5,7 +5,7 @@ use squawk_syntax::ast::{self, AstNode}; use crate::{ ast_nav::{self, SelectContext}, - db::File, + file::InFile, offsets::token_from_offset, symbols::Name, }; @@ -14,11 +14,10 @@ use super::{ActionKind, CodeAction}; pub(super) fn rewrite_select_as_values( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let parent = ast_nav::find_select_parent(token)?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_select_into_as_create_table_as.rs b/crates/squawk_ide/src/code_actions/rewrite_select_into_as_create_table_as.rs index ae77a3ef..bed63770 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_select_into_as_create_table_as.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_select_into_as_create_table_as.rs @@ -6,17 +6,16 @@ use squawk_syntax::{ ast::{self, AstNode}, }; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_select_into_as_create_table_as( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let select_into = token.parent_ancestors().find_map(ast::SelectInto::cast)?; let into_clause = select_into.into_clause()?; diff --git a/crates/squawk_ide/src/code_actions/rewrite_table_as_select.rs b/crates/squawk_ide/src/code_actions/rewrite_table_as_select.rs index e0230dbb..8bf40d8e 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_table_as_select.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_table_as_select.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_table_as_select( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let table = token.parent_ancestors().find_map(ast::Table::cast)?; let table_name = table.relation_name()?.syntax().text(); diff --git a/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs b/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs index e1304efa..b8dc4f35 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs @@ -3,17 +3,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_timestamp_type( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let time_type = token.parent_ancestors().find_map(ast::TimeType::cast)?; let replacement = match time_type.timezone()? { diff --git a/crates/squawk_ide/src/code_actions/rewrite_values_as_select.rs b/crates/squawk_ide/src/code_actions/rewrite_values_as_select.rs index aa5c21f2..9163a818 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_values_as_select.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_values_as_select.rs @@ -4,17 +4,16 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast::{self, AstNode}; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn rewrite_values_as_select( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let values = token.parent_ancestors().find_map(ast::Values::cast)?; let value_token_start = values.values_token().map(|x| x.text_range().start())?; diff --git a/crates/squawk_ide/src/code_actions/test_utils.rs b/crates/squawk_ide/src/code_actions/test_utils.rs index 3724e09d..5007102d 100644 --- a/crates/squawk_ide/src/code_actions/test_utils.rs +++ b/crates/squawk_ide/src/code_actions/test_utils.rs @@ -3,14 +3,14 @@ use salsa::Database as Db; use squawk_linter::Edit; use squawk_syntax::ast; -use crate::db::File; +use crate::file::InFile; use crate::test_utils::Fixture; use super::{ActionKind, CodeAction}; #[must_use] pub(super) fn apply_code_action( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, ) -> String { apply_code_action_(f, sql, false) @@ -18,14 +18,14 @@ pub(super) fn apply_code_action( #[must_use] pub(super) fn apply_code_action_with_errors( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, ) -> String { apply_code_action_(f, sql, true) } fn apply_code_action_( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, allow_errors: bool, ) -> String { @@ -39,7 +39,7 @@ fn apply_code_action_( let file = fixture.file(); let mut actions = vec![]; - f(db, file, &mut actions, offset); + f(db, InFile::new(file, offset), &mut actions); assert!( !actions.is_empty(), @@ -99,7 +99,7 @@ fn check_overlap(edits: &[Edit]) { } fn code_action_not_applicable_( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, allow_errors: bool, ) -> bool { @@ -113,13 +113,13 @@ fn code_action_not_applicable_( let file = fixture.file(); let mut actions = vec![]; - f(db, file, &mut actions, offset); + f(db, InFile::new(file, offset), &mut actions); actions.is_empty() } #[must_use] pub(super) fn code_action_not_applicable( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, ) -> bool { code_action_not_applicable_(f, sql, false) @@ -127,7 +127,7 @@ pub(super) fn code_action_not_applicable( #[must_use] pub(super) fn code_action_not_applicable_with_errors( - f: impl Fn(&dyn Db, File, &mut Vec, TextSize) -> Option<()>, + f: impl Fn(&dyn Db, InFile, &mut Vec) -> Option<()>, sql: &str, ) -> bool { code_action_not_applicable_(f, sql, true) diff --git a/crates/squawk_ide/src/code_actions/unquote_identifier.rs b/crates/squawk_ide/src/code_actions/unquote_identifier.rs index 19050569..7ea63ec0 100644 --- a/crates/squawk_ide/src/code_actions/unquote_identifier.rs +++ b/crates/squawk_ide/src/code_actions/unquote_identifier.rs @@ -5,17 +5,16 @@ use squawk_syntax::ast::{self, AstNode}; use squawk_linter::Edit; use squawk_syntax::quote::unquote_ident; -use crate::{db::File, offsets::token_from_offset}; +use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; pub(super) fn unquote_identifier( db: &dyn Db, - file: File, + position: InFile, actions: &mut Vec, - offset: TextSize, ) -> Option<()> { - let token = token_from_offset(db, file, offset)?; + let token = token_from_offset(db, position)?; let parent = token.parent()?; let name_node = if let Some(name) = ast::Name::cast(parent.clone()) { diff --git a/crates/squawk_ide/src/collect.rs b/crates/squawk_ide/src/collect.rs index 7b839c48..6c8eadd4 100644 --- a/crates/squawk_ide/src/collect.rs +++ b/crates/squawk_ide/src/collect.rs @@ -1,6 +1,7 @@ use crate::ast_nav; use crate::column_name::ColumnName; use crate::db::{File, list_files, parse}; +use crate::file::InFile; use crate::goto_definition::goto_definition; use crate::infer::{Type, infer_type_from_expr, infer_type_from_ty}; use crate::location::{Location, LocationKind}; @@ -19,7 +20,7 @@ pub(crate) fn columns_from_create_table( db: &dyn Db, file: File, create_table: &ast::CreateTableLike, -) -> Vec<(Name, Option)> { +) -> Vec<(Name, Option>)> { let mut columns = vec![]; columns_from_create_table_impl(db, file, create_table, &mut columns, 0); columns @@ -29,7 +30,7 @@ fn columns_from_create_table_impl( db: &dyn Db, file: File, create_table: &ast::CreateTableLike, - columns: &mut Vec<(Name, Option)>, + columns: &mut Vec<(Name, Option>)>, depth: usize, ) { if depth > 40 { @@ -42,13 +43,16 @@ fn columns_from_create_table_impl( ast_nav::CreateTableArg::Inherits(path) => { if let Some((schema, table_name)) = name::schema_and_name_path(&path) { let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - { + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { columns.extend(resolved_to_column_ptrs( db, - lookup_file, - resolved, + resolved.file_id, + resolved.value, depth + 1, )); } @@ -57,7 +61,10 @@ fn columns_from_create_table_impl( ast_nav::CreateTableArg::Column(column) => { if let Some(name) = column.name() { let col_name = Name::from_node(&name); - columns.push((col_name, Some(SyntaxNodePtr::new(name.syntax())))); + columns.push(( + col_name, + Some(InFile::new(file, SyntaxNodePtr::new(name.syntax()))), + )); } } ast_nav::CreateTableArg::LikeClause(like_clause) => { @@ -65,13 +72,16 @@ fn columns_from_create_table_impl( && let Some((schema, table_name)) = name::schema_and_name_path(&path) { let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - { + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { columns.extend(resolved_to_column_ptrs( db, - lookup_file, - resolved, + resolved.file_id, + resolved.value, depth + 1, )); } @@ -87,7 +97,7 @@ fn resolved_to_column_ptrs( file: File, resolved: ResolvedTableName, depth: usize, -) -> Vec<(Name, Option)> { +) -> Vec<(Name, Option>)> { match resolved { ResolvedTableName::Table(parent_table) => { let mut cols = vec![]; @@ -144,13 +154,16 @@ fn table_columns_impl( ast_nav::CreateTableArg::Inherits(path) => { if let Some((schema, table_name)) = name::schema_and_name_path(&path) { let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - { + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { columns.extend(resolved_to_columns_with_types( db, - lookup_file, - resolved, + resolved.file_id, + resolved.value, depth + 1, )); } @@ -167,13 +180,16 @@ fn table_columns_impl( && let Some((schema, table_name)) = name::schema_and_name_path(&path) { let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - { + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { columns.extend(resolved_to_columns_with_types( db, - lookup_file, - resolved, + resolved.file_id, + resolved.value, depth + 1, )); } @@ -292,11 +308,10 @@ fn select_columns_with_types( // Try CTE resolution first since resolve_table_name doesn't handle CTEs if let Some((ptr, kind)) = resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) { let tree = parse(db, file).tree(); let node = ptr.to_node(tree.syntax()); @@ -318,10 +333,13 @@ fn select_columns_with_types( } } // Fall back to builtins for schema-qualified names - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - { - return resolved_to_columns_with_types(db, lookup_file, resolved, 0); + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { + return resolved_to_columns_with_types(db, resolved.file_id, resolved.value, 0); } vec![] } @@ -396,15 +414,19 @@ fn returning_target_list_columns_with_types( if target.star_token().is_some() && let Some((schema, table_name)) = name::schema_and_name_path(path) - && let Some((lookup_file, resolved)) = resolve_table_name( + && let Some(resolved) = resolve_table_name( db, - file, &table_name, schema.as_ref(), - target.syntax().text_range().start(), + InFile::new(file, target.syntax().text_range().start()), ) { - columns.extend(resolved_to_columns_with_types(db, lookup_file, resolved, 0)); + columns.extend(resolved_to_columns_with_types( + db, + resolved.file_id, + resolved.value, + 0, + )); } } } @@ -531,7 +553,7 @@ fn column_ref_type(db: &dyn Db, file: File, expr: &ast::Expr) -> Option { ast::Expr::ParenExpr(paren) => return column_ref_type(db, file, &paren.expr()?), _ => return None, }; - let defs = goto_definition(db, file, position); + let defs = goto_definition(db, InFile::new(file, position)); let def = *defs.first()?; if def.kind != LocationKind::Column { return None; @@ -629,7 +651,7 @@ fn columns_for_star_from_from_item( return columns_for_star_from_alias(db, file, from_item, &alias); } - let Some(table_ptr) = table_ptr_from_from_item(db, file, from_item) else { + let Some(table_ptr) = table_ptr_from_from_item(db, InFile::new(file, from_item)) else { return vec![]; }; @@ -649,7 +671,7 @@ pub(crate) fn 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(db, file, from_item) else { + let Some(table_ptr) = table_ptr_from_from_item(db, InFile::new(file, from_item)) else { return vec![]; }; @@ -782,11 +804,10 @@ fn select_variant_columns_with_types( let position = table.syntax().text_range().start(); let Some((ptr, kind)) = resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) else { return vec![]; }; @@ -881,7 +902,7 @@ fn star_column_names_from_paren_select( }; let mut columns = vec![]; for from_item in ast_nav::iter_from_clause(&from_clause) { - if let Some(table_ptr) = table_ptr_from_from_item(db, file, &from_item) { + if let Some(table_ptr) = table_ptr_from_from_item(db, InFile::new(file, &from_item)) { columns.extend(star_column_names(db, file, &table_ptr)); } } diff --git a/crates/squawk_ide/src/completion.rs b/crates/squawk_ide/src/completion.rs index d294b554..2ed08702 100644 --- a/crates/squawk_ide/src/completion.rs +++ b/crates/squawk_ide/src/completion.rs @@ -7,6 +7,7 @@ use crate::ast_nav; use crate::binder; use crate::collect; use crate::db::{File, bind, parse}; +use crate::file::InFile; use crate::name::{self, Name, Schema}; use crate::resolve; use crate::symbols::SymbolKind; @@ -14,7 +15,9 @@ use crate::tokens::is_string_or_comment; const COMPLETION_MARKER: &str = "squawkCompletionMarker"; -pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec { +pub fn completion(db: &dyn Db, position: InFile) -> Vec { + let file = position.file_id; + let offset = position.value; let parse = parse(db, file); let source_file = parse.tree(); @@ -343,7 +346,7 @@ fn column_completions_from_clause( ) -> Vec { let mut completions = vec![]; let syntax_root = from_clause.syntax().ancestors().last().unwrap(); - for table_ptr in resolve::table_ptrs_from_clause(db, file, from_clause) { + for table_ptr in resolve::table_ptrs_from_clause(db, InFile::new(file, from_clause)) { let table_node = table_ptr.to_node(&syntax_root); match ast_nav::parent_source(&table_node) { Some(ast_nav::ParentSouce::CreateTable(create_table)) => { @@ -460,7 +463,8 @@ fn alias_base_columns_with_types( 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(db, file, &from_item) else { + let Some(table_ptr) = resolve::table_ptr_from_from_item(db, InFile::new(file, &from_item)) + else { return vec![]; }; @@ -851,7 +855,7 @@ fn function_detail( .ancestors() .find_map(ast::CreateFunction::cast)?; let path = create_function.path()?; - let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?; + let (schema, function_name) = resolve::resolve_function_info(db, InFile::new(file, &path))?; let param_list = create_function.param_list()?; let params = param_list.syntax().text().to_string(); @@ -935,6 +939,7 @@ pub struct CompletionItem { #[cfg(test)] mod tests { use super::completion; + use crate::file::InFile; use crate::test_utils::Fixture; use insta::assert_snapshot; use tabled::builder::Builder; @@ -944,7 +949,7 @@ mod tests { fn completions(sql: &str) -> String { let fixture = Fixture::new_allow_errors(sql); let offset = fixture.marker().offset(); - let items = completion(fixture.db(), fixture.file(), offset); + let items = completion(fixture.db(), InFile::new(fixture.file(), offset)); assert!( !items.is_empty(), "No completions found. If this was intended, use `completions_not_found` instead." @@ -955,7 +960,7 @@ mod tests { fn completions_not_found(sql: &str) { let fixture = Fixture::new_allow_errors(sql); let offset = fixture.marker().offset(); - let items = completion(fixture.db(), fixture.file(), offset); + let items = completion(fixture.db(), InFile::new(fixture.file(), offset)); assert_eq!( items, vec![], diff --git a/crates/squawk_ide/src/document_symbols.rs b/crates/squawk_ide/src/document_symbols.rs index 3e4e4f65..536f0f22 100644 --- a/crates/squawk_ide/src/document_symbols.rs +++ b/crates/squawk_ide/src/document_symbols.rs @@ -4,6 +4,7 @@ use squawk_syntax::ast::{self, AstNode}; use crate::binder::extract_string_literal; use crate::db::{File, parse}; +use crate::file::InFile; use crate::resolve::{ resolve_aggregate_info, resolve_function_info, resolve_procedure_info, resolve_sequence_info, resolve_table_info, resolve_type_info, resolve_view_info, @@ -64,32 +65,40 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateTable(create_table) => { - if let Some(symbol) = create_table_symbol(db, file, create_table) { + if let Some(symbol) = create_table_symbol(db, InFile::new(file, create_table)) { symbols.push(symbol); } } ast::Stmt::CreateTableAs(create_table_as) => { - if let Some(symbol) = create_table_as_symbol(db, file, create_table_as) { + if let Some(symbol) = create_table_as_symbol(db, InFile::new(file, create_table_as)) + { symbols.push(symbol); } } ast::Stmt::CreateForeignTable(create_foreign_table) => { - if let Some(symbol) = create_table_symbol(db, file, create_foreign_table) { + if let Some(symbol) = + create_table_symbol(db, InFile::new(file, create_foreign_table)) + { symbols.push(symbol); } } ast::Stmt::CreateFunction(create_function) => { - if let Some(symbol) = create_function_symbol(db, file, create_function) { + if let Some(symbol) = create_function_symbol(db, InFile::new(file, create_function)) + { symbols.push(symbol); } } ast::Stmt::CreateAggregate(create_aggregate) => { - if let Some(symbol) = create_aggregate_symbol(db, file, create_aggregate) { + if let Some(symbol) = + create_aggregate_symbol(db, InFile::new(file, create_aggregate)) + { symbols.push(symbol); } } ast::Stmt::CreateProcedure(create_procedure) => { - if let Some(symbol) = create_procedure_symbol(db, file, create_procedure) { + if let Some(symbol) = + create_procedure_symbol(db, InFile::new(file, create_procedure)) + { symbols.push(symbol); } } @@ -99,12 +108,13 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateDomain(create_domain) => { - if let Some(symbol) = create_domain_symbol(db, file, create_domain) { + if let Some(symbol) = create_domain_symbol(db, InFile::new(file, create_domain)) { symbols.push(symbol); } } ast::Stmt::CreateSequence(create_sequence) => { - if let Some(symbol) = create_sequence_symbol(db, file, create_sequence) { + if let Some(symbol) = create_sequence_symbol(db, InFile::new(file, create_sequence)) + { symbols.push(symbol); } } @@ -154,17 +164,19 @@ pub fn document_symbols(db: &dyn Db, file: File) -> Vec { } } ast::Stmt::CreateType(create_type) => { - if let Some(symbol) = create_type_symbol(db, file, create_type) { + if let Some(symbol) = create_type_symbol(db, InFile::new(file, create_type)) { symbols.push(symbol); } } ast::Stmt::CreateView(create_view) => { - if let Some(symbol) = create_view_symbol(db, file, create_view) { + if let Some(symbol) = create_view_symbol(db, InFile::new(file, create_view)) { symbols.push(symbol); } } ast::Stmt::CreateMaterializedView(create_view) => { - if let Some(symbol) = create_materialized_view_symbol(db, file, create_view) { + if let Some(symbol) = + create_materialized_view_symbol(db, InFile::new(file, create_view)) + { symbols.push(symbol); } } @@ -269,13 +281,14 @@ fn create_schema_symbol(create_schema: ast::CreateSchema) -> Option, ) -> Option { + let file = create_table.file_id; + let create_table = create_table.value; let path = create_table.path()?; let name_node = path.segment()?.name()?; - let (schema, table_name) = resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve_table_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, table_name); let full_range = create_table.syntax().text_range(); @@ -302,13 +315,14 @@ fn create_table_symbol( fn create_table_as_symbol( db: &dyn Db, - file: File, - create_table_as: ast::CreateTableAs, + create_table_as: InFile, ) -> Option { + let file = create_table_as.file_id; + let create_table_as = create_table_as.value; let path = create_table_as.path()?; let name_node = path.segment()?.name()?.syntax().clone(); - let (schema, table_name) = resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve_table_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, table_name); let full_range = create_table_as.syntax().text_range(); @@ -326,15 +340,13 @@ fn create_table_as_symbol( }) } -fn create_view_symbol( - db: &dyn Db, - file: File, - create_view: ast::CreateView, -) -> Option { +fn create_view_symbol(db: &dyn Db, create_view: InFile) -> Option { + let file = create_view.file_id; + let create_view = create_view.value; let path = create_view.path()?; let name_node = path.segment()?.name()?; - let (schema, view_name) = resolve_view_info(db, file, &path)?; + let (schema, view_name) = resolve_view_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, view_name); let full_range = create_view.syntax().text_range(); @@ -378,13 +390,14 @@ fn symbols_from_column_list( // TODO: combine with create_view_symbol fn create_materialized_view_symbol( db: &dyn Db, - file: File, - create_view: ast::CreateMaterializedView, + create_view: InFile, ) -> Option { + let file = create_view.file_id; + let create_view = create_view.value; let path = create_view.path()?; let name_node = path.segment()?.name()?; - let (schema, view_name) = resolve_view_info(db, file, &path)?; + let (schema, view_name) = resolve_view_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, view_name); let full_range = create_view.syntax().text_range(); @@ -401,13 +414,14 @@ fn create_materialized_view_symbol( fn create_function_symbol( db: &dyn Db, - file: File, - create_function: ast::CreateFunction, + create_function: InFile, ) -> Option { + let file = create_function.file_id; + let create_function = create_function.value; let path = create_function.path()?; let name_node = path.segment()?.name()?; - let (schema, function_name) = resolve_function_info(db, file, &path)?; + let (schema, function_name) = resolve_function_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, function_name); let full_range = create_function.syntax().text_range(); @@ -425,13 +439,14 @@ fn create_function_symbol( fn create_aggregate_symbol( db: &dyn Db, - file: File, - create_aggregate: ast::CreateAggregate, + create_aggregate: InFile, ) -> Option { + let file = create_aggregate.file_id; + let create_aggregate = create_aggregate.value; let path = create_aggregate.path()?; let name_node = path.segment()?.name()?; - let (schema, aggregate_name) = resolve_aggregate_info(db, file, &path)?; + let (schema, aggregate_name) = resolve_aggregate_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, aggregate_name); let full_range = create_aggregate.syntax().text_range(); @@ -449,13 +464,14 @@ fn create_aggregate_symbol( fn create_procedure_symbol( db: &dyn Db, - file: File, - create_procedure: ast::CreateProcedure, + create_procedure: InFile, ) -> Option { + let file = create_procedure.file_id; + let create_procedure = create_procedure.value; let path = create_procedure.path()?; let name_node = path.segment()?.name()?; - let (schema, procedure_name) = resolve_procedure_info(db, file, &path)?; + let (schema, procedure_name) = resolve_procedure_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, procedure_name); let full_range = create_procedure.syntax().text_range(); @@ -490,13 +506,14 @@ fn create_index_symbol(create_index: ast::CreateIndex) -> Option fn create_domain_symbol( db: &dyn Db, - file: File, - create_domain: ast::CreateDomain, + create_domain: InFile, ) -> Option { + let file = create_domain.file_id; + let create_domain = create_domain.value; let path = create_domain.path()?; let name_node = path.segment()?.name()?; - let (schema, domain_name) = resolve_type_info(db, file, &path)?; + let (schema, domain_name) = resolve_type_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, domain_name); let full_range = create_domain.syntax().text_range(); @@ -514,13 +531,14 @@ fn create_domain_symbol( fn create_sequence_symbol( db: &dyn Db, - file: File, - create_sequence: ast::CreateSequence, + create_sequence: InFile, ) -> Option { + let file = create_sequence.file_id; + let create_sequence = create_sequence.value; let path = create_sequence.path()?; let name_node = path.segment()?.name()?; - let (schema, sequence_name) = resolve_sequence_info(db, file, &path)?; + let (schema, sequence_name) = resolve_sequence_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, sequence_name); let full_range = create_sequence.syntax().text_range(); @@ -695,15 +713,13 @@ fn create_property_graph_symbol( }) } -fn create_type_symbol( - db: &dyn Db, - file: File, - create_type: ast::CreateType, -) -> Option { +fn create_type_symbol(db: &dyn Db, create_type: InFile) -> Option { + let file = create_type.file_id; + let create_type = create_type.value; let path = create_type.path()?; let name_node = path.segment()?.name()?; - let (schema, type_name) = resolve_type_info(db, file, &path)?; + let (schema, type_name) = resolve_type_info(db, InFile::new(file, &path))?; let name = format!("{}.{}", schema.0, type_name); let full_range = create_type.syntax().text_range(); diff --git a/crates/squawk_ide/src/file.rs b/crates/squawk_ide/src/file.rs index 72bc599f..fd208322 100644 --- a/crates/squawk_ide/src/file.rs +++ b/crates/squawk_ide/src/file.rs @@ -33,11 +33,16 @@ use crate::db::File; /// * `InFile` -- syntax node in a file /// * `InFile` -- ast node in a file /// * `InFile` -- offset in a file -#[allow(dead_code)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub struct InFileWrapper { pub file_id: FileKind, pub value: T, } -#[expect(unused)] + +impl InFileWrapper { + pub fn new(file_id: FileKind, value: T) -> Self { + Self { file_id, value } + } +} + pub type InFile = InFileWrapper; diff --git a/crates/squawk_ide/src/find_references.rs b/crates/squawk_ide/src/find_references.rs index dcdd7465..6774de1f 100644 --- a/crates/squawk_ide/src/find_references.rs +++ b/crates/squawk_ide/src/find_references.rs @@ -1,13 +1,14 @@ -use crate::db::{File, parse}; +use crate::db::parse; +use crate::file::InFile; use crate::goto_definition; use crate::location::Location; use rowan::TextSize; use salsa::Database as Db; use squawk_syntax::ast::{self, AstNode}; -#[salsa::tracked] -pub fn find_references(db: &dyn Db, file: File, offset: TextSize) -> Vec { - let targets = goto_definition::goto_definition(db, file, offset); +pub fn find_references(db: &dyn Db, position: InFile) -> Vec { + let file = position.file_id; + let targets = goto_definition::goto_definition(db, position); let Some(first) = targets.first() else { return vec![]; }; @@ -21,7 +22,7 @@ pub fn find_references(db: &dyn Db, file: File, offset: TextSize) -> Vec Vec SmallVec<[Location; 1]> { - let Some(token) = token_from_offset(db, file, offset) else { +pub fn goto_definition(db: &dyn Db, position: InFile) -> SmallVec<[Location; 1]> { + let file = position.file_id; + let Some(token) = token_from_offset(db, position) else { return smallvec![]; }; let Some(parent) = token.parent() else { @@ -39,21 +40,21 @@ 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(db, file, offset) + && let Some(begin_range) = find_preceding_begin(db, position) { return smallvec![Location::new(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(db, file, offset) + && let Some(begin_range) = find_preceding_begin(db, position) { return smallvec![Location::new(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(db, file, offset) + && let Some(end_range) = find_following_commit_or_rollback(db, position) { return smallvec![Location::new(file, end_range, LocationKind::CommitEnd)]; } @@ -66,7 +67,11 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L if let Some(name_ref) = ast::NameRef::cast(parent.clone()) { for definition_file in list_files(db, file) { - if let Some(locations) = resolve::resolve_name_ref(db, definition_file, &name_ref) { + if let Some(locations) = + // TODO: we shouldn't be wrapping name_ref like this since it's + // a different file. Probably a bug. + resolve::resolve_name_ref(db, InFile::new(definition_file, &name_ref)) + { return locations; } } @@ -82,9 +87,10 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L }); if let Some(ty) = type_node { for definition_file in list_files(db, file) { - let position = token.text_range().start(); if let Some(ptr) = - resolve::resolve_type_ptr_from_type(db, definition_file, &ty, position) + // TODO: we shouldn't be wrapping name_ref like this since it's + // a different file. Probably a bug. + resolve::resolve_type_ptr_from_type(db, InFile::new(definition_file, &ty)) { return smallvec![Location { file: definition_file, @@ -98,12 +104,12 @@ pub fn goto_definition(db: &dyn Db, file: File, offset: TextSize) -> SmallVec<[L smallvec![] } -fn find_preceding_begin(db: &dyn Db, file: File, before: TextSize) -> Option { +fn find_preceding_begin(db: &dyn Db, position: InFile) -> Option { let mut last_begin: Option = None; - for stmt in parse(db, file).tree().stmts() { + for stmt in parse(db, position.file_id).tree().stmts() { if let ast::Stmt::Begin(begin) = stmt { let range = begin.syntax().text_range(); - if range.end() <= before { + if range.end() <= position.value { last_begin = Some(range); } } @@ -111,18 +117,14 @@ fn find_preceding_begin(db: &dyn Db, file: File, before: TextSize) -> Option Option { - for stmt in parse(db, file).tree().stmts() { +fn find_following_commit_or_rollback(db: &dyn Db, position: InFile) -> Option { + for stmt in parse(db, position.file_id).tree().stmts() { let range = match &stmt { ast::Stmt::Commit(commit) => commit.syntax().text_range(), ast::Stmt::Rollback(rollback) => rollback.syntax().text_range(), _ => continue, }; - if range.start() >= after { + if range.start() >= position.value { return Some(range); } } @@ -133,6 +135,7 @@ fn find_following_commit_or_rollback( mod test { use crate::builtins::builtins_file; use crate::db::File; + use crate::file::InFile; use crate::goto_definition::goto_definition; use crate::test_utils::Fixture; use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle}; @@ -157,7 +160,7 @@ mod test { let db = fixture.db(); let current_file = fixture.file(); - let results = goto_definition(db, current_file, offset); + let results = goto_definition(db, InFile::new(current_file, offset)); if results.is_empty() { return None; } diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index aa0d4c65..bc318783 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -2,7 +2,8 @@ use crate::ast_nav; use crate::collect; use crate::column_name::ColumnName; use crate::comments::preceding_comment; -use crate::db::{File, bind, list_files, parse}; +use crate::db::{bind, list_files, parse}; +use crate::file::InFile; use crate::infer::infer_type_from_expr; use crate::location::{Location, LocationKind}; use crate::name; @@ -98,28 +99,29 @@ fn hover_column_with_preceding_comment(snippet: impl Into, def_node: &Sy Hover::snippet(snippet) } -#[salsa::tracked] -pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option { - let token = token_from_offset(db, file, offset)?; +pub fn hover(db: &dyn Db, position: InFile) -> Option { + let file = position.file_id; + let token = token_from_offset(db, position)?; let parent = token.parent()?; 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, file, field_expr) + && let Some(result) = hover_qualified_star(db, InFile::new(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, file, arg_list) + && let Some(result) = + hover_unqualified_star_in_arg_list(db, InFile::new(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, file, target) + && let Some(result) = hover_unqualified_star(db, InFile::new(file, target)) { return Some(result); } @@ -127,17 +129,19 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option { } if ast::NameRef::can_cast(parent.kind()) { - return hover_name_ref(db, file, offset); + return hover_name_ref(db, position); } if let Some(name) = ast::Name::cast(parent) { - return hover_name(db, file, name); + return hover_name(db, InFile::new(file, name)); } None } -fn hover_name(db: &dyn Db, file: File, name: ast::Name) -> Option { +fn hover_name(db: &dyn Db, name: InFile) -> Option { + let file = name.file_id; + let name = name.value; let def = Location::from_node(file, name.syntax())?; match def.kind { LocationKind::Aggregate => hover_aggregate(db, def), @@ -182,7 +186,7 @@ fn hover_name_column(db: &dyn Db, def: Location) -> Option { if let Some(column) = def_node.parent().and_then(ast::Column::cast) && let Some(create_table) = def_node.ancestors().find_map(ast::CreateTableLike::cast) { - return hover_column_definition(db, def.file, create_table, column); + return hover_column_definition(db, InFile::new(def.file, create_table), column); } if def_node @@ -190,18 +194,18 @@ fn hover_name_column(db: &dyn Db, def: Location) -> Option { .any(|ancestor| ast::ColumnList::can_cast(ancestor.kind())) && let Some(create_view) = def_node.ancestors().find_map(ast::CreateViewLike::cast) { - return format_view_column(db, def.file, &create_view, &def_node); + return format_view_column(db, InFile::new(def.file, &create_view), &def_node); } None } -fn hover_name_ref(db: &dyn Db, file: File, offset: TextSize) -> Option { +fn hover_name_ref(db: &dyn Db, position: InFile) -> Option { // We can get multiple in the case of using // // select * from t join u using (id); // - let definitions = goto_definition::goto_definition(db, file, offset); + let definitions = goto_definition::goto_definition(db, position); let def = *definitions.first()?; match def.kind { LocationKind::Aggregate => hover_aggregate(db, def), @@ -349,7 +353,7 @@ fn format_hover_for_column_ptr(db: &dyn Db, def: Location) -> Option { ast_nav::ParentSouce::CreateView(create_view) => { let column_name = collect::column_name_from_node(def_node)?; let path = create_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(db, def.file, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, InFile::new(def.file, &path))?; let ty = collect::view_like_columns_with_types(db, def.file, &create_view) .into_iter() .find(|(name, _)| *name == column_name) @@ -398,7 +402,8 @@ fn format_hover_for_column_ptr(db: &dyn Db, def: Location) -> Option { ast_nav::ParentSouce::CreateTableAs(create_table_as) => { let column_name = collect::column_name_from_node(def_node)?; let path = create_table_as.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, def.file, &path)?; + let (schema, table_name) = + resolve::resolve_table_info(db, InFile::new(def.file, &path))?; let ty = collect::create_table_as_columns_with_types(db, def.file, &create_table_as) .into_iter() .find(|(name, _)| *name == column_name) @@ -425,7 +430,8 @@ fn format_hover_for_column_ptr(db: &dyn Db, def: Location) -> Option { let column_name = column.name()?; let ty = column.ty()?; let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, def.file, &path)?; + let (schema, table_name) = + resolve::resolve_table_info(db, InFile::new(def.file, &path))?; return Some(hover_column_with_preceding_comment( ColumnHover::schema_table_column_type( @@ -450,7 +456,7 @@ fn hover_composite_type_field(db: &dyn Db, def: Location) -> Option { .ancestors() .find_map(ast::CreateType::cast)?; let type_path = create_type.path()?; - let (schema, type_name) = resolve::resolve_type_info(db, def.file, &type_path)?; + let (schema, type_name) = resolve::resolve_type_info(db, InFile::new(def.file, &type_path))?; Some(hover_with_preceding_comment( format!( @@ -466,14 +472,15 @@ fn hover_composite_type_field(db: &dyn Db, def: Location) -> Option { fn hover_column_definition( db: &dyn Db, - file: File, - create_table: impl ast::HasCreateTable, + create_table: InFile, column: ast::Column, ) -> Option { + let file = create_table.file_id; + let create_table = create_table.value; 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(db, file, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let ty = ty.syntax().text().to_string(); Some(hover_with_preceding_comment( ColumnHover::schema_table_column_type(&schema.to_string(), &table_name, &column_name, &ty), @@ -481,18 +488,21 @@ fn hover_column_definition( )) } -fn format_table_source(db: &dyn Db, file: File, source: ast_nav::ParentSouce) -> Option { - match source { - ast_nav::ParentSouce::Alias(alias) => format_alias_with_column_list(db, file, alias), +fn format_table_source(db: &dyn Db, source: InFile) -> Option { + let file = source.file_id; + match source.value { + ast_nav::ParentSouce::Alias(alias) => { + format_alias_with_column_list(db, InFile::new(file, alias)) + } ast_nav::ParentSouce::WithTable(with_table) => format_with_table(with_table), ast_nav::ParentSouce::CreateView(create_view) => { - format_create_view_like(db, file, create_view) + format_create_view_like(db, InFile::new(file, create_view)) } ast_nav::ParentSouce::CreateTable(create_table) => { - format_create_table(db, file, create_table) + format_create_table(db, InFile::new(file, create_table)) } ast_nav::ParentSouce::CreateTableAs(create_table_as) => { - format_create_table_as(db, file, create_table_as) + format_create_table_as(db, InFile::new(file, create_table_as)) } ast_nav::ParentSouce::ParenSelect(paren_select) => format_paren_select(paren_select), } @@ -500,10 +510,12 @@ fn format_table_source(db: &dyn Db, file: File, source: ast_nav::ParentSouce) -> fn hover_table(db: &dyn Db, def: Location) -> Option { let source = ast_nav::parent_source(&def.to_node(db)?)?; - format_table_source(db, def.file, source) + format_table_source(db, InFile::new(def.file, source)) } -fn format_alias_with_column_list(db: &dyn Db, file: File, alias: ast::Alias) -> Option { +fn format_alias_with_column_list(db: &dyn Db, alias: InFile) -> Option { + let file = alias.file_id; + let alias = alias.value; let alias_name = alias.name()?; let name = Name::from_node(&alias_name); @@ -524,7 +536,8 @@ fn format_alias_with_column_list(db: &dyn Db, file: File, alias: ast::Alias) -> .collect(); if let Some(from_item) = alias.syntax().ancestors().find_map(ast::FromItem::cast) - && let Some(table_ptr) = resolve::table_ptr_from_from_item(db, file, &from_item) + && let Some(table_ptr) = + resolve::table_ptr_from_from_item(db, InFile::new(file, &from_item)) { let base_columns = collect::star_column_names(db, file, &table_ptr); for column in base_columns.iter().skip(columns.len()) { @@ -540,16 +553,17 @@ fn format_alias_with_column_list(db: &dyn Db, file: File, alias: ast::Alias) -> Some(Hover::snippet(format!("table {}({})", name, columns))) } -fn hover_qualified_star(db: &dyn Db, file: File, field_expr: ast::FieldExpr) -> Option { - let table_ptr = qualified_star_table_ptr(db, file, field_expr)?; - hover_qualified_star_columns(db, file, &table_ptr) +fn hover_qualified_star(db: &dyn Db, field_expr: InFile) -> Option { + let file = field_expr.file_id; + let table_ptr = qualified_star_table_ptr(db, field_expr)?; + hover_qualified_star_columns(db, InFile::new(file, &table_ptr)) } -fn hover_unqualified_star(db: &dyn Db, file: File, target: ast::Target) -> Option { +fn hover_unqualified_star(db: &dyn Db, target: InFile) -> Option { let mut results = vec![]; - for file in list_files(db, file) { - results = hover_unqualified_star_with_binder(db, file, &target); - if results.is_empty() && target_has_schema_qualified_from_item(&target) { + for file in list_files(db, target.file_id) { + results = hover_unqualified_star_with_binder(db, InFile::new(file, &target.value)); + if results.is_empty() && target_has_schema_qualified_from_item(&target.value) { continue; } else { break; @@ -558,12 +572,13 @@ fn hover_unqualified_star(db: &dyn Db, file: File, target: ast::Target) -> Optio merge_hovers(results) } -fn hover_unqualified_star_with_binder(db: &dyn Db, file: File, target: &ast::Target) -> Vec { +fn hover_unqualified_star_with_binder(db: &dyn Db, target: InFile<&ast::Target>) -> Vec { + let file = target.file_id; let mut results = vec![]; - if let Some(table_ptrs) = unqualified_star_table_ptrs(db, file, target) { + if let Some(table_ptrs) = unqualified_star_table_ptrs(db, target) { for table_ptr in table_ptrs { - if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { + if let Some(columns) = hover_qualified_star_columns(db, InFile::new(file, &table_ptr)) { results.push(columns); } } @@ -591,13 +606,13 @@ fn target_has_schema_qualified_from_item(target: &ast::Target) -> bool { fn hover_unqualified_star_in_arg_list( db: &dyn Db, - file: File, - arg_list: ast::ArgList, + arg_list: InFile, ) -> Option { - let table_ptrs = unqualified_star_in_arg_list_ptrs(db, file, &arg_list)?; + let file = arg_list.file_id; + let table_ptrs = unqualified_star_in_arg_list_ptrs(db, InFile::new(file, &arg_list.value))?; let mut results = vec![]; for table_ptr in table_ptrs { - if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { + if let Some(columns) = hover_qualified_star_columns(db, InFile::new(file, &table_ptr)) { results.push(columns); } } @@ -613,40 +628,41 @@ fn format_subquery_table(name: Name, paren_select: ast::ParenSelect) -> Option, ) -> Option { + let file = table_ptr.file_id; let source_file = parse(db, file).tree(); let root = source_file.syntax(); - let table_name_node = table_ptr.to_node(root); + let table_name_node = table_ptr.value.to_node(root); match ast_nav::parent_source(&table_name_node)? { ast_nav::ParentSouce::Alias(alias) => { - hover_qualified_star_columns_from_alias(db, file, &alias) + hover_qualified_star_columns_from_alias(db, InFile::new(file, &alias)) } ast_nav::ParentSouce::WithTable(with_table) => { - hover_qualified_star_columns_from_cte(db, file, with_table) + hover_qualified_star_columns_from_cte(db, InFile::new(file, with_table)) } ast_nav::ParentSouce::CreateTable(create_table) => { - hover_qualified_star_columns_from_table(db, file, create_table) + hover_qualified_star_columns_from_table(db, InFile::new(file, create_table)) } ast_nav::ParentSouce::CreateTableAs(create_table_as) => { - hover_qualified_star_columns_from_table_as(db, file, &create_table_as) + hover_qualified_star_columns_from_table_as(db, InFile::new(file, &create_table_as)) } ast_nav::ParentSouce::CreateView(create_view) => { - hover_qualified_star_columns_from_view_like(db, file, &create_view) + hover_qualified_star_columns_from_view_like(db, InFile::new(file, &create_view)) } ast_nav::ParentSouce::ParenSelect(paren_select) => { - hover_qualified_star_columns_from_subquery(db, file, &paren_select) + hover_qualified_star_columns_from_subquery(db, InFile::new(file, &paren_select)) } } } fn hover_qualified_star_columns_from_alias( db: &dyn Db, - file: File, - alias: &ast::Alias, + alias: InFile<&ast::Alias>, ) -> Option { + let file = alias.file_id; + let alias = alias.value; let alias_name = Name::from_node(&alias.name()?); alias.column_list()?; let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?; @@ -677,11 +693,12 @@ fn hover_qualified_star_columns_from_alias( fn hover_qualified_star_columns_from_table( db: &dyn Db, - file: File, - create_table: impl ast::HasCreateTable, + create_table: InFile, ) -> Option { + let file = create_table.file_id; + let create_table = create_table.value; let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let schema = schema.to_string(); let results: Vec = collect::table_columns(db, file, &create_table) .into_iter() @@ -701,11 +718,12 @@ fn hover_qualified_star_columns_from_table( fn hover_qualified_star_columns_from_table_as( db: &dyn Db, - file: File, - create_table_as: &ast::CreateTableAs, + create_table_as: InFile<&ast::CreateTableAs>, ) -> Option { + let file = create_table_as.file_id; + let create_table_as = create_table_as.value; let path = create_table_as.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let schema_str = schema.to_string(); let columns = collect::create_table_as_columns_with_types(db, file, create_table_as); @@ -733,9 +751,10 @@ fn hover_qualified_star_columns_from_table_as( fn hover_qualified_star_columns_from_cte( db: &dyn Db, - file: File, - with_table: ast::WithTable, + with_table: InFile, ) -> Option { + let file = with_table.file_id; + let with_table = with_table.value; let cte_name = Name::from_node(&with_table.name()?); let cte_name = cte_name.to_string(); let columns = collect::with_table_columns_with_types(db, file, with_table); @@ -760,11 +779,12 @@ fn hover_qualified_star_columns_from_cte( fn hover_qualified_star_columns_from_view_like( db: &dyn Db, - file: File, - create_view: &ast::CreateViewLike, + create_view: InFile<&ast::CreateViewLike>, ) -> Option { + let file = create_view.file_id; + let create_view = create_view.value; let path = create_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, InFile::new(file, &path))?; let schema_str = schema.to_string(); let columns = collect::view_like_columns_with_types(db, file, create_view); @@ -793,9 +813,10 @@ fn hover_qualified_star_columns_from_view_like( fn hover_qualified_star_columns_from_subquery( db: &dyn Db, - file: File, - paren_select: &ast::ParenSelect, + paren_select: InFile<&ast::ParenSelect>, ) -> Option { + let file = paren_select.file_id; + let paren_select = paren_select.value; let select_variant = paren_select.select()?; if let Some(select) = ast_nav::select_from_variant(select_variant) { @@ -806,18 +827,22 @@ fn hover_qualified_star_columns_from_subquery( for target in target_list.targets() { if target.star_token().is_some() { - let table_ptrs = unqualified_star_table_ptrs(db, file, &target)?; + let table_ptrs = unqualified_star_table_ptrs(db, InFile::new(file, &target))?; for table_ptr in table_ptrs { - if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) { + if let Some(columns) = + hover_qualified_star_columns(db, InFile::new(file, &table_ptr)) + { results.push(columns) } } continue; } - if let Some(result) = - hover_subquery_target_column(db, file, &target, subquery_alias.as_ref()) - { + if let Some(result) = hover_subquery_target_column( + db, + InFile::new(file, &target), + subquery_alias.as_ref(), + ) { results.push(result); } } @@ -859,10 +884,11 @@ fn subquery_alias_name(paren_select: &ast::ParenSelect) -> Option { fn hover_subquery_target_column( db: &dyn Db, - file: File, - target: &ast::Target, + target: InFile<&ast::Target>, subquery_alias: Option<&Name>, ) -> Option { + let file = target.file_id; + let target = target.value; if let Some(alias) = subquery_alias && let Some((col_name, _node)) = ColumnName::from_target(target.clone()) && let Some(col_name) = col_name.to_string() @@ -877,10 +903,13 @@ fn hover_subquery_target_column( } let result = match target.expr()? { - ast::Expr::NameRef(name_ref) => hover(db, file, name_ref.syntax().text_range().start()), + ast::Expr::NameRef(name_ref) => hover( + db, + InFile::new(file, name_ref.syntax().text_range().start()), + ), ast::Expr::FieldExpr(field_expr) => { let field = field_expr.field()?; - hover(db, file, field.syntax().text_range().start()) + hover(db, InFile::new(file, field.syntax().text_range().start())) } _ => None, }; @@ -907,7 +936,7 @@ fn hover_index(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateIndex::cast)?; - format_create_index(db, def.file, create_index) + format_create_index(db, InFile::new(def.file, create_index)) } fn hover_sequence(db: &dyn Db, def: Location) -> Option { @@ -915,7 +944,7 @@ fn hover_sequence(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateSequence::cast)?; - format_create_sequence(db, def.file, create_sequence) + format_create_sequence(db, InFile::new(def.file, create_sequence)) } fn hover_trigger(db: &dyn Db, def: Location) -> Option { @@ -923,7 +952,7 @@ fn hover_trigger(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateTrigger::cast)?; - format_create_trigger(db, def.file, create_trigger) + format_create_trigger(db, InFile::new(def.file, create_trigger)) } fn hover_policy(db: &dyn Db, def: Location) -> Option { @@ -931,7 +960,7 @@ fn hover_policy(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreatePolicy::cast)?; - format_create_policy(db, def.file, create_policy) + format_create_policy(db, InFile::new(def.file, create_policy)) } fn hover_property_graph(db: &dyn Db, def: Location) -> Option { @@ -939,7 +968,7 @@ fn hover_property_graph(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreatePropertyGraph::cast)?; - format_create_property_graph(db, def.file, create_property_graph) + format_create_property_graph(db, InFile::new(def.file, create_property_graph)) } fn hover_event_trigger(db: &dyn Db, def: Location) -> Option { @@ -1023,7 +1052,7 @@ fn hover_type(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateType::cast)?; - format_create_type(db, def.file, create_type) + format_create_type(db, InFile::new(def.file, create_type)) } fn format_declare_cursor(declare: ast::Declare) -> Option { @@ -1053,11 +1082,12 @@ fn format_listen(listen: ast::Listen) -> Option { fn format_create_table( db: &dyn Db, - file: File, - create_table: impl ast::HasCreateTable, + create_table: InFile, ) -> Option { + let file = create_table.file_id; + let create_table = create_table.value; let path = create_table.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let schema = schema.to_string(); let args = create_table.table_arg_list()?.syntax().text().to_string(); @@ -1074,11 +1104,12 @@ fn format_create_table( fn format_create_table_as( db: &dyn Db, - file: File, - create_table_as: ast::CreateTableAs, + create_table_as: InFile, ) -> Option { + let file = create_table_as.file_id; + let create_table_as = create_table_as.value; let path = create_table_as.path()?; - let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let query = create_table_as.query()?.syntax().text().to_string(); Some(Hover::snippet(format!( "table {}.{} as {}", @@ -1088,18 +1119,16 @@ fn format_create_table_as( fn format_create_view(db: &dyn Db, def: Location) -> Option { let create_view = ast::CreateViewLike::cast(def.to_node(db)?)?; - format_create_view_like(db, def.file, create_view) + format_create_view_like(db, InFile::new(def.file, create_view)) } -fn format_create_view_like( - db: &dyn Db, - file: File, - create_view: ast::CreateViewLike, -) -> Option { +fn format_create_view_like(db: &dyn Db, create_view: InFile) -> Option { + let file = create_view.file_id; + let create_view = create_view.value; 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(db, file, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, InFile::new(file, &path))?; let schema = schema.to_string(); let column_list = create_view @@ -1116,19 +1145,19 @@ fn format_create_view_like( }; Some(Hover::snippet(format!( - "{view_kind} {}.{}{} as {}", - schema, view_name, column_list, query + "{view_kind} {schema}.{view_name}{column_list} as {query}", ))) } fn format_view_column( db: &dyn Db, - file: File, - create_view: &ast::CreateViewLike, + create_view: InFile<&ast::CreateViewLike>, def_node: &SyntaxNode, ) -> Option { + let file = create_view.file_id; + let create_view = create_view.value; let path = create_view.path()?; - let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?; + let (schema, view_name) = resolve::resolve_view_info(db, InFile::new(file, &path))?; let column_name = Name::from_string(def_node.to_string()); let ty = collect::view_like_columns_with_types(db, file, create_view) .into_iter() @@ -1163,13 +1192,15 @@ fn format_paren_select(paren_select: ast::ParenSelect) -> Option { Some(Hover::snippet(format!("({})", query))) } -fn format_create_index(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option { +fn format_create_index(db: &dyn Db, create_index: InFile) -> Option { + let file = create_index.file_id; + let create_index = create_index.value; let index_name = create_index.name()?.syntax().text().to_string(); - let index_schema = index_schema(db, file, create_index.clone())?; + let index_schema = index_schema(db, InFile::new(file, create_index.clone()))?; let path = create_index.relation_name()?.path()?; - let (table_schema, table_name) = resolve::resolve_table_info(db, file, &path)?; + let (table_schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &path))?; let partition_item_list = create_index.partition_item_list()?; let columns = partition_item_list.syntax().text().to_string(); @@ -1182,11 +1213,12 @@ fn format_create_index(db: &dyn Db, file: File, create_index: ast::CreateIndex) fn format_create_sequence( db: &dyn Db, - file: File, - create_sequence: ast::CreateSequence, + create_sequence: InFile, ) -> Option { + let file = create_sequence.file_id; + let create_sequence = create_sequence.value; let path = create_sequence.path()?; - let (schema, sequence_name) = resolve::resolve_sequence_info(db, file, &path)?; + let (schema, sequence_name) = resolve::resolve_sequence_info(db, InFile::new(file, &path))?; Some(Hover::snippet(format!( "sequence {}.{}", @@ -1194,30 +1226,26 @@ fn format_create_sequence( ))) } -fn format_create_trigger( - db: &dyn Db, - file: File, - create_trigger: ast::CreateTrigger, -) -> Option { +fn format_create_trigger(db: &dyn Db, create_trigger: InFile) -> Option { + let file = create_trigger.file_id; + let create_trigger = create_trigger.value; 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(db, file, &on_table_path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &on_table_path))?; Some(Hover::snippet(format!( "trigger {}.{} on {}.{}", schema, trigger_name, schema, table_name ))) } -fn format_create_policy( - db: &dyn Db, - file: File, - create_policy: ast::CreatePolicy, -) -> Option { +fn format_create_policy(db: &dyn Db, create_policy: InFile) -> Option { + let file = create_policy.file_id; + let create_policy = create_policy.value; 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(db, file, &on_table_path)?; + let (schema, table_name) = resolve::resolve_table_info(db, InFile::new(file, &on_table_path))?; Some(Hover::snippet(format!( "policy {}.{} on {}.{}", schema, policy_name, schema, table_name @@ -1226,11 +1254,12 @@ fn format_create_policy( fn format_create_property_graph( db: &dyn Db, - file: File, - create_property_graph: ast::CreatePropertyGraph, + create_property_graph: InFile, ) -> Option { + let file = create_property_graph.file_id; + let create_property_graph = create_property_graph.value; let path = create_property_graph.path()?; - let (schema, name) = resolve::resolve_property_graph_info(db, file, &path)?; + let (schema, name) = resolve::resolve_property_graph_info(db, InFile::new(file, &path))?; Some(Hover::snippet(format!( "property graph {}.{}", schema, name @@ -1267,17 +1296,19 @@ fn format_create_role(create_role: ast::CreateRole) -> Option { Some(Hover::snippet(format!("role {}", name))) } -fn index_schema(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option { - let position = create_index.syntax().text_range().start(); - bind(db, file) +fn index_schema(db: &dyn Db, create_index: InFile) -> Option { + let position = create_index.value.syntax().text_range().start(); + bind(db, create_index.file_id) .search_path_at(position) .first() .map(|s| s.to_string()) } -fn format_create_type(db: &dyn Db, file: File, create_type: ast::CreateType) -> Option { +fn format_create_type(db: &dyn Db, create_type: InFile) -> Option { + let file = create_type.file_id; + let create_type = create_type.value; let path = create_type.path()?; - let (schema, type_name) = resolve::resolve_type_info(db, file, &path)?; + let (schema, type_name) = resolve::resolve_type_info(db, InFile::new(file, &path))?; let snippet = if let Some(variant_list) = create_type.variant_list() { let variants = variant_list.syntax().text().to_string(); @@ -1324,7 +1355,7 @@ fn hover_function(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateFunction::cast)?; - format_create_function(db, def.file, create_function) + format_create_function(db, InFile::new(def.file, create_function)) } fn hover_named_arg_parameter(db: &dyn Db, def: Location) -> Option { @@ -1336,7 +1367,8 @@ fn hover_named_arg_parameter(db: &dyn Db, def: Location) -> Option { for ancestor in def_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(db, def.file, &path)?; + let (schema, function_name) = + resolve::resolve_function_info(db, InFile::new(def.file, &path))?; return Some(format_param_hover( schema, function_name, @@ -1346,7 +1378,8 @@ fn hover_named_arg_parameter(db: &dyn Db, def: Location) -> Option { } if let Some(create_procedure) = ast::CreateProcedure::cast(ancestor.clone()) { let path = create_procedure.path()?; - let (schema, procedure_name) = resolve::resolve_procedure_info(db, def.file, &path)?; + let (schema, procedure_name) = + resolve::resolve_procedure_info(db, InFile::new(def.file, &path))?; return Some(format_param_hover( schema, procedure_name, @@ -1356,7 +1389,8 @@ fn hover_named_arg_parameter(db: &dyn Db, def: Location) -> Option { } if let Some(create_aggregate) = ast::CreateAggregate::cast(ancestor) { let path = create_aggregate.path()?; - let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, def.file, &path)?; + let (schema, aggregate_name) = + resolve::resolve_aggregate_info(db, InFile::new(def.file, &path))?; return Some(format_param_hover( schema, aggregate_name, @@ -1390,11 +1424,12 @@ fn format_param_hover( fn format_create_function( db: &dyn Db, - file: File, - create_function: ast::CreateFunction, + create_function: InFile, ) -> Option { + let file = create_function.file_id; + let create_function = create_function.value; let path = create_function.path()?; - let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?; + let (schema, function_name) = resolve::resolve_function_info(db, InFile::new(file, &path))?; let params = create_function.param_list()?.syntax().text().to_string(); let return_type = create_function.ret_type()?.syntax().text().to_string(); @@ -1414,16 +1449,17 @@ fn hover_aggregate(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateAggregate::cast)?; - format_create_aggregate(db, def.file, create_aggregate) + format_create_aggregate(db, InFile::new(def.file, create_aggregate)) } fn format_create_aggregate( db: &dyn Db, - file: File, - create_aggregate: ast::CreateAggregate, + create_aggregate: InFile, ) -> Option { + let file = create_aggregate.file_id; + let create_aggregate = create_aggregate.value; let path = create_aggregate.path()?; - let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, file, &path)?; + let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, InFile::new(file, &path))?; let param_list = create_aggregate.param_list()?; let params = param_list.syntax().text().to_string(); @@ -1439,16 +1475,17 @@ fn hover_procedure(db: &dyn Db, def: Location) -> Option { .to_node(db)? .ancestors() .find_map(ast::CreateProcedure::cast)?; - format_create_procedure(db, def.file, create_procedure) + format_create_procedure(db, InFile::new(def.file, create_procedure)) } fn format_create_procedure( db: &dyn Db, - file: File, - create_procedure: ast::CreateProcedure, + create_procedure: InFile, ) -> Option { + let file = create_procedure.file_id; + let create_procedure = create_procedure.value; let path = create_procedure.path()?; - let (schema, procedure_name) = resolve::resolve_procedure_info(db, file, &path)?; + let (schema, procedure_name) = resolve::resolve_procedure_info(db, InFile::new(file, &path))?; let param_list = create_procedure.param_list()?; let params = param_list.syntax().text().to_string(); @@ -1462,13 +1499,13 @@ fn format_create_procedure( fn hover_routine(db: &dyn Db, def: Location) -> Option { for ancestor in def.to_node(db)?.ancestors() { if let Some(create_function) = ast::CreateFunction::cast(ancestor.clone()) { - return format_create_function(db, def.file, create_function); + return format_create_function(db, InFile::new(def.file, create_function)); } if let Some(create_aggregate) = ast::CreateAggregate::cast(ancestor.clone()) { - return format_create_aggregate(db, def.file, create_aggregate); + return format_create_aggregate(db, InFile::new(def.file, create_aggregate)); } if let Some(create_procedure) = ast::CreateProcedure::cast(ancestor) { - return format_create_procedure(db, def.file, create_procedure); + return format_create_procedure(db, InFile::new(def.file, create_procedure)); } } @@ -1477,9 +1514,10 @@ fn hover_routine(db: &dyn Db, def: Location) -> Option { fn qualified_star_table_ptr( db: &dyn Db, - file: File, - field_expr: ast::FieldExpr, + field_expr: InFile, ) -> Option { + let file = field_expr.file_id; + let field_expr = field_expr.value; let table_name = resolve::qualified_star_table_name(&field_expr)?; let position = field_expr.syntax().text_range().start(); let target = field_expr @@ -1503,11 +1541,10 @@ fn qualified_star_table_ptr( let name_ref = from_item.name_ref(); if let Some((table_like_ptr, _kind)) = resolve::resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) { return Some(table_like_ptr); } @@ -1520,28 +1557,28 @@ fn qualified_star_table_ptr( ast_nav::ParentQuery::Merge(merge) => merge.relation_name()?.path()?, }; - table_or_view_or_cte_ptrs(db, file, position, &path)? + table_or_view_or_cte_ptrs(db, InFile::new(file, &path), position)? .into_iter() .next() } fn table_or_view_or_cte_ptrs( db: &dyn Db, - file: File, + path: InFile<&ast::Path>, position: TextSize, - path: &ast::Path, ) -> Option> { + let file = path.file_id; + let path = path.value; let (schema, table_name) = name::schema_and_name_path(path)?; let mut results = vec![]; let name_ref = path.segment().and_then(|x| x.name_ref()); if let Some((table_like_ptr, _kind)) = resolve::resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) { results.push(table_like_ptr); } @@ -1554,15 +1591,16 @@ fn table_or_view_or_cte_ptrs( fn unqualified_star_table_ptrs( db: &dyn Db, - file: File, - target: &ast::Target, + target: InFile<&ast::Target>, ) -> Option> { + let file = target.file_id; + let target = target.value; target.star_token()?; let path = match ast_nav::target_parent_query(target.clone())? { ast_nav::ParentQuery::Select(select) => { let from_clause = select.from_clause()?; - let results = resolve::table_ptrs_from_clause(db, file, &from_clause); + let results = resolve::table_ptrs_from_clause(db, InFile::new(file, &from_clause)); if results.is_empty() { return None; } @@ -1575,20 +1613,21 @@ fn unqualified_star_table_ptrs( }?; let position = target.syntax().text_range().start(); - table_or_view_or_cte_ptrs(db, file, position, &path) + table_or_view_or_cte_ptrs(db, InFile::new(file, &path), position) } fn unqualified_star_in_arg_list_ptrs( db: &dyn Db, - file: File, - arg_list: &ast::ArgList, + arg_list: InFile<&ast::ArgList>, ) -> Option> { + let file = arg_list.file_id; + let arg_list = arg_list.value; let from_clause = arg_list .syntax() .ancestors() .find_map(ast::Select::cast)? .from_clause()?; - let results = resolve::table_ptrs_from_clause(db, file, &from_clause); + let results = resolve::table_ptrs_from_clause(db, InFile::new(file, &from_clause)); if results.is_empty() { return None; @@ -1599,6 +1638,7 @@ fn unqualified_star_in_arg_list_ptrs( #[cfg(test)] mod test { + use crate::file::InFile; use crate::hover::hover; use crate::test_utils::Fixture; use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle}; @@ -1619,7 +1659,7 @@ mod test { let db = fixture.db(); let file = fixture.file(); - if let Some(type_info) = hover(db, file, offset) { + if let Some(type_info) = hover(db, InFile::new(file, offset)) { let title = format!("hover: {}", type_info.snippet); let group = Level::INFO.primary_title(&title).element( Snippet::source(file.content(db).as_ref()) @@ -1644,7 +1684,8 @@ mod test { let fixture = Fixture::new(sql); let offset = fixture.marker().offset_before(); - hover(fixture.db(), fixture.file(), offset).expect("should find hover information") + hover(fixture.db(), InFile::new(fixture.file(), offset)) + .expect("should find hover information") } #[test] diff --git a/crates/squawk_ide/src/inlay_hints.rs b/crates/squawk_ide/src/inlay_hints.rs index f7bfe96f..c898599f 100644 --- a/crates/squawk_ide/src/inlay_hints.rs +++ b/crates/squawk_ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use crate::collect; use crate::db::{File, parse}; +use crate::file::InFile; use crate::goto_definition; use crate::resolve; use crate::symbols::Name; @@ -19,12 +20,9 @@ pub struct InlayHint { pub position: TextSize, pub label: String, pub kind: InlayHintKind, - // Need this to be an Option because we can still inlay hints when we don't - // have the destination. - // For example: `insert into t(a, b) values (1, 2)` - pub target: Option, - // TODO: combine with the target range above - pub file: Option, + // Optional because we can still emit hints without a destination, + // e.g. `insert into t(a, b) values (1, 2)` with no matching table. + pub target: Option>, } #[salsa::tracked] @@ -55,10 +53,12 @@ fn inlay_hint_call_expr( ast::FieldExpr::cast(expr.syntax().clone())?.field()? }; - let location = - goto_definition::goto_definition(db, file_id, name_ref.syntax().text_range().start()) - .into_iter() - .next()?; + let location = goto_definition::goto_definition( + db, + InFile::new(file_id, name_ref.syntax().text_range().start()), + ) + .into_iter() + .next()?; let def_file = parse(db, location.file).tree(); @@ -72,13 +72,12 @@ fn inlay_hint_call_expr( for (param, arg) in param_list.params().zip(arg_list.args()) { if let Some(param_name) = param.name() { let arg_start = arg.syntax().text_range().start(); - let target = Some(param_name.syntax().text_range()); + let target = Some(InFile::new(location.file, param_name.syntax().text_range())); hints.push(InlayHint { position: arg_start, label: format!("{}: ", param_name.syntax().text()), kind: InlayHintKind::Parameter, target, - file: Some(location.file), }); } } @@ -102,7 +101,7 @@ fn inlay_hint_insert( .start(); // We need to support the table definition not being found since we can // still provide inlay hints when a column list is provided - let location = goto_definition::goto_definition(db, file_id, name_start) + let location = goto_definition::goto_definition(db, InFile::new(file_id, name_start)) .into_iter() .next(); @@ -117,50 +116,56 @@ fn inlay_hint_insert( .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)` - column_list - .columns() - .filter_map(|col| { - let col_name = col.name_ref().map(|x| Name::from_node(&x))?; - let target = create_table - .as_ref() - .and_then(|x| resolve::find_column_in_create_table(db, def_file, x, &col_name)) - .and_then(|x| x.into_iter().next()) - .map(|x| x.range); - Some((col_name, target, location.as_ref().map(|x| x.file))) - }) - .collect() - } else { - // `insert into t values (1, 2, 3)` - collect::columns_from_create_table(db, def_file, &create_table?) - .into_iter() - .map(|(col_name, ptr)| { - let target = ptr.map(|p| p.text_range()); - (col_name, target, location.as_ref().map(|x| x.file)) - }) - .collect() - }; + let columns: Vec<(Name, Option>)> = + if let Some(column_list) = insert.column_list() { + // `insert into t(a, b, c) values (1, 2, 3)` + column_list + .columns() + .filter_map(|col| { + let col_name = col.name_ref().map(|x| Name::from_node(&x))?; + let target = create_table + .as_ref() + .and_then(|x| { + resolve::find_column_in_create_table( + db, + InFile::new(def_file, x), + &col_name, + ) + }) + .and_then(|x| x.into_iter().next()) + .map(|x| InFile::new(x.file, x.range)); + Some((col_name, target)) + }) + .collect() + } else { + // `insert into t values (1, 2, 3)` + collect::columns_from_create_table(db, def_file, &create_table?) + .into_iter() + .map(|(col_name, ptr)| { + let target = ptr.map(|ptr| InFile::new(ptr.file_id, ptr.value.text_range())); + (col_name, target) + }) + .collect() + }; inlay_hint_insert_select(hints, columns, insert.select_variant()?) } fn inlay_hint_insert_select( hints: &mut Vec, - columns: Vec<(Name, Option, Option)>, + columns: Vec<(Name, Option>)>, select_variant: ast::SelectVariant, ) -> Option<()> { if let ast::SelectVariant::Values(values) = &select_variant { // `insert into t values (1, 2);` for row in values.row_list()?.rows() { - for ((column_name, target, file_id), expr) in columns.iter().zip(row.exprs()) { + for ((column_name, target), expr) in columns.iter().zip(row.exprs()) { let expr_start = expr.syntax().text_range().start(); hints.push(InlayHint { position: expr_start, label: format!("{}: ", column_name), kind: InlayHintKind::Parameter, target: *target, - file: *file_id, }); } } @@ -169,7 +174,7 @@ fn inlay_hint_insert_select( // `insert into t select 1, 2;` let target_list = select_variant.target_list()?; - for ((column_name, target, file_id), target_expr) in columns.iter().zip(target_list.targets()) { + for ((column_name, target), target_expr) in columns.iter().zip(target_list.targets()) { let expr = target_expr.expr()?; let expr_start = expr.syntax().text_range().start(); hints.push(InlayHint { @@ -177,7 +182,6 @@ fn inlay_hint_insert_select( label: format!("{}: ", column_name), kind: InlayHintKind::Parameter, target: *target, - file: *file_id, }); } diff --git a/crates/squawk_ide/src/lib.rs b/crates/squawk_ide/src/lib.rs index 1e7f4598..9cb3c8c1 100644 --- a/crates/squawk_ide/src/lib.rs +++ b/crates/squawk_ide/src/lib.rs @@ -10,7 +10,7 @@ pub mod completion; pub mod db; pub mod document_symbols; pub mod expand_selection; -mod file; +pub mod file; pub mod find_references; pub mod folding_ranges; pub mod goto_definition; diff --git a/crates/squawk_ide/src/offsets.rs b/crates/squawk_ide/src/offsets.rs index 14722c0f..267e5f31 100644 --- a/crates/squawk_ide/src/offsets.rs +++ b/crates/squawk_ide/src/offsets.rs @@ -1,13 +1,14 @@ -use crate::db::{File, parse}; +use crate::db::parse; +use crate::file::InFile; use rowan::TextSize; use salsa::Database as Db; use squawk_syntax::{SyntaxKind, SyntaxToken, ast::AstNode}; -pub(crate) fn token_from_offset(db: &dyn Db, file: File, offset: TextSize) -> Option { - let mut token = parse(db, file) +pub(crate) fn token_from_offset(db: &dyn Db, position: InFile) -> Option { + let mut token = parse(db, position.file_id) .tree() .syntax() - .token_at_offset(offset) + .token_at_offset(position.value) .right_biased()?; // want to be lenient in case someone clicks: // - the trailing `;` of a line diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index d35fca62..c8ea5eac 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -8,6 +8,7 @@ use squawk_syntax::{ use crate::column_name::ColumnName; use crate::db::File; +use crate::file::InFile; use crate::location::{Location, LocationKind}; use crate::name::{self, Name, Schema}; use crate::symbols::SymbolKind; @@ -29,9 +30,10 @@ use salsa::Database as Db; /// since `col` is defined in both `t` and `u`. pub(crate) fn resolve_name_ref( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = name_ref.file_id; + let name_ref = name_ref.value; let binder = bind(db, file); let context = classify_name_ref(name_ref.syntax())?; @@ -50,7 +52,12 @@ pub(crate) fn resolve_name_ref( )]); } - let ptr = resolve_table_name_ptr(db, file, &table_name, schema.as_ref(), position)?; + let ptr = resolve_table_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -61,9 +68,12 @@ pub(crate) fn resolve_name_ref( let (schema, relation_name) = name::schema_and_table_name(name_ref)?; let position = name_ref.syntax().text_range().start(); - if let Some(ptr) = - resolve_table_name_ptr(db, file, &relation_name, schema.as_ref(), position) - { + if let Some(ptr) = resolve_table_name_ptr( + db, + &relation_name, + schema.as_ref(), + InFile::new(file, position), + ) { return Some(smallvec![Location::new( file, ptr.text_range(), @@ -71,7 +81,12 @@ pub(crate) fn resolve_name_ref( )]); } - let ptr = resolve_view_name_ptr(db, file, &relation_name, schema.as_ref(), position)?; + let ptr = resolve_view_name_ptr( + db, + &relation_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -116,7 +131,8 @@ pub(crate) fn resolve_name_ref( ) })?; - let param_ptr = find_param_in_func_def(db, file, function_ptr, ¶m_name)?; + let param_ptr = + find_param_in_func_def(db, InFile::new(file, function_ptr), ¶m_name)?; Some(smallvec![Location::new( file, param_ptr.text_range(), @@ -157,11 +173,10 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); let (ptr, kind) = resolve_table_like( db, - file, Some(name_ref), &table_name, schema.as_ref(), - position, + InFile::new(file, position), )?; Some(smallvec![Location::new(file, ptr.text_range(), kind)]) } @@ -180,7 +195,12 @@ pub(crate) fn resolve_name_ref( let (schema, type_name) = name::schema_and_table_name(name_ref)?; let type_name = resolve_float_precision(name_ref, type_name); let position = name_ref.syntax().text_range().start(); - let ptr = resolve_type_name_ptr(db, file, &type_name, schema.as_ref(), position)?; + let ptr = resolve_type_name_ptr( + db, + &type_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -190,7 +210,12 @@ pub(crate) fn resolve_name_ref( NameRefClass::View => { let (schema, view_name) = name::schema_and_table_name(name_ref)?; let position = name_ref.syntax().text_range().start(); - let ptr = resolve_view_name_ptr(db, file, &view_name, schema.as_ref(), position)?; + let ptr = resolve_view_name_ptr( + db, + &view_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -355,7 +380,7 @@ pub(crate) fn resolve_name_ref( 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(db, file, &table_path, column_name) + resolve_column_for_path(db, InFile::new(file, &table_path), column_name) } NameRefClass::Tablespace => { let tablespace_name = Name::from_node(name_ref); @@ -371,7 +396,12 @@ pub(crate) fn resolve_name_ref( let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; let (schema, table_name) = name::schema_and_name_path(&path)?; let position = name_ref.syntax().text_range().start(); - let ptr = resolve_table_name_ptr(db, file, &table_name, schema.as_ref(), position)?; + let ptr = resolve_table_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -396,17 +426,25 @@ pub(crate) fn resolve_name_ref( return None; }; let column_name = Name::from_node(name_ref); - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(file, &path), column_name) } NameRefClass::ConstraintColumn => { 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(db, file, &create_table, &column_name); + return find_column_in_create_table( + db, + InFile::new(file, &create_table), + &column_name, + ); } if let Some(alter_table) = ast::AlterTable::cast(ancestor) { let table_path = alter_table.relation_name()?.path()?; - return resolve_column_for_path(db, file, &table_path, column_name); + return resolve_column_for_path( + db, + InFile::new(file, &table_path), + column_name, + ); } } None @@ -422,7 +460,7 @@ pub(crate) fn resolve_name_ref( } })?; let column_name = Name::from_node(name_ref); - resolve_column_for_path(db, file, &on_table_path, column_name) + resolve_column_for_path(db, InFile::new(file, &on_table_path), column_name) } NameRefClass::PolicyQualifiedColumnTable => { let on_table_path = name_ref.syntax().ancestors().find_map(|n| { @@ -436,7 +474,12 @@ pub(crate) fn resolve_name_ref( })?; let (schema, table_name) = name::schema_and_name_path(&on_table_path)?; let position = name_ref.syntax().text_range().start(); - let ptr = resolve_table_name_ptr(db, file, &table_name, schema.as_ref(), position)?; + let ptr = resolve_table_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -451,18 +494,24 @@ pub(crate) fn resolve_name_ref( let path = like_clause.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; let position = name_ref.syntax().text_range().start(); - if let Some(ptr) = - resolve_table_name_ptr(db, file, &table_name, schema.as_ref(), position) - { + if let Some(ptr) = resolve_table_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { return Some(smallvec![Location::new( file, ptr.text_range(), LocationKind::Table )]); } - if let Some(ptr) = - resolve_view_name_ptr(db, file, &table_name, schema.as_ref(), position) - { + if let Some(ptr) = resolve_view_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) { return Some(smallvec![Location::new( file, ptr.text_range(), @@ -482,11 +531,10 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); resolve_function( db, - file, &function_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) } NameRefClass::Aggregate => { @@ -500,11 +548,10 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); resolve_aggregate( db, - file, &aggregate_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) } NameRefClass::Procedure => { @@ -518,11 +565,10 @@ pub(crate) fn resolve_name_ref( let position = name_ref.syntax().text_range().start(); resolve_procedure( db, - file, &procedure_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) } NameRefClass::Routine => { @@ -537,33 +583,30 @@ pub(crate) fn resolve_name_ref( if let Some(results) = resolve_function( db, - file, &routine_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) { return Some(results); } if let Some(results) = resolve_aggregate( db, - file, &routine_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) { return Some(results); } resolve_procedure( db, - file, &routine_name, schema.as_ref(), params.as_deref(), - position, + InFile::new(file, position), ) } NameRefClass::CallProcedure => { @@ -571,7 +614,13 @@ pub(crate) fn resolve_name_ref( let path = call.path()?; let (schema, procedure_name) = name::schema_and_name_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_procedure(db, file, &procedure_name, schema.as_ref(), None, position) + resolve_procedure( + db, + &procedure_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) } NameRefClass::Schema => { let schema_name = Name::from_node(name_ref); @@ -586,13 +635,25 @@ pub(crate) fn resolve_name_ref( NameRefClass::FunctionCall => { let (schema, function_name) = name::schema_and_name(name_ref); let position = name_ref.syntax().text_range().start(); - resolve_function(db, file, &function_name, schema.as_ref(), None, position) + resolve_function( + db, + &function_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) } NameRefClass::ProcedureCall => { let (schema, procedure_name) = name::schema_and_name(name_ref); let position = name_ref.syntax().text_range().start(); - resolve_procedure(db, file, &procedure_name, schema.as_ref(), None, position) + resolve_procedure( + db, + &procedure_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) } NameRefClass::FunctionName => { let path_type = name_ref @@ -602,23 +663,37 @@ pub(crate) fn resolve_name_ref( let path = path_type.path()?; let (schema, function_name) = name::schema_and_name_path(&path)?; let position = name_ref.syntax().text_range().start(); - resolve_function(db, file, &function_name, schema.as_ref(), None, position) + resolve_function( + db, + &function_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) } NameRefClass::SelectFunctionCall => { let (schema, function_name) = name::schema_and_name(name_ref); let position = name_ref.syntax().text_range().start(); // functions take precedence - if let Some(results) = - resolve_function(db, file, &function_name, schema.as_ref(), None, position) - { + if let Some(results) = resolve_function( + db, + &function_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) { return Some(results); } // aggregates take precedence over function-call-style column access - if let Some(results) = - resolve_aggregate(db, file, &function_name, schema.as_ref(), None, position) - { + if let Some(results) = resolve_aggregate( + db, + &function_name, + schema.as_ref(), + None, + InFile::new(file, position), + ) { return Some(results); } @@ -628,41 +703,44 @@ pub(crate) fn resolve_name_ref( // select a(t) from t; // ``` if schema.is_none() - && let Some(ptr) = resolve_fn_call_column(db, file, name_ref) + && let Some(ptr) = resolve_fn_call_column(db, InFile::new(file, name_ref)) { return Some(ptr); } None } - NameRefClass::CreateIndexColumn => resolve_create_index_column_ptr(db, file, name_ref), - NameRefClass::SelectColumn => resolve_select_column_ptr(db, file, name_ref), + NameRefClass::CreateIndexColumn => { + resolve_create_index_column_ptr(db, InFile::new(file, name_ref)) + } + NameRefClass::SelectColumn => resolve_select_column_ptr(db, InFile::new(file, name_ref)), NameRefClass::SelectGroupByAliasOrColumn => { - resolve_select_group_by_alias_or_column_ptr(db, file, name_ref) + resolve_select_group_by_alias_or_column_ptr(db, InFile::new(file, name_ref)) } NameRefClass::SelectOrderByAliasOrColumn => { - resolve_select_order_by_alias_or_column_ptr(db, file, name_ref) + resolve_select_order_by_alias_or_column_ptr(db, InFile::new(file, name_ref)) } NameRefClass::SelectQualifiedColumnTable => { - resolve_select_qualified_column_table_name_ptr(db, file, name_ref) + resolve_select_qualified_column_table_name_ptr(db, InFile::new(file, name_ref)) } NameRefClass::SelectQualifiedColumn => { - resolve_select_qualified_column_ptr(db, file, name_ref) + resolve_select_qualified_column_ptr(db, InFile::new(file, name_ref)) + } + NameRefClass::CompositeTypeField => { + resolve_composite_type_field_ptr(db, InFile::new(file, name_ref)) } - NameRefClass::CompositeTypeField => resolve_composite_type_field_ptr(db, file, name_ref), NameRefClass::InsertColumn => { let column_name = Name::from_node(name_ref); let insert = name_ref.syntax().ancestors().find_map(ast::Insert::cast)?; let path = insert.path()?; - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(file, &path), column_name) } NameRefClass::InsertQualifiedColumnTable => { let insert = name_ref.syntax().ancestors().find_map(ast::Insert::cast)?; let path = insert.path()?; resolve_table_in_returning_clause( db, - file, - name_ref, + InFile::new(file, name_ref), insert.alias(), &path, insert.returning_clause(), @@ -672,15 +750,14 @@ pub(crate) fn resolve_name_ref( let column_name = Name::from_node(name_ref); let delete = name_ref.syntax().ancestors().find_map(ast::Delete::cast)?; let path = delete.relation_name()?.path()?; - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(file, &path), column_name) } NameRefClass::DeleteQualifiedColumnTable => { let delete = name_ref.syntax().ancestors().find_map(ast::Delete::cast)?; let path = delete.relation_name()?.path()?; resolve_table_in_returning_clause( db, - file, - name_ref, + InFile::new(file, name_ref), delete.alias(), &path, delete.returning_clause(), @@ -693,7 +770,7 @@ pub(crate) fn resolve_name_ref( if let Some(from_clause) = update.from_clause() { for from_item in ast_nav::iter_from_clause(&from_clause) { if let Some(result) = - resolve_from_item_column_ptr(db, file, &from_item, name_ref) + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) { return Some(result); } @@ -701,22 +778,23 @@ pub(crate) fn resolve_name_ref( } // `update t set a = b` let path = update.relation_name()?.path()?; - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(file, &path), column_name) } NameRefClass::UpdateQualifiedColumnTable => { let update = name_ref.syntax().ancestors().find_map(ast::Update::cast)?; let path = update.relation_name()?.path()?; resolve_table_in_returning_clause( db, - file, - name_ref, + InFile::new(file, name_ref), update.alias(), &path, update.returning_clause(), ) } - NameRefClass::MergeColumn => resolve_merge_column_ptr(db, file, name_ref), - NameRefClass::MergeQualifiedColumnTable => resolve_merge_table_name_ptr(db, file, name_ref), + NameRefClass::MergeColumn => resolve_merge_column_ptr(db, InFile::new(file, name_ref)), + NameRefClass::MergeQualifiedColumnTable => { + resolve_merge_table_name_ptr(db, InFile::new(file, name_ref)) + } NameRefClass::JoinUsingColumn => { let join_expr = name_ref .syntax() @@ -726,14 +804,16 @@ pub(crate) fn resolve_name_ref( let mut results: SmallVec<[Location; 1]> = SmallVec::new(); for from_item in ast_nav::iter_join_expr(&join_expr) { if let Some(locations) = - resolve_from_item_column_ptr(db, file, &from_item, name_ref) + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) { results.extend(locations); } } (!results.is_empty()).then_some(results) } - NameRefClass::PropertyGraphColumn => resolve_property_graph_column_ptr(db, file, name_ref), + NameRefClass::PropertyGraphColumn => { + resolve_property_graph_column_ptr(db, InFile::new(file, name_ref)) + } NameRefClass::AlterColumn => { let column_name = Name::from_node(name_ref); let alter_table = name_ref @@ -741,39 +821,37 @@ pub(crate) fn resolve_name_ref( .ancestors() .find_map(ast::AlterTable::cast)?; let table_path = alter_table.relation_name()?.path()?; - resolve_column_for_path(db, file, &table_path, column_name) + resolve_column_for_path(db, InFile::new(file, &table_path), column_name) } } - .or_else(|| resolve_special_keyword_as_function(db, file, name_ref)) + .or_else(|| resolve_special_keyword_as_function(db, InFile::new(file, name_ref))) } fn resolve_table_name_ptr( db: &dyn Db, - file: File, table_name: &Name, schema: Option<&Schema>, - position: TextSize, + position: InFile, ) -> Option { - let binder = bind(db, file); - binder.lookup_with(table_name, SymbolKind::Table, position, schema) + let binder = bind(db, position.file_id); + binder.lookup_with(table_name, SymbolKind::Table, position.value, schema) } fn resolve_type_name_ptr( db: &dyn Db, - file: File, type_name: &Name, schema: Option<&Schema>, - position: TextSize, + position: InFile, ) -> Option { - let binder = bind(db, file); - if let Some(ptr) = binder.lookup_with(type_name, SymbolKind::Type, position, schema) { + let binder = bind(db, position.file_id); + if let Some(ptr) = binder.lookup_with(type_name, SymbolKind::Type, position.value, schema) { return Some(ptr); } if schema.is_none() && let Some(fallback_name) = fallback_type_alias(type_name) { - return binder.lookup_with(&fallback_name, SymbolKind::Type, position, None); + return binder.lookup_with(&fallback_name, SymbolKind::Type, position.value, None); } None @@ -781,12 +859,16 @@ fn resolve_type_name_ptr( pub(crate) fn resolve_type_ptr_from_type( db: &dyn Db, - file: File, - ty: &ast::Type, - position: TextSize, + ty: InFile<&ast::Type>, ) -> Option { - let (schema, type_name) = name::schema_and_type_name(ty)?; - resolve_type_name_ptr(db, file, &type_name, schema.as_ref(), position) + let position = ty.value.syntax().text_range().start(); + let (schema, type_name) = name::schema_and_type_name(ty.value)?; + resolve_type_name_ptr( + db, + &type_name, + schema.as_ref(), + InFile::new(ty.file_id, position), + ) } fn fallback_type_alias(type_name: &Name) -> Option { @@ -818,28 +900,26 @@ fn resolve_float_precision(name_ref: &ast::NameRef, type_name: Name) -> Name { fn resolve_view_name_ptr( db: &dyn Db, - file: File, view_name: &Name, schema: Option<&Schema>, - position: TextSize, + position: InFile, ) -> Option { - let binder = bind(db, file); - binder.lookup_with(view_name, SymbolKind::View, position, schema) + let binder = bind(db, position.file_id); + binder.lookup_with(view_name, SymbolKind::View, position.value, schema) } pub(crate) fn resolve_table_like( db: &dyn Db, - file: File, name_ref: Option<&ast::NameRef>, table_name: &Name, schema: Option<&Schema>, - position: TextSize, + position: InFile, ) -> Option<(SyntaxNodePtr, LocationKind)> { - if let Some(view_name_ptr) = resolve_view_name_ptr(db, file, table_name, schema, position) { + if let Some(view_name_ptr) = resolve_view_name_ptr(db, table_name, schema, position) { return Some((view_name_ptr, LocationKind::View)); } - if let Some(table_name_ptr) = resolve_table_name_ptr(db, file, table_name, schema, position) { + if let Some(table_name_ptr) = resolve_table_name_ptr(db, table_name, schema, position) { return Some((table_name_ptr, LocationKind::Table)); } @@ -855,24 +935,23 @@ pub(crate) fn resolve_table_like( fn resolve_for_kind_with_params( db: &dyn Db, - file: File, name: &Name, schema: Option<&Schema>, params: Option<&[Name]>, - position: TextSize, + position: InFile, kind: SymbolKind, ) -> Option { - let binder = bind(db, file); - binder.lookup_with_params(name, kind, position, schema, params) + let binder = bind(db, position.file_id); + binder.lookup_with_params(name, kind, position.value, schema, params) } // some keywords behave as functions fn resolve_special_keyword_as_function( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, ) -> Option> { let function_name = name_ref + .value .syntax() .first_child_or_token() .and_then(|t| match t.kind() { @@ -883,21 +962,25 @@ fn resolve_special_keyword_as_function( _ => None, })?; let function_name = Name::from_string(function_name); - let position = name_ref.syntax().text_range().start(); - resolve_function(db, file, &function_name, None, None, position) + let position = name_ref.value.syntax().text_range().start(); + resolve_function( + db, + &function_name, + None, + None, + InFile::new(name_ref.file_id, position), + ) } fn resolve_function( db: &dyn Db, - file: File, function_name: &Name, schema: Option<&Schema>, params: Option<&[Name]>, - position: TextSize, + position: InFile, ) -> Option> { let ptr = resolve_for_kind_with_params( db, - file, function_name, schema, params, @@ -905,7 +988,7 @@ fn resolve_function( SymbolKind::Function, )?; Some(smallvec![Location::new( - file, + position.file_id, ptr.text_range(), LocationKind::Function )]) @@ -913,15 +996,13 @@ fn resolve_function( fn resolve_aggregate( db: &dyn Db, - file: File, aggregate_name: &Name, schema: Option<&Schema>, params: Option<&[Name]>, - position: TextSize, + position: InFile, ) -> Option> { let ptr = resolve_for_kind_with_params( db, - file, aggregate_name, schema, params, @@ -929,7 +1010,7 @@ fn resolve_aggregate( SymbolKind::Aggregate, )?; Some(smallvec![Location::new( - file, + position.file_id, ptr.text_range(), LocationKind::Aggregate )]) @@ -937,15 +1018,13 @@ fn resolve_aggregate( fn resolve_procedure( db: &dyn Db, - file: File, procedure_name: &Name, schema: Option<&Schema>, params: Option<&[Name]>, - position: TextSize, + position: InFile, ) -> Option> { let ptr = resolve_for_kind_with_params( db, - file, procedure_name, schema, params, @@ -953,7 +1032,7 @@ fn resolve_procedure( SymbolKind::Procedure, )?; Some(smallvec![Location::new( - file, + position.file_id, ptr.text_range(), LocationKind::Procedure )]) @@ -961,9 +1040,10 @@ fn resolve_procedure( fn resolve_create_index_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let column_name = Name::from_node(column_name_ref); let create_index = column_name_ref @@ -972,14 +1052,15 @@ fn resolve_create_index_column_ptr( .find_map(ast::CreateIndex::cast)?; let path = create_index.relation_name()?.path()?; - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(file, &path), column_name) } fn resolve_property_graph_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let column_name = Name::from_node(column_name_ref); let parent = column_name_ref.syntax().parent()?; @@ -989,17 +1070,31 @@ 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(db, file, &table_name, None, &column_name, position); + return resolve_column_for_table( + db, + &table_name, + None, + &column_name, + InFile::new(file, position), + ); } else if let Some(edge_table_def) = column_list .syntax() .ancestors() .find_map(ast::EdgeTableDef::cast) { - return resolve_column_for_path(db, file, &edge_table_def.path()?, column_name); + return resolve_column_for_path( + db, + InFile::new(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(db, file, &vertex_table_def.path()?, column_name); + return resolve_column_for_path( + db, + InFile::new(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()?) @@ -1007,9 +1102,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(db, file, &edge.path()?, column_name); + return resolve_column_for_path(db, InFile::new(file, &edge.path()?), column_name); } else if let Some(vertex) = ast::VertexTableDef::cast(parent) { - return resolve_column_for_path(db, file, &vertex.path()?, column_name); + return resolve_column_for_path(db, InFile::new(file, &vertex.path()?), column_name); } } @@ -1018,28 +1113,29 @@ fn resolve_property_graph_column_ptr( fn resolve_column_for_path( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, column_name: Name, ) -> Option> { + let file = path.file_id; + let path = path.value; let (schema, table_name) = name::schema_and_name_path(path)?; let position = path.syntax().text_range().start(); resolve_column_for_table( db, - file, &table_name, schema.as_ref(), &column_name, - position, + InFile::new(file, position), ) } fn resolve_select_qualified_column_table_name_ptr( db: &dyn Db, - file: File, - table_name_ref: &ast::NameRef, + table_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = table_name_ref.file_id; + let table_name_ref = table_name_ref.value; let table_name = Name::from_node(table_name_ref); let field_expr = table_name_ref @@ -1084,11 +1180,10 @@ fn resolve_select_qualified_column_table_name_ptr( let position = table_name_ref.syntax().text_range().start(); return resolve_function( db, - file, &function_name, function_schema.as_ref(), None, - position, + InFile::new(file, position), ); } @@ -1100,11 +1195,10 @@ fn resolve_select_qualified_column_table_name_ptr( let position = table_name_ref.syntax().text_range().start(); let (ptr, kind) = resolve_table_like( db, - file, Some(table_name_ref), &table_name, schema.as_ref(), - position, + InFile::new(file, position), )?; Some(smallvec![Location::new(file, ptr.text_range(), kind)]) } @@ -1154,9 +1248,10 @@ fn match_table_in_returning_clause( fn resolve_select_qualified_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let column_name = Name::from_node(column_name_ref); let field_expr = column_name_ref @@ -1181,8 +1276,7 @@ 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( db, - file, - &call_expr, + InFile::new(file, &call_expr), column_name_ref, &column_name, 0, @@ -1200,8 +1294,7 @@ fn resolve_select_qualified_column_ptr( if let Some(paren_select) = from_item.paren_select() { return resolve_subquery_column_ptr( db, - file, - &paren_select, + InFile::new(file, &paren_select), column_name_ref, &column_name, Some(&alias), @@ -1211,8 +1304,7 @@ fn resolve_select_qualified_column_ptr( if let Some(paren_expr) = from_item.paren_expr() { return resolve_column_from_paren_expr( db, - file, - &paren_expr, + InFile::new(file, &paren_expr), column_name_ref, &column_name, ); @@ -1241,8 +1333,7 @@ fn resolve_select_qualified_column_ptr( let cte_name = Name::from_node(&name_ref_node); return resolve_cte_column( db, - file, - column_name_ref, + InFile::new(file, column_name_ref), &cte_name, &column_name, ); @@ -1296,9 +1387,12 @@ 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(db, file, column_name_ref, &table_name, &column_name) - { + if let Some(cte_column_ptr) = resolve_cte_column( + db, + InFile::new(file, column_name_ref), + &table_name, + &column_name, + ) { return Some(cte_column_ptr); } return None; @@ -1310,54 +1404,55 @@ fn resolve_select_qualified_column_ptr( resolve_column_for_table( db, - file, &table_name, schema.as_ref(), &column_name, - position, + InFile::new(file, position), ) } fn resolve_column_for_table( db: &dyn Db, - file: File, table_name: &Name, schema: Option<&Schema>, column_name: &Name, - position: TextSize, + position: InFile, ) -> Option> { - let (file, resolved) = resolve_table_name(db, file, table_name, schema, position)?; - match resolved { + let resolved = resolve_table_name(db, table_name, schema, position)?; + let file = resolved.file_id; + // TODO: this is probably a bug + let position = InFile::new(file, position.value); + match resolved.value { ResolvedTableName::View(create_view) => { if let Some(ptr) = find_column_in_create_view_like(file, &create_view, column_name) { return Some(ptr); } - return resolve_function(db, file, column_name, schema, None, position); + return resolve_function(db, 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(db, file, &create_table_like, column_name) + find_column_in_create_table(db, InFile::new(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(db, file, column_name, schema, None, position); + return resolve_function(db, column_name, schema, None, position); } ResolvedTableName::TableAs(create_table_as) => { if let Some(ptr) = - find_column_in_create_table_as(db, file, &create_table_as, column_name) + find_column_in_create_table_as(db, InFile::new(file, &create_table_as), column_name) { return Some(ptr); } - return resolve_function(db, file, column_name, schema, None, position); + return resolve_function(db, column_name, schema, None, position); } ResolvedTableName::SelectInto(select_into) => { if let Some(ptr) = find_column_in_select_into(file, &select_into, column_name) { return Some(ptr); } - return resolve_function(db, file, column_name, schema, None, position); + return resolve_function(db, column_name, schema, None, position); } } } @@ -1372,15 +1467,20 @@ pub(crate) enum ResolvedTableName { // + goto def setup pub(crate) fn resolve_table_name( db: &dyn Db, - file: File, table_name: &Name, schema: Option<&Schema>, - position: TextSize, -) -> Option<(File, ResolvedTableName)> { + position: InFile, +) -> Option> { use ResolvedTableName::*; - for file in list_files(db, file) { - let Some((ptr, kind)) = resolve_table_like(db, file, None, table_name, schema, position) - else { + for file in list_files(db, position.file_id) { + let Some((ptr, kind)) = resolve_table_like( + db, + None, + table_name, + schema, + // TODO: this is probably a bug + InFile::new(file, position.value), + ) else { continue; }; let tree = parse(db, file).tree(); @@ -1388,18 +1488,18 @@ pub(crate) fn resolve_table_name( match kind { LocationKind::Table => { if let Some(create_table) = node.ancestors().find_map(ast::CreateTableLike::cast) { - return Some((file, Table(create_table))); + return Some(InFile::new(file, Table(create_table))); } if let Some(create_table_as) = node.ancestors().find_map(ast::CreateTableAs::cast) { - return Some((file, TableAs(create_table_as))); + return Some(InFile::new(file, TableAs(create_table_as))); } if let Some(select_into) = node.ancestors().find_map(ast::SelectInto::cast) { - return Some((file, SelectInto(select_into))); + return Some(InFile::new(file, SelectInto(select_into))); } } LocationKind::View => { if let Some(view) = node.ancestors().find_map(ast::CreateViewLike::cast) { - return Some((file, View(view))); + return Some(InFile::new(file, View(view))); } } _ => (), @@ -1425,17 +1525,17 @@ fn resolve_merge_alias(name_ref: &ast::NameRef, table_name: &Name) -> Option, column_name_ref: &ast::NameRef, ) -> Option> { + let file = from_item.file_id; + let from_item = from_item.value; let column_name = Name::from_node(column_name_ref); if let Some(paren_select) = from_item.paren_select() { let alias = from_item.alias(); if let Some(ptr) = resolve_subquery_column_ptr( db, - file, - &paren_select, + InFile::new(file, &paren_select), column_name_ref, &column_name, alias.as_ref(), @@ -1469,9 +1569,12 @@ fn resolve_from_item_column_ptr( } } - if let Some(ptr) = - resolve_column_from_paren_expr(db, file, &paren_expr, column_name_ref, &column_name) - { + if let Some(ptr) = resolve_column_from_paren_expr( + db, + InFile::new(file, &paren_expr), + column_name_ref, + &column_name, + ) { return Some(ptr); } if let Some(alias_name) = from_item.alias().and_then(|x| x.name()) @@ -1506,8 +1609,7 @@ 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( db, - file, - &call_expr, + InFile::new(file, &call_expr), column_name_ref, &column_name, alias_len, @@ -1520,8 +1622,7 @@ fn resolve_from_item_column_ptr( if let Some(ptr) = resolve_column_from_table_or_view_or_cte( db, - file, - column_name_ref, + InFile::new(file, column_name_ref), &table_name, schema.as_ref(), &column_name, @@ -1544,45 +1645,47 @@ fn resolve_from_item_column_ptr( fn resolve_column_from_table_or_view_or_cte( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, table_name: &Name, schema: Option<&Schema>, column_name: &Name, ) -> Option> { - resolve_column_from_table_or_view_or_cte_impl( - db, - file, - name_ref, - table_name, - schema, - column_name, - 0, - ) + resolve_column_from_table_or_view_or_cte_impl(db, name_ref, table_name, schema, column_name, 0) } fn resolve_column_from_table_or_view_or_cte_impl( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, table_name: &Name, schema: Option<&Schema>, column_name: &Name, depth: u32, ) -> Option> { + let file = name_ref.file_id; + let name_ref = name_ref.value; if depth > 40 { log::info!("max resolve depth reached, probably in a cycle"); return None; } let position = name_ref.syntax().text_range().start(); - let (table_like_ptr, kind) = - resolve_table_like(db, file, Some(name_ref), table_name, schema, position)?; + let (table_like_ptr, kind) = resolve_table_like( + db, + Some(name_ref), + table_name, + schema, + InFile::new(file, position), + )?; match kind { LocationKind::Table => { if schema.is_none() && resolve_cte_table(name_ref, table_name).is_some() { - return resolve_cte_column(db, file, name_ref, table_name, column_name); + return resolve_cte_column( + db, + InFile::new(file, name_ref), + table_name, + column_name, + ); } let tree = parse(db, file).tree(); @@ -1591,7 +1694,7 @@ fn resolve_column_from_table_or_view_or_cte_impl( if let Some(create_table) = node.ancestors().find_map(ast::CreateTableLike::cast) { if let Some(cols) = - find_column_in_create_table(db, file, &create_table, column_name) + find_column_in_create_table(db, InFile::new(file, &create_table), column_name) { return Some(cols); } @@ -1605,8 +1708,7 @@ fn resolve_column_from_table_or_view_or_cte_impl( name::schema_and_name_path(&parent_path)?; return resolve_column_from_table_or_view_or_cte_impl( db, - file, - name_ref, + InFile::new(file, name_ref), &parent_table_name, parent_schema.as_ref(), column_name, @@ -1629,9 +1731,11 @@ fn resolve_column_from_table_or_view_or_cte_impl( } if let Some(create_table_as) = node.ancestors().find_map(ast::CreateTableAs::cast) { - if let Some(cols) = - find_column_in_create_table_as(db, file, &create_table_as, column_name) - { + if let Some(cols) = find_column_in_create_table_as( + db, + InFile::new(file, &create_table_as), + column_name, + ) { return Some(cols); } @@ -1688,34 +1792,34 @@ fn resolve_column_from_table_or_view_or_cte_impl( fn resolve_from_item_for_cte_star( db: &dyn Db, - file: File, - from_item: &ast::FromItem, + from_item: InFile<&ast::FromItem>, name_ref: &ast::NameRef, cte_name: &Name, column_name: &Name, ) -> Option> { - if let Some((schema, table_name)) = name::schema_and_table_from_from_item(from_item) + let file = from_item.file_id; + if let Some((schema, table_name)) = name::schema_and_table_from_from_item(from_item.value) && table_name == *cte_name { let position = name_ref.syntax().text_range().start(); return resolve_column_for_table( db, - file, &table_name, schema.as_ref(), column_name, - position, + InFile::new(file, position), ); } - resolve_from_item_column_ptr(db, file, from_item, name_ref) + resolve_from_item_column_ptr(db, from_item, name_ref) } fn resolve_select_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let select = column_name_ref .syntax() .ancestors() @@ -1724,7 +1828,7 @@ fn resolve_select_column_ptr( for from_item in ast_nav::iter_from_clause(&from_clause) { if let Some(column_ptr) = - resolve_from_item_column_ptr(db, file, &from_item, column_name_ref) + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), column_name_ref) { return Some(column_ptr); } @@ -1735,30 +1839,29 @@ fn resolve_select_column_ptr( fn resolve_select_group_by_alias_or_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { - if let Some(ptr) = resolve_select_column_ptr(db, file, column_name_ref) { + if let Some(ptr) = resolve_select_column_ptr(db, column_name_ref) { return Some(ptr); } - resolve_select_target_alias_ptr(file, column_name_ref) + resolve_select_target_alias_ptr(column_name_ref) } fn resolve_select_order_by_alias_or_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { - if let Some(ptr) = resolve_select_target_alias_ptr(file, column_name_ref) { + if let Some(ptr) = resolve_select_target_alias_ptr(column_name_ref) { return Some(ptr); } - resolve_select_column_ptr(db, file, column_name_ref) + resolve_select_column_ptr(db, column_name_ref) } fn resolve_select_target_alias_ptr( - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let select = column_name_ref .syntax() .ancestors() @@ -1782,9 +1885,10 @@ fn resolve_select_target_alias_ptr( fn resolve_fn_call_column( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = name_ref.file_id; + let name_ref = name_ref.value; let column_name = Name::from_node(name_ref); // function call syntax for columns is only valid if there is one argument @@ -1804,8 +1908,7 @@ fn resolve_fn_call_column( if let Some((schema, table_name)) = name::schema_and_table_from_from_item(&from_item) && let Some(result) = resolve_column_from_table_or_view_or_cte( db, - file, - name_ref, + InFile::new(file, name_ref), &table_name, schema.as_ref(), &column_name, @@ -1885,20 +1988,20 @@ fn find_from_item_for_select_qualified_name_ref( pub(crate) fn find_column_in_create_table( db: &dyn Db, - file: File, - create_table: &impl ast::HasCreateTable, + create_table: InFile<&impl ast::HasCreateTable>, column_name: &Name, ) -> Option> { - find_column_in_create_table_impl(db, file, create_table, column_name, 0) + find_column_in_create_table_impl(db, create_table, column_name, 0) } fn find_column_in_create_table_impl( db: &dyn Db, - file: File, - create_table: &impl ast::HasCreateTable, + create_table: InFile<&impl ast::HasCreateTable>, column_name: &Name, depth: usize, ) -> Option> { + let file = create_table.file_id; + let create_table = create_table.value; if depth > 40 { log::info!("max depth reached, probably in a cycle"); return None; @@ -1909,10 +2012,13 @@ fn find_column_in_create_table_impl( ast_nav::CreateTableArg::Inherits(path) => { let (schema, table_name) = name::schema_and_name_path(&path)?; let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - && let Some(ptr) = - find_column_in_resolved(db, lookup_file, resolved, column_name, depth + 1) + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) && let Some(ptr) = + find_column_in_resolved(db, resolved, column_name, depth + 1) { return Some(ptr); } @@ -1932,10 +2038,13 @@ fn find_column_in_create_table_impl( let path = like_clause.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; let position = path.syntax().text_range().start(); - if let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - && let Some(ptr) = - find_column_in_resolved(db, lookup_file, resolved, column_name, depth + 1) + if let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) && let Some(ptr) = + find_column_in_resolved(db, resolved, column_name, depth + 1) { return Some(ptr); } @@ -1949,17 +2058,20 @@ fn find_column_in_create_table_impl( fn find_column_in_resolved( db: &dyn Db, - file: File, - resolved: ResolvedTableName, + resolved: InFile, column_name: &Name, depth: usize, ) -> Option> { - match resolved { - ResolvedTableName::Table(parent_table) => { - find_column_in_create_table_impl(db, file, &parent_table, column_name, depth) - } + let file = resolved.file_id; + match resolved.value { + ResolvedTableName::Table(parent_table) => find_column_in_create_table_impl( + db, + InFile::new(file, &parent_table), + column_name, + depth, + ), ResolvedTableName::TableAs(create_table_as) => { - find_column_in_create_table_as(db, file, &create_table_as, column_name) + find_column_in_create_table_as(db, InFile::new(file, &create_table_as), column_name) } ResolvedTableName::SelectInto(select_into) => { find_column_in_select_into(file, &select_into, column_name) @@ -2018,10 +2130,11 @@ fn find_column_in_create_view_like( fn find_column_in_create_table_as( db: &dyn Db, - file: File, - create_table_as: &ast::CreateTableAs, + create_table_as: InFile<&ast::CreateTableAs>, column_name: &Name, ) -> Option> { + let file = create_table_as.file_id; + let create_table_as = create_table_as.value; let query = create_table_as.query()?; if let ast::SelectVariant::Values(values) = &query { @@ -2041,9 +2154,13 @@ fn find_column_in_create_table_as( let path = table.relation_name()?.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; let position = table.syntax().text_range().start(); - let (lookup_file, resolved) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position)?; - return find_column_in_resolved(db, lookup_file, resolved, column_name, 0); + let resolved = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + )?; + return find_column_in_resolved(db, resolved, column_name, 0); } let select = ast_nav::select_from_variant(query)?; @@ -2053,7 +2170,8 @@ fn find_column_in_create_table_as( for target in target_list.targets() { if target.star_token().is_some() { if let Some(from_clause) = &from_clause - && let Some(result) = find_column_in_from_clause(db, file, from_clause, column_name) + && let Some(result) = + find_column_in_from_clause(db, InFile::new(file, from_clause), column_name) { return Some(result); } @@ -2076,21 +2194,25 @@ fn find_column_in_create_table_as( fn find_column_in_from_clause( db: &dyn Db, - file: File, - from_clause: &ast::FromClause, + from_clause: InFile<&ast::FromClause>, column_name: &Name, ) -> Option> { + let file = from_clause.file_id; + let from_clause = from_clause.value; for from_item in ast_nav::iter_from_clause(from_clause) { let Some((schema, table_name)) = name::schema_and_table_from_from_item(&from_item) else { continue; }; let position = from_item.syntax().text_range().start(); - let Some((lookup_file, resolved)) = - resolve_table_name(db, file, &table_name, schema.as_ref(), position) - else { + let Some(resolved) = resolve_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + ) else { continue; }; - if let Some(result) = find_column_in_resolved(db, lookup_file, resolved, column_name, 0) { + if let Some(result) = find_column_in_resolved(db, resolved, column_name, 0) { return Some(result); } } @@ -2123,24 +2245,31 @@ fn resolve_cte_table(name_ref: &ast::NameRef, cte_name: &Name) -> Option Option { +fn count_columns_for_path(db: &dyn Db, path: InFile<&ast::Path>) -> Option { + let file = path.file_id; + let path = path.value; let (schema, table_name) = name::schema_and_name_path(path)?; let position = path.syntax().text_range().start(); - count_columns_for_table_name(db, file, &table_name, schema.as_ref(), position, None) + count_columns_for_table_name( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + None, + ) } fn count_columns_for_table_name( db: &dyn Db, - file: File, table_name: &Name, schema: Option<&Schema>, - position: TextSize, + position: InFile, name_ref: Option<&ast::NameRef>, ) -> Option { + let file = position.file_id; let tree = parse(db, file).tree(); let root = tree.syntax(); - let (table_like_ptr, kind) = - resolve_table_like(db, file, name_ref, table_name, schema, position)?; + let (table_like_ptr, kind) = resolve_table_like(db, name_ref, table_name, schema, position)?; match kind { LocationKind::Table => { @@ -2231,11 +2360,12 @@ fn count_columns_for_with_table(with_table: ast::WithTable) -> Option { fn resolve_cte_column( db: &dyn Db, - file: File, - name_ref: &ast::NameRef, + name_ref: InFile<&ast::NameRef>, cte_name: &Name, column_name: &Name, ) -> Option> { + let file = name_ref.file_id; + let name_ref = name_ref.value; let with_table = ast_nav::find_cte_with_table(name_ref, cte_name)?; let column_list_len = if let Some(column_list) = with_table.column_list() { @@ -2283,15 +2413,16 @@ fn resolve_cte_column( return resolve_column_from_table_or_view_or_cte( db, - file, - name_ref, + InFile::new(file, name_ref), &table_name, schema.as_ref(), column_name, ); } - if let Some(column) = column_in_with_query(&query, db, file, column_name, column_list_len) { + if let Some(column) = + column_in_with_query(InFile::new(file, &query), db, column_name, column_list_len) + { return Some(column); } @@ -2305,7 +2436,7 @@ fn resolve_cte_column( let target_column_count = from_clause .as_ref() .and_then(|from_clause| { - count_columns_for_target(db, file, name_ref, &target, from_clause) + count_columns_for_target(db, InFile::new(file, &target), name_ref, from_clause) }) .unwrap_or(1); let column_list_end = column_index.saturating_add(target_column_count); @@ -2329,11 +2460,10 @@ fn resolve_cte_column( && let Some(from_clause) = &from_clause && let Some(result) = resolve_from_clause_for_cte_star( db, - file, + InFile::new(file, from_clause), name_ref, cte_name, column_name, - from_clause, ) { return Some(result); @@ -2344,11 +2474,10 @@ fn resolve_cte_column( && let Some(from_clause) = &from_clause && let Some(result) = resolve_qualified_star_in_from_clause( db, - file, + InFile::new(file, from_clause), name_ref, cte_name, column_name, - from_clause, &table_name, ) { @@ -2372,12 +2501,13 @@ fn resolve_cte_column( } fn column_in_with_query( - query: &ast::WithQuery, + query: InFile<&ast::WithQuery>, db: &dyn Db, - file: File, column_name: &Name, column_list_len: usize, ) -> Option> { + let file = query.file_id; + let query = query.value; let (returning_clause, path) = match query { ast::WithQuery::Delete(delete) => { (delete.returning_clause(), delete.relation_name()?.path()?) @@ -2397,7 +2527,7 @@ fn column_in_with_query( let mut column_index: usize = 0; for target in returning_clause?.target_list()?.targets() { let target_column_count = if target.star_token().is_some() { - count_columns_for_path(db, file, &path).unwrap_or(1) + count_columns_for_path(db, InFile::new(file, &path)).unwrap_or(1) } else { 1 }; @@ -2419,7 +2549,8 @@ fn column_in_with_query( )]); } if matches!(col_name, ColumnName::Star) - && let Some(ptr) = resolve_column_for_path(db, file, &path, column_name.clone()) + && let Some(ptr) = + resolve_column_for_path(db, InFile::new(file, &path), column_name.clone()) { return Some(ptr); } @@ -2432,12 +2563,13 @@ fn column_in_with_query( fn resolve_subquery_column_ptr( db: &dyn Db, - file: File, - paren_select: &ast::ParenSelect, + paren_select: InFile<&ast::ParenSelect>, name_ref: &ast::NameRef, column_name: &Name, alias: Option<&ast::Alias>, ) -> Option> { + let file = paren_select.file_id; + let paren_select = paren_select.value; let select_variant = paren_select.select()?; let column_list_len = if let Some(column_list) = alias.and_then(|x| x.column_list()) { @@ -2467,8 +2599,7 @@ fn resolve_subquery_column_ptr( return resolve_column_from_table_or_view_or_cte( db, - file, - name_ref, + InFile::new(file, name_ref), &table_name, schema.as_ref(), column_name, @@ -2495,8 +2626,7 @@ fn resolve_subquery_column_ptr( let subquery_select = ast_nav::select_from_variant(select_variant)?; resolve_column_from_select_targets( db, - file, - &subquery_select, + InFile::new(file, &subquery_select), name_ref, column_name, column_list_len, @@ -2505,12 +2635,13 @@ fn resolve_subquery_column_ptr( fn resolve_column_from_select_targets( db: &dyn Db, - file: File, - select: &ast::Select, + select: InFile<&ast::Select>, name_ref: &ast::NameRef, column_name: &Name, skip_target_count: usize, ) -> Option> { + let file = select.file_id; + let select = select.value; let from_clause = select.from_clause(); for (i, target) in select.select_clause()?.target_list()?.targets().enumerate() { @@ -2532,7 +2663,7 @@ fn resolve_column_from_select_targets( { for from_item in ast_nav::iter_from_clause(from_clause) { if let Some(result) = - resolve_from_item_column_ptr(db, file, &from_item, name_ref) + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) { return Some(result); } @@ -2544,7 +2675,8 @@ 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(db, file, &from_item, name_ref) + && let Some(result) = + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) { return Some(result); } @@ -2570,9 +2702,10 @@ pub(crate) fn qualified_star_table_name(field_expr: &ast::FieldExpr) -> Option, ) -> Vec { + let file = from_clause.file_id; + let from_clause = from_clause.value; let mut results = vec![]; for from_item in ast_nav::iter_from_clause(from_clause) { @@ -2596,11 +2729,10 @@ pub(crate) fn table_ptrs_from_clause( let position = from_item.syntax().text_range().start(); if let Some((table_like_ptr, _kind)) = resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) { results.push(table_like_ptr); } @@ -2611,15 +2743,16 @@ pub(crate) fn table_ptrs_from_clause( pub(crate) fn table_ptr_from_from_item( db: &dyn Db, - file: File, - from_item: &ast::FromItem, + from_item: InFile<&ast::FromItem>, ) -> Option { + let file = from_item.file_id; + let from_item = from_item.value; if let Some(paren_select) = from_item.paren_select() { return Some(SyntaxNodePtr::new(paren_select.syntax())); } if let Some(paren_expr) = from_item.paren_expr() { - return table_ptr_from_paren_expr(db, file, &paren_expr); + return table_ptr_from_paren_expr(db, InFile::new(file, &paren_expr)); } let (schema, table_name) = name::schema_and_table_from_from_item(from_item)?; @@ -2628,45 +2761,46 @@ pub(crate) fn table_ptr_from_from_item( resolve_table_like( db, - file, name_ref.as_ref(), &table_name, schema.as_ref(), - position, + InFile::new(file, position), ) .map(|(table_like_ptr, _kind)| table_like_ptr) } fn table_ptr_from_paren_expr( db: &dyn Db, - file: File, - paren_expr: &ast::ParenExpr, + paren_expr: InFile<&ast::ParenExpr>, ) -> Option { + let file = paren_expr.file_id; + let paren_expr = paren_expr.value; if let Some(from_item) = paren_expr.from_item() { - return table_ptr_from_from_item(db, file, &from_item); + return table_ptr_from_from_item(db, InFile::new(file, &from_item)); } if let Some(ast::Expr::ParenExpr(inner)) = paren_expr.expr() { - return table_ptr_from_paren_expr(db, file, &inner); + return table_ptr_from_paren_expr(db, InFile::new(file, &inner)); } None } fn count_columns_for_target( db: &dyn Db, - file: File, + target: InFile<&ast::Target>, name_ref: &ast::NameRef, - target: &ast::Target, from_clause: &ast::FromClause, ) -> Option { + let file = target.file_id; + let target = target.value; if target.star_token().is_some() { - return count_columns_for_from_clause(db, file, name_ref, from_clause); + return count_columns_for_from_clause(db, InFile::new(file, from_clause), name_ref); } if let Some(ast::Expr::FieldExpr(field_expr)) = target.expr() && 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(db, file, name_ref, &from_item); + return count_columns_for_from_item(db, InFile::new(file, &from_item), name_ref); } Some(1) @@ -2674,15 +2808,18 @@ fn count_columns_for_target( fn count_columns_for_from_clause( db: &dyn Db, - file: File, + from_clause: InFile<&ast::FromClause>, name_ref: &ast::NameRef, - from_clause: &ast::FromClause, ) -> Option { + let file = from_clause.file_id; + let from_clause = from_clause.value; let mut total: usize = 0; let mut found = false; for from_item in ast_nav::iter_from_clause(from_clause) { - if let Some(count) = count_columns_for_from_item(db, file, name_ref, &from_item) { + if let Some(count) = + count_columns_for_from_item(db, InFile::new(file, &from_item), name_ref) + { total = total.saturating_add(count); found = true; } @@ -2693,34 +2830,38 @@ fn count_columns_for_from_clause( fn count_columns_for_from_item( db: &dyn Db, - file: File, + from_item: InFile<&ast::FromItem>, name_ref: &ast::NameRef, - from_item: &ast::FromItem, ) -> Option { + let file = from_item.file_id; + let from_item = from_item.value; let (schema, table_name) = name::schema_and_table_from_from_item(from_item)?; let position = name_ref.syntax().text_range().start(); count_columns_for_table_name( db, - file, &table_name, schema.as_ref(), - position, + InFile::new(file, position), Some(name_ref), ) } fn resolve_from_clause_for_cte_star( db: &dyn Db, - file: File, + from_clause: InFile<&ast::FromClause>, name_ref: &ast::NameRef, cte_name: &Name, column_name: &Name, - from_clause: &ast::FromClause, ) -> Option> { - for from_item in ast_nav::iter_from_clause(from_clause) { - if let Some(result) = - resolve_from_item_for_cte_star(db, file, &from_item, name_ref, cte_name, column_name) - { + let file = from_clause.file_id; + for from_item in ast_nav::iter_from_clause(from_clause.value) { + if let Some(result) = resolve_from_item_for_cte_star( + db, + InFile::new(file, &from_item), + name_ref, + cte_name, + column_name, + ) { return Some(result); } } @@ -2730,33 +2871,45 @@ fn resolve_from_clause_for_cte_star( fn resolve_qualified_star_in_from_clause( db: &dyn Db, - file: File, + from_clause: InFile<&ast::FromClause>, name_ref: &ast::NameRef, cte_name: &Name, column_name: &Name, - from_clause: &ast::FromClause, table_name: &Name, ) -> Option> { - let from_item = find_from_item_in_from_clause(from_clause, table_name)?; - resolve_from_item_for_cte_star(db, file, &from_item, name_ref, cte_name, column_name) + let file = from_clause.file_id; + let from_item = find_from_item_in_from_clause(from_clause.value, table_name)?; + resolve_from_item_for_cte_star( + db, + InFile::new(file, &from_item), + name_ref, + cte_name, + column_name, + ) } fn resolve_column_from_paren_expr( db: &dyn Db, - file: File, - paren_expr: &ast::ParenExpr, + paren_expr: InFile<&ast::ParenExpr>, name_ref: &ast::NameRef, column_name: &Name, ) -> Option> { + let file = paren_expr.file_id; + let paren_expr = paren_expr.value; if let Some(select) = paren_expr.select() { - return resolve_column_from_select_targets(db, file, &select, name_ref, column_name, 0); + return resolve_column_from_select_targets( + db, + InFile::new(file, &select), + name_ref, + column_name, + 0, + ); } if let Some(ast::Expr::CallExpr(call_expr)) = paren_expr.expr() && let Some(result) = resolve_column_from_call_expr_return_table( db, - file, - &call_expr, + InFile::new(file, &call_expr), name_ref, column_name, 0, @@ -2766,7 +2919,12 @@ fn resolve_column_from_paren_expr( } if let Some(ast::Expr::ParenExpr(paren_expr)) = paren_expr.expr() { - return resolve_column_from_paren_expr(db, file, &paren_expr, name_ref, column_name); + return resolve_column_from_paren_expr( + db, + InFile::new(file, &paren_expr), + name_ref, + column_name, + ); } if let Some(from_item) = paren_expr.from_item() { @@ -2774,8 +2932,7 @@ fn resolve_column_from_paren_expr( let alias = from_item.alias(); return resolve_subquery_column_ptr( db, - file, - &paren_select, + InFile::new(file, &paren_select), name_ref, column_name, alias.as_ref(), @@ -2788,14 +2945,19 @@ fn resolve_column_from_paren_expr( .find_map(ast::SelectVariant::cast) { let select = ast_nav::select_from_variant(select_variant)?; - return resolve_column_from_select_targets(db, file, &select, name_ref, column_name, 0); + return resolve_column_from_select_targets( + db, + InFile::new(file, &select), + name_ref, + column_name, + 0, + ); } if let Some(nested_paren_expr) = from_item.paren_expr() { return resolve_column_from_paren_expr( db, - file, - &nested_paren_expr, + InFile::new(file, &nested_paren_expr), name_ref, column_name, ); @@ -2807,16 +2969,22 @@ fn resolve_column_from_paren_expr( fn resolve_column_from_call_expr_return_table( db: &dyn Db, - file: File, - call_expr: &ast::CallExpr, + call_expr: InFile<&ast::CallExpr>, name_ref: &ast::NameRef, column_name: &Name, min_index: usize, ) -> Option> { + let file = call_expr.file_id; + let call_expr = call_expr.value; let position = name_ref.syntax().text_range().start(); let (schema, function_name) = name::schema_and_func_name(call_expr)?; - let function_locs = - resolve_function(db, file, &function_name, schema.as_ref(), None, position)?; + let function_locs = resolve_function( + db, + &function_name, + schema.as_ref(), + None, + InFile::new(file, position), + )?; let function_node = function_locs.first()?.to_node(db)?; let create_function = function_node .ancestors() @@ -2842,78 +3010,63 @@ fn resolve_column_from_call_expr_return_table( pub(crate) fn resolve_table_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::Table) + resolve_symbol_info(db, path, SymbolKind::Table) } pub(crate) fn resolve_function_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::Function) + resolve_symbol_info(db, path, SymbolKind::Function) } pub(crate) fn resolve_aggregate_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::Aggregate) + resolve_symbol_info(db, path, SymbolKind::Aggregate) } pub(crate) fn resolve_procedure_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::Procedure) + resolve_symbol_info(db, path, SymbolKind::Procedure) } -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_type_info(db: &dyn Db, path: InFile<&ast::Path>) -> Option<(Schema, String)> { + resolve_symbol_info(db, path, SymbolKind::Type) } pub(crate) fn resolve_property_graph_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::PropertyGraph) + resolve_symbol_info(db, path, SymbolKind::PropertyGraph) } -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_view_info(db: &dyn Db, path: InFile<&ast::Path>) -> Option<(Schema, String)> { + resolve_symbol_info(db, path, SymbolKind::View) } pub(crate) fn resolve_sequence_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, ) -> Option<(Schema, String)> { - resolve_symbol_info(db, file, path, SymbolKind::Sequence) + resolve_symbol_info(db, path, SymbolKind::Sequence) } fn resolve_symbol_info( db: &dyn Db, - file: File, - path: &ast::Path, + path: InFile<&ast::Path>, kind: SymbolKind, ) -> Option<(Schema, String)> { - let binder = bind(db, file); - let name = name::table_name(path)?; - let schema = name::schema_name(path); - let position = path.syntax().text_range().start(); + let binder = bind(db, path.file_id); + let name = name::table_name(path.value)?; + let schema = name::schema_name(path.value); + let position = path.value.syntax().text_range().start(); binder.lookup_info(&name, schema.as_ref(), kind, position) } @@ -2934,9 +3087,10 @@ fn param_signature(node: &impl ast::HasParamList) -> Option> { fn resolve_composite_type_field_ptr( db: &dyn Db, - file: File, - field_name_ref: &ast::NameRef, + field_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = field_name_ref.file_id; + let field_name_ref = field_name_ref.value; let tree = parse(db, file).tree(); let root = tree.syntax(); let field_name = Name::from_node(field_name_ref); @@ -2947,8 +3101,12 @@ fn resolve_composite_type_field_ptr( let base = field_expr.base()?; if let ast::Expr::ParenExpr(ref paren_expr) = base - && let Some(result) = - resolve_column_from_paren_expr(db, file, paren_expr, field_name_ref, &field_name) + && let Some(result) = resolve_column_from_paren_expr( + db, + InFile::new(file, paren_expr), + field_name_ref, + &field_name, + ) { return Some(result); } @@ -2958,14 +3116,15 @@ fn resolve_composite_type_field_ptr( _ => None, })?; - let column_locs = resolve_select_column_ptr(db, file, &base_name_ref)?; + let column_locs = resolve_select_column_ptr(db, InFile::new(file, &base_name_ref))?; let column_node = column_locs.first()?.to_node(db)?; let (schema, type_name) = resolve_composite_type_from_column_node(&column_node) .or_else(|| resolve_composite_type_from_cast_node(&column_node))?; let position = field_name_ref.syntax().text_range().start(); - let type_name_ptr = resolve_type_name_ptr(db, file, &type_name, schema.as_ref(), position)?; + let type_name_ptr = + resolve_type_name_ptr(db, &type_name, schema.as_ref(), InFile::new(file, position))?; let type_node = type_name_ptr.to_node(root); let create_type = type_node.ancestors().find_map(ast::CreateType::cast)?; @@ -3005,9 +3164,10 @@ fn resolve_composite_type_from_cast_node( fn resolve_merge_column_ptr( db: &dyn Db, - file: File, - column_name_ref: &ast::NameRef, + column_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = column_name_ref.file_id; + let column_name_ref = column_name_ref.value; let column_name = Name::from_node(column_name_ref); let merge = column_name_ref .syntax() @@ -3016,25 +3176,27 @@ fn resolve_merge_column_ptr( // Try resolving in source table first if let Some(from_item) = merge.using_on_clause().and_then(|x| x.from_item()) - && let Some(ptr) = resolve_from_item_column_ptr(db, file, &from_item, column_name_ref) + && let Some(ptr) = + resolve_from_item_column_ptr(db, InFile::new(file, &from_item), column_name_ref) { return Some(ptr); } let path = merge.relation_name()?.path()?; - resolve_column_for_path(db, file, &path, column_name) + resolve_column_for_path(db, InFile::new(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( db: &dyn Db, - file: File, - table_name_ref: &ast::NameRef, + table_name_ref: InFile<&ast::NameRef>, alias: Option, path: &ast::Path, returning_clause: Option, ) -> Option> { + let file = table_name_ref.file_id; + let table_name_ref = table_name_ref.value; let table_name = Name::from_node(table_name_ref); let (schema, stmt_table_name) = name::schema_and_name_path(path)?; @@ -3055,8 +3217,12 @@ fn resolve_table_in_returning_clause( LocationKind::Table )]), ReturningClauseMatch::PseudoTable => { - let ptr = - resolve_table_name_ptr(db, file, &stmt_table_name, schema.as_ref(), position)?; + let ptr = resolve_table_name_ptr( + db, + &stmt_table_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -3064,7 +3230,12 @@ fn resolve_table_in_returning_clause( )]) } ReturningClauseMatch::Table => { - let ptr = resolve_table_name_ptr(db, file, &table_name, schema.as_ref(), position)?; + let ptr = resolve_table_name_ptr( + db, + &table_name, + schema.as_ref(), + InFile::new(file, position), + )?; Some(smallvec![Location::new( file, ptr.text_range(), @@ -3076,9 +3247,10 @@ fn resolve_table_in_returning_clause( fn resolve_merge_table_name_ptr( db: &dyn Db, - file: File, - table_name_ref: &ast::NameRef, + table_name_ref: InFile<&ast::NameRef>, ) -> Option> { + let file = table_name_ref.file_id; + let table_name_ref = table_name_ref.value; let table_name = Name::from_node(table_name_ref); let merge = table_name_ref .syntax() @@ -3093,8 +3265,13 @@ fn resolve_merge_table_name_ptr( let item_name = Name::from_node(&item_name_ref); if item_name == table_name { let position = table_name_ref.syntax().text_range().start(); - let (ptr, kind) = - resolve_table_like(db, file, Some(table_name_ref), &item_name, None, position)?; + let (ptr, kind) = resolve_table_like( + db, + Some(table_name_ref), + &item_name, + None, + InFile::new(file, position), + )?; return Some(smallvec![Location::new(file, ptr.text_range(), kind)]); } } @@ -3112,8 +3289,7 @@ fn resolve_merge_table_name_ptr( resolve_table_in_returning_clause( db, - file, - table_name_ref, + InFile::new(file, table_name_ref), merge.alias(), &path, merge.returning_clause(), @@ -3122,10 +3298,11 @@ fn resolve_merge_table_name_ptr( fn find_param_in_func_def( db: &dyn Db, - file: File, - function_ptr: SyntaxNodePtr, + function_ptr: InFile, param_name: &Name, ) -> Option { + let file = function_ptr.file_id; + let function_ptr = function_ptr.value; let tree = parse(db, file).tree(); let root = tree.syntax(); let function_node = function_ptr.to_node(root); diff --git a/crates/squawk_ide/src/semantic_tokens.rs b/crates/squawk_ide/src/semantic_tokens.rs index f38ef626..964da682 100644 --- a/crates/squawk_ide/src/semantic_tokens.rs +++ b/crates/squawk_ide/src/semantic_tokens.rs @@ -6,6 +6,7 @@ use squawk_syntax::{ }; use crate::db::{File, parse}; +use crate::file::InFile; use crate::goto_definition::goto_definition; use crate::location::LocationKind; @@ -215,9 +216,11 @@ impl TryFrom for SemanticTokenType { } } -fn token_type_for_node(db: &dyn Db, file: File, node: &T) -> Option { - let offset = node.syntax().text_range().start(); - let location = goto_definition(db, file, offset).into_iter().next()?; +fn token_type_for_node(db: &dyn Db, node: InFile<&T>) -> Option { + let offset = node.value.syntax().text_range().start(); + let location = goto_definition(db, InFile::new(node.file_id, offset)) + .into_iter() + .next()?; SemanticTokenType::try_from(location.kind).ok() } @@ -295,13 +298,13 @@ pub fn semantic_tokens( match event { Enter(NodeOrToken::Node(node)) => { if let Some(name) = ast::Name::cast(node.clone()) - && let Some(token_type) = token_type_for_node(db, file, &name) + && let Some(token_type) = token_type_for_node(db, InFile::new(file, &name)) { out.push_token(name.syntax().clone().into(), token_type); } if let Some(name_ref) = ast::NameRef::cast(node.clone()) - && let Some(token_type) = token_type_for_node(db, file, &name_ref) + && let Some(token_type) = token_type_for_node(db, InFile::new(file, &name_ref)) { out.push_token(name_ref.syntax().clone().into(), token_type); } diff --git a/crates/squawk_server/src/handlers/code_action.rs b/crates/squawk_server/src/handlers/code_action.rs index bc969b2f..89a2086e 100644 --- a/crates/squawk_server/src/handlers/code_action.rs +++ b/crates/squawk_server/src/handlers/code_action.rs @@ -22,9 +22,9 @@ pub(crate) fn handle_code_action( let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); let line_index = line_index(db, file); - let offset = lsp_utils::offset(&line_index, params.range.start).unwrap(); + let position = lsp_utils::offset(db, file, params.range.start).unwrap(); - let ide_actions = code_actions(db, file, offset).unwrap_or_default(); + let ide_actions = code_actions(db, position).unwrap_or_default(); for action in ide_actions { let lsp_action = lsp_utils::code_action(&line_index, uri.clone(), action); diff --git a/crates/squawk_server/src/handlers/completion.rs b/crates/squawk_server/src/handlers/completion.rs index 9a33460f..b757d810 100644 --- a/crates/squawk_server/src/handlers/completion.rs +++ b/crates/squawk_server/src/handlers/completion.rs @@ -1,7 +1,6 @@ use anyhow::Result; use lsp_types::{CompletionParams, CompletionResponse}; use squawk_ide::completion::completion; -use squawk_ide::db::line_index; use crate::global_state::Snapshot; use crate::lsp_utils; @@ -15,13 +14,12 @@ pub(crate) fn handle_completion( let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); - let line_index = line_index(db, file); - let Some(offset) = lsp_utils::offset(&line_index, position) else { + let Some(position) = lsp_utils::offset(db, file, position) else { return Ok(Some(CompletionResponse::Array(vec![]))); }; - let completion_items = completion(db, file, offset) + let completion_items = completion(db, position) .into_iter() .map(lsp_utils::completion_item) .collect(); diff --git a/crates/squawk_server/src/handlers/goto_definition.rs b/crates/squawk_server/src/handlers/goto_definition.rs index 6287951f..1809dceb 100644 --- a/crates/squawk_server/src/handlers/goto_definition.rs +++ b/crates/squawk_server/src/handlers/goto_definition.rs @@ -1,6 +1,5 @@ use anyhow::Result; use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse}; -use squawk_ide::db::line_index; use squawk_ide::goto_definition::goto_definition; use crate::global_state::Snapshot; @@ -15,14 +14,13 @@ pub(crate) fn handle_goto_definition( let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); - let line_index = line_index(db, file); - let offset = lsp_utils::offset(&line_index, position).unwrap(); + let position = lsp_utils::offset(db, file, position).unwrap(); - let ranges = goto_definition(db, file, offset) + let ranges = goto_definition(db, position) .into_iter() .filter_map(|location| { debug_assert!( - !location.range.contains(offset), + !location.range.contains(position.value), "Our target destination range must not include the source range otherwise go to def won't work in vscode." ); to_location(snapshot, location) diff --git a/crates/squawk_server/src/handlers/hover.rs b/crates/squawk_server/src/handlers/hover.rs index ba5a143a..8e8bb0a5 100644 --- a/crates/squawk_server/src/handlers/hover.rs +++ b/crates/squawk_server/src/handlers/hover.rs @@ -1,6 +1,5 @@ use anyhow::Result; use lsp_types::{Hover, HoverContents, HoverParams, MarkedString}; -use squawk_ide::db::line_index; use squawk_ide::hover::hover; use crate::global_state::Snapshot; @@ -12,10 +11,9 @@ pub(crate) fn handle_hover(snapshot: &Snapshot, params: HoverParams) -> Result snapshot.uri(target_file)?, - None => uri.clone(), - }; - - 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, squawk_ide::inlay_hints::InlayHintKind::Type => InlayHintKind::TYPE, }; - let label = if let Some(target_range) = hint.target { + let label = if let Some(target) = hint.target { + let target_uri = snapshot.uri(target.file_id)?; + let target_line_index = line_index(db, target.file_id); InlayHintLabel::LabelParts(vec![InlayHintLabelPart { value: hint.label, location: Some(Location { - uri: uri.clone(), - range: lsp_utils::range(line_index, target_range), + uri: target_uri, + range: lsp_utils::range(&target_line_index, target.value), }), tooltip: None, command: None, diff --git a/crates/squawk_server/src/handlers/references.rs b/crates/squawk_server/src/handlers/references.rs index 27a7944d..9124c000 100644 --- a/crates/squawk_server/src/handlers/references.rs +++ b/crates/squawk_server/src/handlers/references.rs @@ -1,6 +1,5 @@ use anyhow::Result; use lsp_types::{Location, ReferenceParams}; -use squawk_ide::db::line_index; use squawk_ide::find_references::find_references; use crate::global_state::Snapshot; @@ -15,10 +14,9 @@ pub(crate) fn handle_references( let db = snapshot.db(); let file = snapshot.file(&uri).unwrap(); - let line_index = line_index(db, file); - let offset = lsp_utils::offset(&line_index, position).unwrap(); + let position = lsp_utils::offset(db, file, position).unwrap(); - let refs = find_references(db, file, offset); + let refs = find_references(db, position); let include_declaration = params.context.include_declaration; let locations: Vec = refs @@ -27,7 +25,7 @@ pub(crate) fn handle_references( if include_declaration { return true; } - if loc.file == file && !loc.range.contains(offset) { + if loc.file == file && !loc.range.contains(position.value) { return true; } false diff --git a/crates/squawk_server/src/handlers/selection_range.rs b/crates/squawk_server/src/handlers/selection_range.rs index 39b239e5..9376f629 100644 --- a/crates/squawk_server/src/handlers/selection_range.rs +++ b/crates/squawk_server/src/handlers/selection_range.rs @@ -21,9 +21,10 @@ pub(crate) fn handle_selection_range( let mut selection_ranges = vec![]; for position in params.positions { - let Some(offset) = lsp_utils::offset(&line_index, position) else { + let Some(position) = lsp_utils::offset(db, file, position) else { continue; }; + let offset = position.value; let mut ranges = vec![]; { diff --git a/crates/squawk_server/src/lsp_utils.rs b/crates/squawk_server/src/lsp_utils.rs index 912f9060..15e622ea 100644 --- a/crates/squawk_server/src/lsp_utils.rs +++ b/crates/squawk_server/src/lsp_utils.rs @@ -8,8 +8,10 @@ use lsp_types::{ CodeAction, CodeActionKind, FoldingRange, FoldingRangeKind as LspFoldingRangeKind, Location, SemanticToken, Url, WorkspaceEdit, }; +use salsa::Database as Db; use squawk_ide::code_actions::ActionKind; -use squawk_ide::db::line_index; +use squawk_ide::db::{File, line_index}; +use squawk_ide::file::InFile; use squawk_ide::folding_ranges::{Fold, FoldKind}; use squawk_ide::semantic_tokens::{SemanticTokenModifier, SemanticTokenType}; @@ -17,8 +19,8 @@ use crate::global_state::Snapshot; use crate::semantic_tokens; pub(crate) fn text_range(index: &LineIndex, range: lsp_types::Range) -> Option { - let start = offset(index, range.start)?; - let end = offset(index, range.end)?; + let start = text_size(index, range.start)?; + let end = text_size(index, range.end)?; if end >= start { Some(TextRange::new(start, end)) } else { @@ -31,7 +33,7 @@ pub(crate) fn text_range(index: &LineIndex, range: lsp_types::Range) -> Option Option { +fn text_size(index: &LineIndex, position: lsp_types::Position) -> Option { let line_range = index.line(position.line)?; let col = TextSize::from(position.character); @@ -49,6 +51,16 @@ pub(crate) fn offset(index: &LineIndex, position: lsp_types::Position) -> Option Some(line_range.start() + clamped_len) } +pub(crate) fn offset( + db: &dyn Db, + file: File, + position: lsp_types::Position, +) -> Option> { + let line_index = line_index(db, file); + let offset = text_size(&line_index, position)?; + Some(InFile::new(file, offset)) +} + pub(crate) fn code_action( line_index: &LineIndex, uri: Url, diff --git a/crates/squawk_wasm/src/lib.rs b/crates/squawk_wasm/src/lib.rs index ed0fd402..5d9d5d25 100644 --- a/crates/squawk_wasm/src/lib.rs +++ b/crates/squawk_wasm/src/lib.rs @@ -5,6 +5,7 @@ use salsa::Setter; use serde::{Deserialize, Serialize}; use squawk_ide::builtins::builtins_file; use squawk_ide::db::{self, Database, File}; +use squawk_ide::file::InFile; use squawk_ide::folding_ranges::{FoldKind, folding_ranges}; use squawk_ide::semantic_tokens::{SemanticTokenType, semantic_tokens}; use squawk_syntax::ast::AstNode; @@ -272,9 +273,8 @@ impl SquawkDatabase { pub fn goto_definition(&self, line: u32, col: u32) -> Result { 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 result = squawk_ide::goto_definition::goto_definition(&self.db, file, offset); + let position = position_to_offset(&self.db, file, line, col)?; + let result = squawk_ide::goto_definition::goto_definition(&self.db, position); let response: Vec = result .into_iter() @@ -306,9 +306,8 @@ impl SquawkDatabase { pub fn hover(&self, line: u32, col: u32) -> Result { let file = self.file()?; - let line_index = db::line_index(&self.db, file); - let offset = position_to_offset(&line_index, line, col)?; - let result = squawk_ide::hover::hover(&self.db, file, offset); + let position = position_to_offset(&self.db, file, line, col)?; + let result = squawk_ide::hover::hover(&self.db, position); let converted = result.map(|hover| WasmHover { snippet: hover.snippet, @@ -320,9 +319,8 @@ impl SquawkDatabase { pub fn find_references(&self, line: u32, col: u32) -> Result { let file = self.file()?; - 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 position = position_to_offset(&self.db, file, line, col)?; + let references = squawk_ide::find_references::find_references(&self.db, position); let locations: Vec = references .iter() .map(|loc| { @@ -366,8 +364,8 @@ impl SquawkDatabase { pub fn code_actions(&self, line: u32, col: u32) -> Result { let file = self.file()?; let line_index = db::line_index(&self.db, file); - let offset = position_to_offset(&line_index, line, col)?; - let actions = squawk_ide::code_actions::code_actions(&self.db, file, offset); + let position = position_to_offset(&self.db, file, line, col)?; + let actions = squawk_ide::code_actions::code_actions(&self.db, position); let converted = actions.map(|actions| { actions @@ -486,7 +484,7 @@ impl SquawkDatabase { for pos in positions { let pos: Position = serde_wasm_bindgen::from_value(pos).map_err(into_error)?; - let offset = position_to_offset(&line_index, pos.line, pos.column)?; + let offset = position_to_offset(&self.db, file, pos.line, pos.column)?.value; let mut ranges = vec![]; let mut range = TextRange::new(offset, offset); @@ -573,9 +571,8 @@ impl SquawkDatabase { pub fn completion(&self, line: u32, col: u32) -> Result { let file = self.file()?; - let line_index = db::line_index(&self.db, file); - let offset = position_to_offset(&line_index, line, col)?; - let items = squawk_ide::completion::completion(&self.db, file, offset); + let position = position_to_offset(&self.db, file, line, col)?; + let items = squawk_ide::completion::completion(&self.db, position); let converted: Vec = items .into_iter() @@ -625,19 +622,22 @@ fn into_error(err: E) -> Error { } fn position_to_offset( - line_index: &LineIndex, + db: &Database, + file: File, line: u32, col: u32, -) -> Result { +) -> Result, Error> { + let line_index = db::line_index(db, file); let wide_pos = line_index::WideLineCol { line, col }; let pos = line_index .to_utf8(line_index::WideEncoding::Utf16, wide_pos) .ok_or_else(|| Error::new("Invalid position"))?; - line_index + let offset = line_index .offset(pos) - .ok_or_else(|| Error::new("Invalid position offset")) + .ok_or_else(|| Error::new("Invalid position offset"))?; + Ok(InFile::new(file, offset)) } fn convert_document_symbol(