From 4f71fa76073f25948c092e136066d9ad4668a363 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Wed, 31 Dec 2025 20:25:41 -0500 Subject: [PATCH 1/2] ide: goto def & hover with procedures --- crates/squawk_ide/src/binder.rs | 29 ++ crates/squawk_ide/src/goto_definition.rs | 349 +++++++++++++++++++- crates/squawk_ide/src/hover.rs | 384 ++++++++++++++++++++++- crates/squawk_ide/src/resolve.rs | 80 +++++ crates/squawk_ide/src/symbols.rs | 1 + 5 files changed, 841 insertions(+), 2 deletions(-) diff --git a/crates/squawk_ide/src/binder.rs b/crates/squawk_ide/src/binder.rs index a7694a6a..8bda33d0 100644 --- a/crates/squawk_ide/src/binder.rs +++ b/crates/squawk_ide/src/binder.rs @@ -81,6 +81,7 @@ fn bind_stmt(b: &mut Binder, stmt: ast::Stmt) { ast::Stmt::CreateIndex(create_index) => bind_create_index(b, create_index), ast::Stmt::CreateFunction(create_function) => bind_create_function(b, create_function), ast::Stmt::CreateAggregate(create_aggregate) => bind_create_aggregate(b, create_aggregate), + ast::Stmt::CreateProcedure(create_procedure) => bind_create_procedure(b, create_procedure), ast::Stmt::CreateSchema(create_schema) => bind_create_schema(b, create_schema), ast::Stmt::Set(set) => bind_set(b, set), _ => {} @@ -190,6 +191,34 @@ fn bind_create_aggregate(b: &mut Binder, create_aggregate: ast::CreateAggregate) b.scopes[root].insert(aggregate_name, aggregate_id); } +fn bind_create_procedure(b: &mut Binder, create_procedure: ast::CreateProcedure) { + let Some(path) = create_procedure.path() else { + return; + }; + + let Some(procedure_name) = item_name(&path) else { + return; + }; + + let name_ptr = path_to_ptr(&path); + + let Some(schema) = schema_name(b, &path, false) else { + return; + }; + + let params = extract_param_signature(create_procedure.param_list()); + + let procedure_id = b.symbols.alloc(Symbol { + kind: SymbolKind::Procedure, + ptr: name_ptr, + schema, + params, + }); + + let root = b.root_scope(); + b.scopes[root].insert(procedure_name, procedure_id); +} + fn bind_create_schema(b: &mut Binder, create_schema: ast::CreateSchema) { let Some(schema_name_node) = create_schema.name() else { return; diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index cd5b99e8..f7100b95 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -2453,13 +2453,360 @@ drop aggregate sum$0(bigint); "); } + #[test] + fn goto_drop_routine_function() { + assert_snapshot!(goto(" +create function foo() returns int as $$ select 1 $$ language sql; +drop routine foo$0(); +"), @r" + ╭▸ + 2 │ create function foo() returns int as $$ select 1 $$ language sql; + │ ─── 2. destination + 3 │ drop routine foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_aggregate() { + assert_snapshot!(goto(" +create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8); +drop routine myavg$0(int); +"), @r" + ╭▸ + 2 │ create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8); + │ ───── 2. destination + 3 │ drop routine myavg(int); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_with_schema() { + assert_snapshot!(goto(" +set search_path to public; +create function foo() returns int as $$ select 1 $$ language sql; +drop routine public.foo$0(); +"), @r" + ╭▸ + 3 │ create function foo() returns int as $$ select 1 $$ language sql; + │ ─── 2. destination + 4 │ drop routine public.foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_defined_after() { + assert_snapshot!(goto(" +drop routine foo$0(); +create function foo() returns int as $$ select 1 $$ language sql; +"), @r" + ╭▸ + 2 │ drop routine foo(); + │ ─ 1. source + 3 │ create function foo() returns int as $$ select 1 $$ language sql; + ╰╴ ─── 2. destination + "); + } + + #[test] + fn goto_drop_routine_with_search_path() { + assert_snapshot!(goto(" +create function foo() returns int as $$ select 1 $$ language sql; +set search_path to bar; +create function foo() returns int as $$ select 1 $$ language sql; +set search_path to default; +drop routine foo$0(); +"), @r" + ╭▸ + 2 │ create function foo() returns int as $$ select 1 $$ language sql; + │ ─── 2. destination + ‡ + 6 │ drop routine foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_overloaded() { + assert_snapshot!(goto(" +create function add(complex) returns complex as $$ select null $$ language sql; +create function add(bigint) returns bigint as $$ select 1 $$ language sql; +drop routine add$0(complex); +"), @r" + ╭▸ + 2 │ create function add(complex) returns complex as $$ select null $$ language sql; + │ ─── 2. destination + 3 │ create function add(bigint) returns bigint as $$ select 1 $$ language sql; + 4 │ drop routine add(complex); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_second_overload() { + assert_snapshot!(goto(" +create function add(complex) returns complex as $$ select null $$ language sql; +create function add(bigint) returns bigint as $$ select 1 $$ language sql; +drop routine add$0(bigint); +"), @r" + ╭▸ + 3 │ create function add(bigint) returns bigint as $$ select 1 $$ language sql; + │ ─── 2. destination + 4 │ drop routine add(bigint); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_aggregate_overloaded() { + assert_snapshot!(goto(" +create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)'); +create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0'); +drop routine sum$0(complex); +"), @r" + ╭▸ + 2 │ create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)'); + │ ─── 2. destination + 3 │ create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0'); + 4 │ drop routine sum(complex); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_multiple() { + assert_snapshot!(goto(" +create function foo() returns int as $$ select 1 $$ language sql; +create function bar() returns int as $$ select 1 $$ language sql; +drop routine foo(), bar$0(); +"), @r" + ╭▸ + 3 │ create function bar() returns int as $$ select 1 $$ language sql; + │ ─── 2. destination + 4 │ drop routine foo(), bar(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure() { + assert_snapshot!(goto(" +create procedure foo() language sql as $$ select 1 $$; +drop procedure foo$0(); +"), @r" + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 3 │ drop procedure foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure_with_schema() { + assert_snapshot!(goto(" +set search_path to public; +create procedure foo() language sql as $$ select 1 $$; +drop procedure public.foo$0(); +"), @r" + ╭▸ + 3 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 4 │ drop procedure public.foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure_defined_after() { + assert_snapshot!(goto(" +drop procedure foo$0(); +create procedure foo() language sql as $$ select 1 $$; +"), @r" + ╭▸ + 2 │ drop procedure foo(); + │ ─ 1. source + 3 │ create procedure foo() language sql as $$ select 1 $$; + ╰╴ ─── 2. destination + "); + } + + #[test] + fn goto_drop_procedure_with_search_path() { + assert_snapshot!(goto(" +create procedure foo() language sql as $$ select 1 $$; +set search_path to bar; +create procedure foo() language sql as $$ select 1 $$; +set search_path to default; +drop procedure foo$0(); +"), @r" + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + ‡ + 6 │ drop procedure foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure_overloaded() { + assert_snapshot!(goto(" +create procedure add(complex) language sql as $$ select null $$; +create procedure add(bigint) language sql as $$ select 1 $$; +drop procedure add$0(complex); +"), @r" + ╭▸ + 2 │ create procedure add(complex) language sql as $$ select null $$; + │ ─── 2. destination + 3 │ create procedure add(bigint) language sql as $$ select 1 $$; + 4 │ drop procedure add(complex); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure_second_overload() { + assert_snapshot!(goto(" +create procedure add(complex) language sql as $$ select null $$; +create procedure add(bigint) language sql as $$ select 1 $$; +drop procedure add$0(bigint); +"), @r" + ╭▸ + 3 │ create procedure add(bigint) language sql as $$ select 1 $$; + │ ─── 2. destination + 4 │ drop procedure add(bigint); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_procedure_multiple() { + assert_snapshot!(goto(" +create procedure foo() language sql as $$ select 1 $$; +create procedure bar() language sql as $$ select 1 $$; +drop procedure foo(), bar$0(); +"), @r" + ╭▸ + 3 │ create procedure bar() language sql as $$ select 1 $$; + │ ─── 2. destination + 4 │ drop procedure foo(), bar(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_procedure_definition_returns_self() { + assert_snapshot!(goto(" +create procedure foo$0() language sql as $$ select 1 $$; +"), @r" + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + │ ┬─┬ + │ │ │ + │ │ 1. source + ╰╴ 2. destination + "); + } + + #[test] + fn goto_call_procedure() { + assert_snapshot!(goto(" +create procedure foo() language sql as $$ select 1 $$; +call foo$0(); +"), @r" + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 3 │ call foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_call_procedure_with_schema() { + assert_snapshot!(goto(" +create procedure public.foo() language sql as $$ select 1 $$; +call public.foo$0(); +"), @r" + ╭▸ + 2 │ create procedure public.foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 3 │ call public.foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_call_procedure_with_search_path() { + assert_snapshot!(goto(" +set search_path to myschema; +create procedure foo() language sql as $$ select 1 $$; +call myschema.foo$0(); +"), @r" + ╭▸ + 3 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 4 │ call myschema.foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_procedure() { + assert_snapshot!(goto(" +create procedure foo() language sql as $$ select 1 $$; +drop routine foo$0(); +"), @r" + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + │ ─── 2. destination + 3 │ drop routine foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_prefers_function_over_procedure() { + assert_snapshot!(goto(" +create function foo() returns int as $$ select 1 $$ language sql; +create procedure foo() language sql as $$ select 1 $$; +drop routine foo$0(); +"), @r" + ╭▸ + 2 │ create function foo() returns int as $$ select 1 $$ language sql; + │ ─── 2. destination + 3 │ create procedure foo() language sql as $$ select 1 $$; + 4 │ drop routine foo(); + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_drop_routine_prefers_aggregate_over_procedure() { + assert_snapshot!(goto(" +create aggregate foo(int) (sfunc = int4_avg_accum, stype = _int8); +create procedure foo(int) language sql as $$ select 1 $$; +drop routine foo$0(int); +"), @r" + ╭▸ + 2 │ create aggregate foo(int) (sfunc = int4_avg_accum, stype = _int8); + │ ─── 2. destination + 3 │ create procedure foo(int) language sql as $$ select 1 $$; + 4 │ drop routine foo(int); + ╰╴ ─ 1. source + "); + } + #[test] fn goto_table_alias_in_qualified_column() { assert_snapshot!(goto(" create table t(a int8, b text); select f$0.a from t as f; "), @r" - ╭▸ + ╭▸ 3 │ select f.a from t as f; ╰╴ ─ 1. source ─ 2. destination "); diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index d0f25a9f..a1bb528f 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -48,6 +48,18 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option { return hover_aggregate(file, &name_ref, &binder); } + if is_procedure_ref(&name_ref) { + return hover_procedure(file, &name_ref, &binder); + } + + if is_routine_ref(&name_ref) { + return hover_routine(file, &name_ref, &binder); + } + + if is_call_procedure(&name_ref) { + return hover_procedure(file, &name_ref, &binder); + } + if is_select_function_call(&name_ref) { // Try function first, but fall back to column if no function found // (handles function-call-style column access like `select a(t)`) @@ -97,6 +109,14 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option { return format_create_aggregate(&create_aggregate, &binder); } + if let Some(create_procedure) = name + .syntax() + .ancestors() + .find_map(ast::CreateProcedure::cast) + { + return format_create_procedure(&create_procedure, &binder); + } + if let Some(create_schema) = name.syntax().ancestors().find_map(ast::CreateSchema::cast) { return format_create_schema(&create_schema); } @@ -374,6 +394,33 @@ fn is_aggregate_ref(name_ref: &ast::NameRef) -> bool { false } +fn is_procedure_ref(name_ref: &ast::NameRef) -> bool { + for ancestor in name_ref.syntax().ancestors() { + if ast::DropProcedure::can_cast(ancestor.kind()) { + return true; + } + } + false +} + +fn is_routine_ref(name_ref: &ast::NameRef) -> bool { + for ancestor in name_ref.syntax().ancestors() { + if ast::DropRoutine::can_cast(ancestor.kind()) { + return true; + } + } + false +} + +fn is_call_procedure(name_ref: &ast::NameRef) -> bool { + for ancestor in name_ref.syntax().ancestors() { + if ast::Call::can_cast(ancestor.kind()) { + return true; + } + } + false +} + fn is_select_function_call(name_ref: &ast::NameRef) -> bool { let mut in_call_expr = false; let mut in_arg_list = false; @@ -566,6 +613,87 @@ fn aggregate_schema( search_path.first().map(|s| s.to_string()) } +fn hover_procedure( + file: &ast::SourceFile, + name_ref: &ast::NameRef, + binder: &binder::Binder, +) -> Option { + let procedure_ptr = resolve::resolve_name_ref(binder, name_ref)?; + + let root = file.syntax(); + let procedure_name_node = procedure_ptr.to_node(root); + + let create_procedure = procedure_name_node + .ancestors() + .find_map(ast::CreateProcedure::cast)?; + + format_create_procedure(&create_procedure, binder) +} + +fn format_create_procedure( + create_procedure: &ast::CreateProcedure, + binder: &binder::Binder, +) -> Option { + let path = create_procedure.path()?; + let segment = path.segment()?; + let name = segment.name()?; + let procedure_name = name.syntax().text().to_string(); + + let schema = if let Some(qualifier) = path.qualifier() { + qualifier.syntax().text().to_string() + } else { + procedure_schema(create_procedure, binder)? + }; + + let param_list = create_procedure.param_list()?; + let params = param_list.syntax().text().to_string(); + + Some(format!("procedure {}.{}{}", schema, procedure_name, params)) +} + +fn procedure_schema( + create_procedure: &ast::CreateProcedure, + binder: &binder::Binder, +) -> Option { + let position = create_procedure.syntax().text_range().start(); + let search_path = binder.search_path_at(position); + search_path.first().map(|s| s.to_string()) +} + +fn hover_routine( + file: &ast::SourceFile, + name_ref: &ast::NameRef, + binder: &binder::Binder, +) -> Option { + let routine_ptr = resolve::resolve_name_ref(binder, name_ref)?; + + let root = file.syntax(); + let routine_name_node = routine_ptr.to_node(root); + + if let Some(create_function) = routine_name_node + .ancestors() + .find_map(ast::CreateFunction::cast) + { + return format_create_function(&create_function, binder); + } + + if let Some(create_aggregate) = routine_name_node + .ancestors() + .find_map(ast::CreateAggregate::cast) + { + return format_create_aggregate(&create_aggregate, binder); + } + + if let Some(create_procedure) = routine_name_node + .ancestors() + .find_map(ast::CreateProcedure::cast) + { + return format_create_procedure(&create_procedure, binder); + } + + None +} + #[cfg(test)] mod test { use crate::hover::hover; @@ -1850,9 +1978,263 @@ with t as ( select COLUMN1$0, COLUMN2 from t; "), @r" hover: column t.column1 - ╭▸ + ╭▸ 5 │ select COLUMN1, COLUMN2 from t; ╰╴ ─ hover "); } + + #[test] + fn hover_on_drop_procedure() { + assert_snapshot!(check_hover(" +create procedure foo() language sql as $$ select 1 $$; +drop procedure foo$0(); +"), @r" + hover: procedure public.foo() + ╭▸ + 3 │ drop procedure foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_procedure_with_schema() { + assert_snapshot!(check_hover(" +create procedure myschema.foo() language sql as $$ select 1 $$; +drop procedure myschema.foo$0(); +"), @r" + hover: procedure myschema.foo() + ╭▸ + 3 │ drop procedure myschema.foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_create_procedure_definition() { + assert_snapshot!(check_hover(" +create procedure foo$0() language sql as $$ select 1 $$; +"), @r" + hover: procedure public.foo() + ╭▸ + 2 │ create procedure foo() language sql as $$ select 1 $$; + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_create_procedure_with_explicit_schema() { + assert_snapshot!(check_hover(" +create procedure myschema.foo$0() language sql as $$ select 1 $$; +"), @r" + hover: procedure myschema.foo() + ╭▸ + 2 │ create procedure myschema.foo() language sql as $$ select 1 $$; + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_procedure_with_search_path() { + assert_snapshot!(check_hover(r#" +set search_path to myschema; +create procedure foo() language sql as $$ select 1 $$; +drop procedure foo$0(); +"#), @r" + hover: procedure myschema.foo() + ╭▸ + 4 │ drop procedure foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_procedure_overloaded() { + assert_snapshot!(check_hover(" +create procedure add(complex) language sql as $$ select null $$; +create procedure add(bigint) language sql as $$ select 1 $$; +drop procedure add$0(complex); +"), @r" + hover: procedure public.add(complex) + ╭▸ + 4 │ drop procedure add(complex); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_procedure_second_overload() { + assert_snapshot!(check_hover(" +create procedure add(complex) language sql as $$ select null $$; +create procedure add(bigint) language sql as $$ select 1 $$; +drop procedure add$0(bigint); +"), @r" + hover: procedure public.add(bigint) + ╭▸ + 4 │ drop procedure add(bigint); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_call_procedure() { + assert_snapshot!(check_hover(" +create procedure foo() language sql as $$ select 1 $$; +call foo$0(); +"), @r" + hover: procedure public.foo() + ╭▸ + 3 │ call foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_call_procedure_with_schema() { + assert_snapshot!(check_hover(" +create procedure public.foo() language sql as $$ select 1 $$; +call public.foo$0(); +"), @r" + hover: procedure public.foo() + ╭▸ + 3 │ call public.foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_call_procedure_with_search_path() { + assert_snapshot!(check_hover(r#" +set search_path to myschema; +create procedure foo() language sql as $$ select 1 $$; +call foo$0(); +"#), @r" + hover: procedure myschema.foo() + ╭▸ + 4 │ call foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_call_procedure_with_params() { + assert_snapshot!(check_hover(" +create procedure add(a int, b int) language sql as $$ select a + b $$; +call add$0(1, 2); +"), @r" + hover: procedure public.add(a int, b int) + ╭▸ + 3 │ call add(1, 2); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_function() { + assert_snapshot!(check_hover(" +create function foo() returns int as $$ select 1 $$ language sql; +drop routine foo$0(); +"), @r" + hover: function public.foo() returns int + ╭▸ + 3 │ drop routine foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_aggregate() { + assert_snapshot!(check_hover(" +create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8); +drop routine myavg$0(int); +"), @r" + hover: aggregate public.myavg(int) + ╭▸ + 3 │ drop routine myavg(int); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_procedure() { + assert_snapshot!(check_hover(" +create procedure foo() language sql as $$ select 1 $$; +drop routine foo$0(); +"), @r" + hover: procedure public.foo() + ╭▸ + 3 │ drop routine foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_with_schema() { + assert_snapshot!(check_hover(" +set search_path to public; +create function foo() returns int as $$ select 1 $$ language sql; +drop routine public.foo$0(); +"), @r" + hover: function public.foo() returns int + ╭▸ + 4 │ drop routine public.foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_with_search_path() { + assert_snapshot!(check_hover(r#" +set search_path to myschema; +create function foo() returns int as $$ select 1 $$ language sql; +drop routine foo$0(); +"#), @r" + hover: function myschema.foo() returns int + ╭▸ + 4 │ drop routine foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_overloaded() { + assert_snapshot!(check_hover(" +create function add(complex) returns complex as $$ select null $$ language sql; +create function add(bigint) returns bigint as $$ select 1 $$ language sql; +drop routine add$0(complex); +"), @r" + hover: function public.add(complex) returns complex + ╭▸ + 4 │ drop routine add(complex); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_prefers_function_over_procedure() { + assert_snapshot!(check_hover(" +create function foo() returns int as $$ select 1 $$ language sql; +create procedure foo() language sql as $$ select 1 $$; +drop routine foo$0(); +"), @r" + hover: function public.foo() returns int + ╭▸ + 4 │ drop routine foo(); + ╰╴ ─ hover + "); + } + + #[test] + fn hover_on_drop_routine_prefers_aggregate_over_procedure() { + assert_snapshot!(check_hover(" +create aggregate foo(int) (sfunc = int4_avg_accum, stype = _int8); +create procedure foo(int) language sql as $$ select 1 $$; +drop routine foo$0(int); +"), @r" + hover: aggregate public.foo(int) + ╭▸ + 4 │ drop routine foo(int); + ╰╴ ─ hover + "); + } } diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index cba47088..654aba3a 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -16,6 +16,9 @@ enum NameRefContext { DropIndex, DropFunction, DropAggregate, + DropProcedure, + DropRoutine, + CallProcedure, DropSchema, CreateIndex, CreateIndexColumn, @@ -104,6 +107,57 @@ pub(crate) fn resolve_name_ref(binder: &Binder, name_ref: &ast::NameRef) -> Opti position, ) } + NameRefContext::DropProcedure => { + let function_sig = name_ref + .syntax() + .ancestors() + .find_map(ast::FunctionSig::cast)?; + let path = function_sig.path()?; + let procedure_name = extract_table_name(&path)?; + let schema = extract_schema_name(&path); + let params = extract_param_signature(&function_sig); + let position = name_ref.syntax().text_range().start(); + resolve_procedure( + binder, + &procedure_name, + &schema, + params.as_deref(), + position, + ) + } + NameRefContext::DropRoutine => { + let function_sig = name_ref + .syntax() + .ancestors() + .find_map(ast::FunctionSig::cast)?; + let path = function_sig.path()?; + let routine_name = extract_table_name(&path)?; + let schema = extract_schema_name(&path); + let params = extract_param_signature(&function_sig); + let position = name_ref.syntax().text_range().start(); + + if let Some(ptr) = + resolve_function(binder, &routine_name, &schema, params.as_deref(), position) + { + return Some(ptr); + } + + if let Some(ptr) = + resolve_aggregate(binder, &routine_name, &schema, params.as_deref(), position) + { + return Some(ptr); + } + + resolve_procedure(binder, &routine_name, &schema, params.as_deref(), position) + } + NameRefContext::CallProcedure => { + let call = name_ref.syntax().ancestors().find_map(ast::Call::cast)?; + let path = call.path()?; + let procedure_name = extract_table_name(&path)?; + let schema = extract_schema_name(&path); + let position = name_ref.syntax().text_range().start(); + resolve_procedure(binder, &procedure_name, &schema, None, position) + } NameRefContext::DropSchema | NameRefContext::SchemaQualifier => { let schema_name = Name::from_node(name_ref); resolve_schema(binder, &schema_name) @@ -261,6 +315,15 @@ fn classify_name_ref_context(name_ref: &ast::NameRef) -> Option if ast::DropAggregate::can_cast(ancestor.kind()) { return Some(NameRefContext::DropAggregate); } + if ast::DropProcedure::can_cast(ancestor.kind()) { + return Some(NameRefContext::DropProcedure); + } + if ast::DropRoutine::can_cast(ancestor.kind()) { + return Some(NameRefContext::DropRoutine); + } + if ast::Call::can_cast(ancestor.kind()) { + return Some(NameRefContext::CallProcedure); + } if ast::DropSchema::can_cast(ancestor.kind()) { return Some(NameRefContext::DropSchema); } @@ -439,6 +502,23 @@ fn resolve_aggregate( ) } +fn resolve_procedure( + binder: &Binder, + procedure_name: &Name, + schema: &Option, + params: Option<&[Name]>, + position: TextSize, +) -> Option { + resolve_for_kind_with_params( + binder, + procedure_name, + schema, + params, + position, + SymbolKind::Procedure, + ) +} + fn resolve_schema(binder: &Binder, schema_name: &Name) -> Option { let symbols = binder.scopes[binder.root_scope()].get(schema_name)?; let symbol_id = symbols.iter().copied().find(|id| { diff --git a/crates/squawk_ide/src/symbols.rs b/crates/squawk_ide/src/symbols.rs index 20db7787..cef8e1eb 100644 --- a/crates/squawk_ide/src/symbols.rs +++ b/crates/squawk_ide/src/symbols.rs @@ -48,6 +48,7 @@ pub(crate) enum SymbolKind { Index, Function, Aggregate, + Procedure, Schema, } From 608b2e7d047b5580e3c36dd09d704ac5c62b6c5b Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Wed, 31 Dec 2025 21:11:22 -0500 Subject: [PATCH 2/2] fix --- crates/squawk_ide/src/goto_definition.rs | 2 +- crates/squawk_ide/src/hover.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index f7100b95..85c6172c 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -2806,7 +2806,7 @@ drop routine foo$0(int); create table t(a int8, b text); select f$0.a from t as f; "), @r" - ╭▸ + ╭▸ 3 │ select f.a from t as f; ╰╴ ─ 1. source ─ 2. destination "); diff --git a/crates/squawk_ide/src/hover.rs b/crates/squawk_ide/src/hover.rs index a1bb528f..cea61cdb 100644 --- a/crates/squawk_ide/src/hover.rs +++ b/crates/squawk_ide/src/hover.rs @@ -1978,7 +1978,7 @@ with t as ( select COLUMN1$0, COLUMN2 from t; "), @r" hover: column t.column1 - ╭▸ + ╭▸ 5 │ select COLUMN1, COLUMN2 from t; ╰╴ ─ hover ");