squawk_ide/
hover.rs

1use crate::offsets::token_from_offset;
2use crate::resolve;
3use crate::{binder, symbols::Name};
4use rowan::TextSize;
5use squawk_syntax::ast::{self, AstNode};
6
7pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
8    let token = token_from_offset(file, offset)?;
9    let parent = token.parent()?;
10
11    let binder = binder::bind(file);
12
13    // TODO: can we use the classify_name_ref_context function from goto def here?
14    if let Some(name_ref) = ast::NameRef::cast(parent.clone()) {
15        if is_column_ref(&name_ref) {
16            return hover_column(file, &name_ref, &binder);
17        }
18
19        if is_type_ref(&name_ref) {
20            return hover_type(file, &name_ref, &binder);
21        }
22
23        if is_select_column(&name_ref) {
24            // Try hover as column first
25            if let Some(result) = hover_column(file, &name_ref, &binder) {
26                return Some(result);
27            }
28            // If no column, try as function (handles field-style function calls like `t.b`)
29            if let Some(result) = hover_function(file, &name_ref, &binder) {
30                return Some(result);
31            }
32            // Finally try as table (handles case like `select t from t;` where t is the table)
33            return hover_table(file, &name_ref, &binder);
34        }
35
36        if is_table_ref(&name_ref) {
37            return hover_table(file, &name_ref, &binder);
38        }
39
40        if is_select_from_table(&name_ref) {
41            return hover_table(file, &name_ref, &binder);
42        }
43
44        if is_update_from_table(&name_ref) {
45            return hover_table(file, &name_ref, &binder);
46        }
47
48        if is_index_ref(&name_ref) {
49            return hover_index(file, &name_ref, &binder);
50        }
51
52        if is_function_ref(&name_ref) {
53            return hover_function(file, &name_ref, &binder);
54        }
55
56        if is_aggregate_ref(&name_ref) {
57            return hover_aggregate(file, &name_ref, &binder);
58        }
59
60        if is_procedure_ref(&name_ref) {
61            return hover_procedure(file, &name_ref, &binder);
62        }
63
64        if is_routine_ref(&name_ref) {
65            return hover_routine(file, &name_ref, &binder);
66        }
67
68        if is_call_procedure(&name_ref) {
69            return hover_procedure(file, &name_ref, &binder);
70        }
71
72        if is_select_function_call(&name_ref) {
73            // Try function first, but fall back to column if no function found
74            // (handles function-call-style column access like `select a(t)`)
75            if let Some(result) = hover_function(file, &name_ref, &binder) {
76                return Some(result);
77            }
78            return hover_column(file, &name_ref, &binder);
79        }
80
81        if is_schema_ref(&name_ref) {
82            return hover_schema(file, &name_ref, &binder);
83        }
84    }
85
86    if let Some(name) = ast::Name::cast(parent) {
87        if let Some(column) = name.syntax().parent().and_then(ast::Column::cast)
88            && let Some(create_table) = column.syntax().ancestors().find_map(ast::CreateTable::cast)
89        {
90            return hover_column_definition(&create_table, &column, &binder);
91        }
92
93        if let Some(create_table) = name.syntax().ancestors().find_map(ast::CreateTable::cast) {
94            return format_create_table(&create_table, &binder);
95        }
96
97        if let Some(with_table) = name.syntax().parent().and_then(ast::WithTable::cast) {
98            return format_with_table(&with_table);
99        }
100
101        if let Some(create_index) = name.syntax().ancestors().find_map(ast::CreateIndex::cast) {
102            return format_create_index(&create_index, &binder);
103        }
104
105        if let Some(create_type) = name.syntax().ancestors().find_map(ast::CreateType::cast) {
106            return format_create_type(&create_type, &binder);
107        }
108
109        if let Some(create_function) = name
110            .syntax()
111            .ancestors()
112            .find_map(ast::CreateFunction::cast)
113        {
114            return format_create_function(&create_function, &binder);
115        }
116
117        if let Some(create_aggregate) = name
118            .syntax()
119            .ancestors()
120            .find_map(ast::CreateAggregate::cast)
121        {
122            return format_create_aggregate(&create_aggregate, &binder);
123        }
124
125        if let Some(create_procedure) = name
126            .syntax()
127            .ancestors()
128            .find_map(ast::CreateProcedure::cast)
129        {
130            return format_create_procedure(&create_procedure, &binder);
131        }
132
133        if let Some(create_schema) = name.syntax().ancestors().find_map(ast::CreateSchema::cast) {
134            return format_create_schema(&create_schema);
135        }
136    }
137
138    None
139}
140
141fn format_column(
142    schema: &str,
143    table_name: &str,
144    column_name: &str,
145    ty: &impl std::fmt::Display,
146) -> String {
147    format!("column {schema}.{table_name}.{column_name} {ty}")
148}
149
150fn hover_column(
151    file: &ast::SourceFile,
152    name_ref: &ast::NameRef,
153    binder: &binder::Binder,
154) -> Option<String> {
155    let column_ptr = resolve::resolve_name_ref(binder, name_ref)?;
156
157    let root = file.syntax();
158    let column_name_node = column_ptr.to_node(root);
159
160    if let Some(with_table) = column_name_node.ancestors().find_map(ast::WithTable::cast) {
161        let cte_name = with_table.name()?.syntax().text().to_string();
162        let column_name = if column_name_node
163            .ancestors()
164            .any(|a| ast::Values::can_cast(a.kind()))
165        {
166            Name::from_node(name_ref)
167        } else {
168            Name::from_string(column_name_node.text().to_string())
169        };
170        return Some(format!("column {}.{}", cte_name, column_name));
171    }
172
173    let column = column_name_node.ancestors().find_map(ast::Column::cast)?;
174    let column_name = column.name()?.syntax().text().to_string();
175    let ty = column.ty()?;
176
177    let create_table = column
178        .syntax()
179        .ancestors()
180        .find_map(ast::CreateTable::cast)?;
181    let path = create_table.path()?;
182    let table_name = path.segment()?.name()?.syntax().text().to_string();
183
184    let schema = if let Some(qualifier) = path.qualifier() {
185        qualifier.syntax().text().to_string()
186    } else {
187        table_schema(&create_table, binder)?
188    };
189
190    Some(format_column(
191        &schema,
192        &table_name,
193        &column_name,
194        &ty.syntax().text(),
195    ))
196}
197
198fn hover_column_definition(
199    create_table: &ast::CreateTable,
200    column: &ast::Column,
201    binder: &binder::Binder,
202) -> Option<String> {
203    let column_name = column.name()?.syntax().text().to_string();
204    let ty = column.ty()?;
205    let path = create_table.path()?;
206    let table_name = path.segment()?.name()?.syntax().text().to_string();
207
208    let schema = if let Some(qualifier) = path.qualifier() {
209        qualifier.syntax().text().to_string()
210    } else {
211        table_schema(create_table, binder)?
212    };
213
214    Some(format_column(
215        &schema,
216        &table_name,
217        &column_name,
218        &ty.syntax().text(),
219    ))
220}
221
222fn hover_table(
223    file: &ast::SourceFile,
224    name_ref: &ast::NameRef,
225    binder: &binder::Binder,
226) -> Option<String> {
227    let table_ptr = resolve::resolve_name_ref(binder, name_ref)?;
228
229    let root = file.syntax();
230    let table_name_node = table_ptr.to_node(root);
231
232    if let Some(with_table) = table_name_node.ancestors().find_map(ast::WithTable::cast) {
233        return format_with_table(&with_table);
234    }
235
236    let create_table = table_name_node
237        .ancestors()
238        .find_map(ast::CreateTable::cast)?;
239
240    format_create_table(&create_table, binder)
241}
242
243fn hover_index(
244    file: &ast::SourceFile,
245    name_ref: &ast::NameRef,
246    binder: &binder::Binder,
247) -> Option<String> {
248    let index_ptr = resolve::resolve_name_ref(binder, name_ref)?;
249
250    let root = file.syntax();
251    let index_name_node = index_ptr.to_node(root);
252
253    let create_index = index_name_node
254        .ancestors()
255        .find_map(ast::CreateIndex::cast)?;
256
257    format_create_index(&create_index, binder)
258}
259
260fn hover_type(
261    file: &ast::SourceFile,
262    name_ref: &ast::NameRef,
263    binder: &binder::Binder,
264) -> Option<String> {
265    let type_ptr = resolve::resolve_name_ref(binder, name_ref)?;
266
267    let root = file.syntax();
268    let type_name_node = type_ptr.to_node(root);
269
270    let create_type = type_name_node.ancestors().find_map(ast::CreateType::cast)?;
271
272    format_create_type(&create_type, binder)
273}
274
275// Insert inferred schema into the create table hover info
276fn format_create_table(create_table: &ast::CreateTable, binder: &binder::Binder) -> Option<String> {
277    let path = create_table.path()?;
278    let segment = path.segment()?;
279    let table_name = segment.name()?.syntax().text().to_string();
280
281    let schema = if let Some(qualifier) = path.qualifier() {
282        qualifier.syntax().text().to_string()
283    } else {
284        table_schema(create_table, binder)?
285    };
286
287    let args = create_table.table_arg_list()?.syntax().text().to_string();
288
289    Some(format!("table {}.{}{}", schema, table_name, args))
290}
291
292fn format_with_table(with_table: &ast::WithTable) -> Option<String> {
293    let name = with_table.name()?.syntax().text().to_string();
294    let query = with_table.query()?.syntax().text().to_string();
295    Some(format!("with {} as ({})", name, query))
296}
297
298fn table_schema(create_table: &ast::CreateTable, binder: &binder::Binder) -> Option<String> {
299    let is_temp = create_table.temp_token().is_some() || create_table.temporary_token().is_some();
300    if is_temp {
301        return Some("pg_temp".to_string());
302    }
303
304    let position = create_table.syntax().text_range().start();
305    let search_path = binder.search_path_at(position);
306    search_path.first().map(|s| s.to_string())
307}
308
309fn format_create_index(create_index: &ast::CreateIndex, binder: &binder::Binder) -> Option<String> {
310    let index_name = create_index.name()?.syntax().text().to_string();
311
312    let index_schema = index_schema(create_index, binder)?;
313
314    let relation_name = create_index.relation_name()?;
315    let path = relation_name.path()?;
316    let (table_schema, table_name) = resolve::resolve_table_info(binder, &path)?;
317
318    let partition_item_list = create_index.partition_item_list()?;
319    let columns = partition_item_list.syntax().text().to_string();
320
321    Some(format!(
322        "index {}.{} on {}.{}{}",
323        index_schema, index_name, table_schema, table_name, columns
324    ))
325}
326
327fn index_schema(create_index: &ast::CreateIndex, binder: &binder::Binder) -> Option<String> {
328    let position = create_index.syntax().text_range().start();
329    let search_path = binder.search_path_at(position);
330    search_path.first().map(|s| s.to_string())
331}
332
333fn format_create_type(create_type: &ast::CreateType, binder: &binder::Binder) -> Option<String> {
334    let path = create_type.path()?;
335    let segment = path.segment()?;
336    let type_name = segment.name()?.syntax().text().to_string();
337
338    let schema = if let Some(qualifier) = path.qualifier() {
339        qualifier.syntax().text().to_string()
340    } else {
341        type_schema(create_type, binder)?
342    };
343
344    if let Some(variant_list) = create_type.variant_list() {
345        let variants = variant_list.syntax().text().to_string();
346        return Some(format!(
347            "type {}.{} as enum {}",
348            schema, type_name, variants
349        ));
350    }
351
352    if let Some(column_list) = create_type.column_list() {
353        let columns = column_list.syntax().text().to_string();
354        return Some(format!("type {}.{} as {}", schema, type_name, columns));
355    }
356
357    if let Some(attribute_list) = create_type.attribute_list() {
358        let attributes = attribute_list.syntax().text().to_string();
359        return Some(format!("type {}.{} {}", schema, type_name, attributes));
360    }
361
362    Some(format!("type {}.{}", schema, type_name))
363}
364
365fn type_schema(create_type: &ast::CreateType, binder: &binder::Binder) -> Option<String> {
366    let position = create_type.syntax().text_range().start();
367    let search_path = binder.search_path_at(position);
368    search_path.first().map(|s| s.to_string())
369}
370
371fn is_column_ref(name_ref: &ast::NameRef) -> bool {
372    let mut in_partition_item = false;
373    let mut in_column_list = false;
374    let mut in_where_clause = false;
375    let mut in_set_clause = false;
376
377    for ancestor in name_ref.syntax().ancestors() {
378        if ast::PartitionItem::can_cast(ancestor.kind()) {
379            in_partition_item = true;
380        }
381        if ast::CreateIndex::can_cast(ancestor.kind()) {
382            return in_partition_item;
383        }
384        if ast::ColumnList::can_cast(ancestor.kind()) {
385            in_column_list = true;
386        }
387        if ast::Insert::can_cast(ancestor.kind()) {
388            return in_column_list;
389        }
390        if ast::WhereClause::can_cast(ancestor.kind()) {
391            in_where_clause = true;
392        }
393        if ast::SetClause::can_cast(ancestor.kind()) {
394            in_set_clause = true;
395        }
396        if ast::Delete::can_cast(ancestor.kind()) {
397            return in_where_clause;
398        }
399        if ast::Update::can_cast(ancestor.kind()) {
400            return in_where_clause || in_set_clause;
401        }
402    }
403    false
404}
405
406fn is_table_ref(name_ref: &ast::NameRef) -> bool {
407    let mut in_partition_item = false;
408    let mut in_column_list = false;
409    let mut in_where_clause = false;
410    let mut in_set_clause = false;
411    let mut in_from_clause = false;
412
413    for ancestor in name_ref.syntax().ancestors() {
414        if ast::DropTable::can_cast(ancestor.kind()) {
415            return true;
416        }
417        if ast::Table::can_cast(ancestor.kind()) {
418            return true;
419        }
420        if ast::ColumnList::can_cast(ancestor.kind()) {
421            in_column_list = true;
422        }
423        if ast::Insert::can_cast(ancestor.kind()) {
424            return !in_column_list;
425        }
426        if ast::WhereClause::can_cast(ancestor.kind()) {
427            in_where_clause = true;
428        }
429        if ast::SetClause::can_cast(ancestor.kind()) {
430            in_set_clause = true;
431        }
432        if ast::FromClause::can_cast(ancestor.kind()) {
433            in_from_clause = true;
434        }
435        if ast::Delete::can_cast(ancestor.kind()) {
436            return !in_where_clause;
437        }
438        if ast::Update::can_cast(ancestor.kind()) {
439            return !in_where_clause && !in_set_clause && !in_from_clause;
440        }
441        if ast::DropIndex::can_cast(ancestor.kind()) {
442            return false;
443        }
444        if ast::PartitionItem::can_cast(ancestor.kind()) {
445            in_partition_item = true;
446        }
447        if ast::CreateIndex::can_cast(ancestor.kind()) {
448            return !in_partition_item;
449        }
450    }
451    false
452}
453
454fn is_index_ref(name_ref: &ast::NameRef) -> bool {
455    for ancestor in name_ref.syntax().ancestors() {
456        if ast::DropIndex::can_cast(ancestor.kind()) {
457            return true;
458        }
459    }
460    false
461}
462
463fn is_type_ref(name_ref: &ast::NameRef) -> bool {
464    let mut in_type = false;
465    for ancestor in name_ref.syntax().ancestors() {
466        if ast::PathType::can_cast(ancestor.kind()) || ast::ExprType::can_cast(ancestor.kind()) {
467            in_type = true;
468        }
469        if ast::DropType::can_cast(ancestor.kind()) {
470            return true;
471        }
472        if ast::CastExpr::can_cast(ancestor.kind()) && in_type {
473            return true;
474        }
475    }
476    false
477}
478
479fn is_function_ref(name_ref: &ast::NameRef) -> bool {
480    for ancestor in name_ref.syntax().ancestors() {
481        if ast::DropFunction::can_cast(ancestor.kind()) {
482            return true;
483        }
484    }
485    false
486}
487
488fn is_aggregate_ref(name_ref: &ast::NameRef) -> bool {
489    for ancestor in name_ref.syntax().ancestors() {
490        if ast::DropAggregate::can_cast(ancestor.kind()) {
491            return true;
492        }
493    }
494    false
495}
496
497fn is_procedure_ref(name_ref: &ast::NameRef) -> bool {
498    for ancestor in name_ref.syntax().ancestors() {
499        if ast::DropProcedure::can_cast(ancestor.kind()) {
500            return true;
501        }
502    }
503    false
504}
505
506fn is_routine_ref(name_ref: &ast::NameRef) -> bool {
507    for ancestor in name_ref.syntax().ancestors() {
508        if ast::DropRoutine::can_cast(ancestor.kind()) {
509            return true;
510        }
511    }
512    false
513}
514
515fn is_call_procedure(name_ref: &ast::NameRef) -> bool {
516    for ancestor in name_ref.syntax().ancestors() {
517        if ast::Call::can_cast(ancestor.kind()) {
518            return true;
519        }
520    }
521    false
522}
523
524fn is_select_function_call(name_ref: &ast::NameRef) -> bool {
525    let mut in_call_expr = false;
526    let mut in_arg_list = false;
527
528    for ancestor in name_ref.syntax().ancestors() {
529        if ast::ArgList::can_cast(ancestor.kind()) {
530            in_arg_list = true;
531        }
532        if ast::CallExpr::can_cast(ancestor.kind()) {
533            in_call_expr = true;
534        }
535        if ast::Select::can_cast(ancestor.kind()) && in_call_expr && !in_arg_list {
536            return true;
537        }
538    }
539    false
540}
541
542fn is_select_from_table(name_ref: &ast::NameRef) -> bool {
543    let mut in_from_clause = false;
544
545    for ancestor in name_ref.syntax().ancestors() {
546        if ast::FromClause::can_cast(ancestor.kind()) {
547            in_from_clause = true;
548        }
549        if ast::Select::can_cast(ancestor.kind()) && in_from_clause {
550            return true;
551        }
552    }
553    false
554}
555
556fn is_update_from_table(name_ref: &ast::NameRef) -> bool {
557    let mut in_from_clause = false;
558
559    for ancestor in name_ref.syntax().ancestors() {
560        if ast::FromClause::can_cast(ancestor.kind()) {
561            in_from_clause = true;
562        }
563        if ast::Update::can_cast(ancestor.kind()) && in_from_clause {
564            return true;
565        }
566    }
567    false
568}
569
570fn is_select_column(name_ref: &ast::NameRef) -> bool {
571    let mut in_call_expr = false;
572    let mut in_arg_list = false;
573    let mut in_from_clause = false;
574
575    for ancestor in name_ref.syntax().ancestors() {
576        if ast::ArgList::can_cast(ancestor.kind()) {
577            in_arg_list = true;
578        }
579        if ast::CallExpr::can_cast(ancestor.kind()) {
580            in_call_expr = true;
581        }
582        if ast::FromClause::can_cast(ancestor.kind()) {
583            in_from_clause = true;
584        }
585        if ast::Select::can_cast(ancestor.kind()) {
586            // if we're inside a call expr but not inside an arg list, this is a function call
587            if in_call_expr && !in_arg_list {
588                return false;
589            }
590            // if we're in FROM clause, this is a table reference, not a column
591            if in_from_clause {
592                return false;
593            }
594            // anything else in SELECT (target list, WHERE, ORDER BY, etc.) is a column
595            return true;
596        }
597    }
598    false
599}
600
601fn is_schema_ref(name_ref: &ast::NameRef) -> bool {
602    for ancestor in name_ref.syntax().ancestors() {
603        if ast::DropSchema::can_cast(ancestor.kind()) {
604            return true;
605        }
606    }
607    false
608}
609
610fn hover_schema(
611    file: &ast::SourceFile,
612    name_ref: &ast::NameRef,
613    binder: &binder::Binder,
614) -> Option<String> {
615    let schema_ptr = resolve::resolve_name_ref(binder, name_ref)?;
616
617    let root = file.syntax();
618    let schema_name_node = schema_ptr.to_node(root);
619
620    let create_schema = ast::CreateSchema::cast(schema_name_node.parent()?)?;
621
622    format_create_schema(&create_schema)
623}
624
625fn format_create_schema(create_schema: &ast::CreateSchema) -> Option<String> {
626    let schema_name = create_schema.name()?.syntax().text().to_string();
627    Some(format!("schema {}", schema_name))
628}
629
630fn hover_function(
631    file: &ast::SourceFile,
632    name_ref: &ast::NameRef,
633    binder: &binder::Binder,
634) -> Option<String> {
635    let function_ptr = resolve::resolve_name_ref(binder, name_ref)?;
636
637    let root = file.syntax();
638    let function_name_node = function_ptr.to_node(root);
639
640    let create_function = function_name_node
641        .ancestors()
642        .find_map(ast::CreateFunction::cast)?;
643
644    format_create_function(&create_function, binder)
645}
646
647fn format_create_function(
648    create_function: &ast::CreateFunction,
649    binder: &binder::Binder,
650) -> Option<String> {
651    let path = create_function.path()?;
652    let segment = path.segment()?;
653    let name = segment.name()?;
654    let function_name = name.syntax().text().to_string();
655
656    let schema = if let Some(qualifier) = path.qualifier() {
657        qualifier.syntax().text().to_string()
658    } else {
659        function_schema(create_function, binder)?
660    };
661
662    let param_list = create_function.param_list()?;
663    let params = param_list.syntax().text().to_string();
664
665    let ret_type = create_function.ret_type()?;
666    let return_type = ret_type.syntax().text().to_string();
667
668    Some(format!(
669        "function {}.{}{} {}",
670        schema, function_name, params, return_type
671    ))
672}
673
674fn function_schema(
675    create_function: &ast::CreateFunction,
676    binder: &binder::Binder,
677) -> Option<String> {
678    let position = create_function.syntax().text_range().start();
679    let search_path = binder.search_path_at(position);
680    search_path.first().map(|s| s.to_string())
681}
682
683fn hover_aggregate(
684    file: &ast::SourceFile,
685    name_ref: &ast::NameRef,
686    binder: &binder::Binder,
687) -> Option<String> {
688    let aggregate_ptr = resolve::resolve_name_ref(binder, name_ref)?;
689
690    let root = file.syntax();
691    let aggregate_name_node = aggregate_ptr.to_node(root);
692
693    let create_aggregate = aggregate_name_node
694        .ancestors()
695        .find_map(ast::CreateAggregate::cast)?;
696
697    format_create_aggregate(&create_aggregate, binder)
698}
699
700fn format_create_aggregate(
701    create_aggregate: &ast::CreateAggregate,
702    binder: &binder::Binder,
703) -> Option<String> {
704    let path = create_aggregate.path()?;
705    let segment = path.segment()?;
706    let name = segment.name()?;
707    let aggregate_name = name.syntax().text().to_string();
708
709    let schema = if let Some(qualifier) = path.qualifier() {
710        qualifier.syntax().text().to_string()
711    } else {
712        aggregate_schema(create_aggregate, binder)?
713    };
714
715    let param_list = create_aggregate.param_list()?;
716    let params = param_list.syntax().text().to_string();
717
718    Some(format!("aggregate {}.{}{}", schema, aggregate_name, params))
719}
720
721fn aggregate_schema(
722    create_aggregate: &ast::CreateAggregate,
723    binder: &binder::Binder,
724) -> Option<String> {
725    let position = create_aggregate.syntax().text_range().start();
726    let search_path = binder.search_path_at(position);
727    search_path.first().map(|s| s.to_string())
728}
729
730fn hover_procedure(
731    file: &ast::SourceFile,
732    name_ref: &ast::NameRef,
733    binder: &binder::Binder,
734) -> Option<String> {
735    let procedure_ptr = resolve::resolve_name_ref(binder, name_ref)?;
736
737    let root = file.syntax();
738    let procedure_name_node = procedure_ptr.to_node(root);
739
740    let create_procedure = procedure_name_node
741        .ancestors()
742        .find_map(ast::CreateProcedure::cast)?;
743
744    format_create_procedure(&create_procedure, binder)
745}
746
747fn format_create_procedure(
748    create_procedure: &ast::CreateProcedure,
749    binder: &binder::Binder,
750) -> Option<String> {
751    let path = create_procedure.path()?;
752    let segment = path.segment()?;
753    let name = segment.name()?;
754    let procedure_name = name.syntax().text().to_string();
755
756    let schema = if let Some(qualifier) = path.qualifier() {
757        qualifier.syntax().text().to_string()
758    } else {
759        procedure_schema(create_procedure, binder)?
760    };
761
762    let param_list = create_procedure.param_list()?;
763    let params = param_list.syntax().text().to_string();
764
765    Some(format!("procedure {}.{}{}", schema, procedure_name, params))
766}
767
768fn procedure_schema(
769    create_procedure: &ast::CreateProcedure,
770    binder: &binder::Binder,
771) -> Option<String> {
772    let position = create_procedure.syntax().text_range().start();
773    let search_path = binder.search_path_at(position);
774    search_path.first().map(|s| s.to_string())
775}
776
777fn hover_routine(
778    file: &ast::SourceFile,
779    name_ref: &ast::NameRef,
780    binder: &binder::Binder,
781) -> Option<String> {
782    let routine_ptr = resolve::resolve_name_ref(binder, name_ref)?;
783
784    let root = file.syntax();
785    let routine_name_node = routine_ptr.to_node(root);
786
787    if let Some(create_function) = routine_name_node
788        .ancestors()
789        .find_map(ast::CreateFunction::cast)
790    {
791        return format_create_function(&create_function, binder);
792    }
793
794    if let Some(create_aggregate) = routine_name_node
795        .ancestors()
796        .find_map(ast::CreateAggregate::cast)
797    {
798        return format_create_aggregate(&create_aggregate, binder);
799    }
800
801    if let Some(create_procedure) = routine_name_node
802        .ancestors()
803        .find_map(ast::CreateProcedure::cast)
804    {
805        return format_create_procedure(&create_procedure, binder);
806    }
807
808    None
809}
810
811#[cfg(test)]
812mod test {
813    use crate::hover::hover;
814    use crate::test_utils::fixture;
815    use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
816    use insta::assert_snapshot;
817    use squawk_syntax::ast;
818
819    #[track_caller]
820    fn check_hover(sql: &str) -> String {
821        check_hover_(sql).expect("should find hover information")
822    }
823
824    #[track_caller]
825    fn check_hover_(sql: &str) -> Option<String> {
826        let (mut offset, sql) = fixture(sql);
827        offset = offset.checked_sub(1.into()).unwrap_or_default();
828        let parse = ast::SourceFile::parse(&sql);
829        assert_eq!(parse.errors(), vec![]);
830        let file: ast::SourceFile = parse.tree();
831
832        if let Some(type_info) = hover(&file, offset) {
833            let offset_usize: usize = offset.into();
834            let title = format!("hover: {}", type_info);
835            let group = Level::INFO.primary_title(&title).element(
836                Snippet::source(&sql).fold(true).annotation(
837                    AnnotationKind::Context
838                        .span(offset_usize..offset_usize + 1)
839                        .label("hover"),
840                ),
841            );
842            let renderer = Renderer::plain().decor_style(DecorStyle::Unicode);
843            return Some(
844                renderer
845                    .render(&[group])
846                    .to_string()
847                    // neater
848                    .replace("info: hover:", "hover:"),
849            );
850        }
851        None
852    }
853
854    #[test]
855    fn hover_column_in_create_index() {
856        assert_snapshot!(check_hover("
857create table users(id int, email text);
858create index idx_email on users(email$0);
859"), @r"
860        hover: column public.users.email text
861          ╭▸ 
862        3 │ create index idx_email on users(email);
863          ╰╴                                    ─ hover
864        ");
865    }
866
867    #[test]
868    fn hover_column_int_type() {
869        assert_snapshot!(check_hover("
870create table users(id int, email text);
871create index idx_id on users(id$0);
872"), @r"
873        hover: column public.users.id int
874          ╭▸ 
875        3 │ create index idx_id on users(id);
876          ╰╴                              ─ hover
877        ");
878    }
879
880    #[test]
881    fn hover_column_with_schema() {
882        assert_snapshot!(check_hover("
883create table public.users(id int, email text);
884create index idx_email on public.users(email$0);
885"), @r"
886        hover: column public.users.email text
887          ╭▸ 
888        3 │ create index idx_email on public.users(email);
889          ╰╴                                           ─ hover
890        ");
891    }
892
893    #[test]
894    fn hover_column_temp_table() {
895        assert_snapshot!(check_hover("
896create temp table users(id int, email text);
897create index idx_email on users(email$0);
898"), @r"
899        hover: column pg_temp.users.email text
900          ╭▸ 
901        3 │ create index idx_email on users(email);
902          ╰╴                                    ─ hover
903        ");
904    }
905
906    #[test]
907    fn hover_column_multiple_columns() {
908        assert_snapshot!(check_hover("
909create table users(id int, email text, name varchar(100));
910create index idx_users on users(id, email$0, name);
911"), @r"
912        hover: column public.users.email text
913          ╭▸ 
914        3 │ create index idx_users on users(id, email, name);
915          ╰╴                                        ─ hover
916        ");
917    }
918
919    #[test]
920    fn hover_column_varchar() {
921        assert_snapshot!(check_hover("
922create table users(id int, name varchar(100));
923create index idx_name on users(name$0);
924"), @r"
925        hover: column public.users.name varchar(100)
926          ╭▸ 
927        3 │ create index idx_name on users(name);
928          ╰╴                                  ─ hover
929        ");
930    }
931
932    #[test]
933    fn hover_column_bigint() {
934        assert_snapshot!(check_hover("
935create table metrics(value bigint);
936create index idx_value on metrics(value$0);
937"), @r"
938        hover: column public.metrics.value bigint
939          ╭▸ 
940        3 │ create index idx_value on metrics(value);
941          ╰╴                                      ─ hover
942        ");
943    }
944
945    #[test]
946    fn hover_column_timestamp() {
947        assert_snapshot!(check_hover("
948create table events(created_at timestamp with time zone);
949create index idx_created on events(created_at$0);
950"), @r"
951        hover: column public.events.created_at timestamp with time zone
952          ╭▸ 
953        3 │ create index idx_created on events(created_at);
954          ╰╴                                            ─ hover
955        ");
956    }
957
958    #[test]
959    fn hover_column_with_search_path() {
960        assert_snapshot!(check_hover(r#"
961set search_path to myschema;
962create table myschema.users(id int, email text);
963create index idx_email on users(email$0);
964"#), @r"
965        hover: column myschema.users.email text
966          ╭▸ 
967        4 │ create index idx_email on users(email);
968          ╰╴                                    ─ hover
969        ");
970    }
971
972    #[test]
973    fn hover_column_explicit_schema_overrides_search_path() {
974        assert_snapshot!(check_hover(r#"
975set search_path to myschema;
976create table public.users(id int, email text);
977create table myschema.users(value bigint);
978create index idx_email on public.users(email$0);
979"#), @r"
980        hover: column public.users.email text
981          ╭▸ 
982        5 │ create index idx_email on public.users(email);
983          ╰╴                                           ─ hover
984        ");
985    }
986
987    #[test]
988    fn hover_on_table_name() {
989        assert_snapshot!(check_hover("
990create table t(id int);
991create index idx on t$0(id);
992"), @r"
993        hover: table public.t(id int)
994          ╭▸ 
995        3 │ create index idx on t(id);
996          ╰╴                    ─ hover
997        ");
998    }
999
1000    #[test]
1001    fn hover_on_index_name_in_create() {
1002        assert_snapshot!(check_hover("
1003create table users(id int);
1004create index idx$0 on users(id);
1005"), @r"
1006        hover: index public.idx on public.users(id)
1007          ╭▸ 
1008        3 │ create index idx on users(id);
1009          ╰╴               ─ hover
1010        ");
1011    }
1012
1013    #[test]
1014    fn hover_table_in_create_index() {
1015        assert_snapshot!(check_hover("
1016create table users(id int, email text);
1017create index idx_email on users$0(email);
1018"), @r"
1019        hover: table public.users(id int, email text)
1020          ╭▸ 
1021        3 │ create index idx_email on users(email);
1022          ╰╴                              ─ hover
1023        ");
1024    }
1025
1026    #[test]
1027    fn hover_table_with_schema() {
1028        assert_snapshot!(check_hover("
1029create table public.users(id int, email text);
1030create index idx on public.users$0(id);
1031"), @r"
1032        hover: table public.users(id int, email text)
1033          ╭▸ 
1034        3 │ create index idx on public.users(id);
1035          ╰╴                               ─ hover
1036        ");
1037    }
1038
1039    #[test]
1040    fn hover_table_temp() {
1041        assert_snapshot!(check_hover("
1042create temp table users(id int, email text);
1043create index idx on users$0(id);
1044"), @r"
1045        hover: table pg_temp.users(id int, email text)
1046          ╭▸ 
1047        3 │ create index idx on users(id);
1048          ╰╴                        ─ hover
1049        ");
1050    }
1051
1052    #[test]
1053    fn hover_table_multiline() {
1054        assert_snapshot!(check_hover("
1055create table users(
1056    id int,
1057    email text,
1058    name varchar(100)
1059);
1060create index idx on users$0(id);
1061"), @r"
1062        hover: table public.users(
1063                  id int,
1064                  email text,
1065                  name varchar(100)
1066              )
1067          ╭▸ 
1068        7 │ create index idx on users(id);
1069          ╰╴                        ─ hover
1070        ");
1071    }
1072
1073    #[test]
1074    fn hover_table_with_search_path() {
1075        assert_snapshot!(check_hover(r#"
1076set search_path to myschema;
1077create table users(id int, email text);
1078create index idx on users$0(id);
1079"#), @r"
1080        hover: table myschema.users(id int, email text)
1081          ╭▸ 
1082        4 │ create index idx on users(id);
1083          ╰╴                        ─ hover
1084        ");
1085    }
1086
1087    #[test]
1088    fn hover_table_search_path_at_definition() {
1089        assert_snapshot!(check_hover(r#"
1090set search_path to myschema;
1091create table users(id int, email text);
1092set search_path to myschema, otherschema;
1093create index idx on users$0(id);
1094"#), @r"
1095        hover: table myschema.users(id int, email text)
1096          ╭▸ 
1097        5 │ create index idx on users(id);
1098          ╰╴                        ─ hover
1099        ");
1100    }
1101
1102    #[test]
1103    fn hover_on_create_table_definition() {
1104        assert_snapshot!(check_hover("
1105create table t$0(x bigint);
1106"), @r"
1107        hover: table public.t(x bigint)
1108          ╭▸ 
1109        2 │ create table t(x bigint);
1110          ╰╴             ─ hover
1111        ");
1112    }
1113
1114    #[test]
1115    fn hover_on_create_table_definition_with_schema() {
1116        assert_snapshot!(check_hover("
1117create table myschema.users$0(id int);
1118"), @r"
1119        hover: table myschema.users(id int)
1120          ╭▸ 
1121        2 │ create table myschema.users(id int);
1122          ╰╴                          ─ hover
1123        ");
1124    }
1125
1126    #[test]
1127    fn hover_on_create_temp_table_definition() {
1128        assert_snapshot!(check_hover("
1129create temp table t$0(x bigint);
1130"), @r"
1131        hover: table pg_temp.t(x bigint)
1132          ╭▸ 
1133        2 │ create temp table t(x bigint);
1134          ╰╴                  ─ hover
1135        ");
1136    }
1137
1138    #[test]
1139    fn hover_on_column_in_create_table() {
1140        assert_snapshot!(check_hover("
1141create table t(id$0 int);
1142"), @r"
1143        hover: column public.t.id int
1144          ╭▸ 
1145        2 │ create table t(id int);
1146          ╰╴                ─ hover
1147        ");
1148    }
1149
1150    #[test]
1151    fn hover_on_column_in_create_table_with_schema() {
1152        assert_snapshot!(check_hover("
1153create table myschema.users(id$0 int, name text);
1154"), @r"
1155        hover: column myschema.users.id int
1156          ╭▸ 
1157        2 │ create table myschema.users(id int, name text);
1158          ╰╴                             ─ hover
1159        ");
1160    }
1161
1162    #[test]
1163    fn hover_on_column_in_temp_table() {
1164        assert_snapshot!(check_hover("
1165create temp table t(x$0 bigint);
1166"), @r"
1167        hover: column pg_temp.t.x bigint
1168          ╭▸ 
1169        2 │ create temp table t(x bigint);
1170          ╰╴                    ─ hover
1171        ");
1172    }
1173
1174    #[test]
1175    fn hover_on_multiple_columns() {
1176        assert_snapshot!(check_hover("
1177create table t(id int, email$0 text, name varchar(100));
1178"), @r"
1179        hover: column public.t.email text
1180          ╭▸ 
1181        2 │ create table t(id int, email text, name varchar(100));
1182          ╰╴                           ─ hover
1183        ");
1184    }
1185
1186    #[test]
1187    fn hover_on_drop_table() {
1188        assert_snapshot!(check_hover("
1189create table users(id int, email text);
1190drop table users$0;
1191"), @r"
1192        hover: table public.users(id int, email text)
1193          ╭▸ 
1194        3 │ drop table users;
1195          ╰╴               ─ hover
1196        ");
1197    }
1198
1199    #[test]
1200    fn hover_on_drop_table_with_schema() {
1201        assert_snapshot!(check_hover("
1202create table myschema.users(id int);
1203drop table myschema.users$0;
1204"), @r"
1205        hover: table myschema.users(id int)
1206          ╭▸ 
1207        3 │ drop table myschema.users;
1208          ╰╴                        ─ hover
1209        ");
1210    }
1211
1212    #[test]
1213    fn hover_on_drop_temp_table() {
1214        assert_snapshot!(check_hover("
1215create temp table t(x bigint);
1216drop table t$0;
1217"), @r"
1218        hover: table pg_temp.t(x bigint)
1219          ╭▸ 
1220        3 │ drop table t;
1221          ╰╴           ─ hover
1222        ");
1223    }
1224
1225    #[test]
1226    fn hover_on_create_index_definition() {
1227        assert_snapshot!(check_hover("
1228create table t(x bigint);
1229create index idx$0 on t(x);
1230"), @r"
1231        hover: index public.idx on public.t(x)
1232          ╭▸ 
1233        3 │ create index idx on t(x);
1234          ╰╴               ─ hover
1235        ");
1236    }
1237
1238    #[test]
1239    fn hover_on_drop_index() {
1240        assert_snapshot!(check_hover("
1241create table t(x bigint);
1242create index idx_x on t(x);
1243drop index idx_x$0;
1244"), @r"
1245        hover: index public.idx_x on public.t(x)
1246          ╭▸ 
1247        4 │ drop index idx_x;
1248          ╰╴               ─ hover
1249        ");
1250    }
1251
1252    #[test]
1253    fn hover_on_create_type_definition() {
1254        assert_snapshot!(check_hover("
1255create type status$0 as enum ('active', 'inactive');
1256"), @r"
1257        hover: type public.status as enum ('active', 'inactive')
1258          ╭▸ 
1259        2 │ create type status as enum ('active', 'inactive');
1260          ╰╴                 ─ hover
1261        ");
1262    }
1263
1264    #[test]
1265    fn hover_on_create_type_definition_with_schema() {
1266        assert_snapshot!(check_hover("
1267create type myschema.status$0 as enum ('active', 'inactive');
1268"), @r"
1269        hover: type myschema.status as enum ('active', 'inactive')
1270          ╭▸ 
1271        2 │ create type myschema.status as enum ('active', 'inactive');
1272          ╰╴                          ─ hover
1273        ");
1274    }
1275
1276    #[test]
1277    fn hover_on_drop_type() {
1278        assert_snapshot!(check_hover("
1279create type status as enum ('active', 'inactive');
1280drop type status$0;
1281"), @r"
1282        hover: type public.status as enum ('active', 'inactive')
1283          ╭▸ 
1284        3 │ drop type status;
1285          ╰╴               ─ hover
1286        ");
1287    }
1288
1289    #[test]
1290    fn hover_on_drop_type_with_schema() {
1291        assert_snapshot!(check_hover("
1292create type myschema.status as enum ('active', 'inactive');
1293drop type myschema.status$0;
1294"), @r"
1295        hover: type myschema.status as enum ('active', 'inactive')
1296          ╭▸ 
1297        3 │ drop type myschema.status;
1298          ╰╴                        ─ hover
1299        ");
1300    }
1301
1302    #[test]
1303    fn hover_on_create_type_composite() {
1304        assert_snapshot!(check_hover("
1305create type person$0 as (name text, age int);
1306"), @r"
1307        hover: type public.person as (name text, age int)
1308          ╭▸ 
1309        2 │ create type person as (name text, age int);
1310          ╰╴                 ─ hover
1311        ");
1312    }
1313
1314    #[test]
1315    fn hover_on_drop_type_composite() {
1316        assert_snapshot!(check_hover("
1317create type person as (name text, age int);
1318drop type person$0;
1319"), @r"
1320        hover: type public.person as (name text, age int)
1321          ╭▸ 
1322        3 │ drop type person;
1323          ╰╴               ─ hover
1324        ");
1325    }
1326
1327    #[test]
1328    fn hover_on_create_type_range() {
1329        assert_snapshot!(check_hover("
1330create type int4_range$0 as range (subtype = int4);
1331"), @r"
1332        hover: type public.int4_range (subtype = int4)
1333          ╭▸ 
1334        2 │ create type int4_range as range (subtype = int4);
1335          ╰╴                     ─ hover
1336        ");
1337    }
1338
1339    #[test]
1340    fn hover_on_drop_type_range() {
1341        assert_snapshot!(check_hover("
1342create type int4_range as range (subtype = int4);
1343drop type int4_range$0;
1344"), @r"
1345        hover: type public.int4_range (subtype = int4)
1346          ╭▸ 
1347        3 │ drop type int4_range;
1348          ╰╴                   ─ hover
1349        ");
1350    }
1351
1352    #[test]
1353    fn hover_on_cast_operator() {
1354        assert_snapshot!(check_hover("
1355create type foo as enum ('a', 'b');
1356select x::foo$0;
1357"), @r"
1358        hover: type public.foo as enum ('a', 'b')
1359          ╭▸ 
1360        3 │ select x::foo;
1361          ╰╴            ─ hover
1362        ");
1363    }
1364
1365    #[test]
1366    fn hover_on_cast_function() {
1367        assert_snapshot!(check_hover("
1368create type bar as enum ('x', 'y');
1369select cast(x as bar$0);
1370"), @r"
1371        hover: type public.bar as enum ('x', 'y')
1372          ╭▸ 
1373        3 │ select cast(x as bar);
1374          ╰╴                   ─ hover
1375        ");
1376    }
1377
1378    #[test]
1379    fn hover_on_cast_with_schema() {
1380        assert_snapshot!(check_hover("
1381create type myschema.baz as enum ('m', 'n');
1382select x::myschema.baz$0;
1383"), @r"
1384        hover: type myschema.baz as enum ('m', 'n')
1385          ╭▸ 
1386        3 │ select x::myschema.baz;
1387          ╰╴                     ─ hover
1388        ");
1389    }
1390
1391    #[test]
1392    fn hover_on_drop_function() {
1393        assert_snapshot!(check_hover("
1394create function foo() returns int as $$ select 1 $$ language sql;
1395drop function foo$0();
1396"), @r"
1397        hover: function public.foo() returns int
1398          ╭▸ 
1399        3 │ drop function foo();
1400          ╰╴                ─ hover
1401        ");
1402    }
1403
1404    #[test]
1405    fn hover_on_drop_function_with_schema() {
1406        assert_snapshot!(check_hover("
1407create function myschema.foo() returns int as $$ select 1 $$ language sql;
1408drop function myschema.foo$0();
1409"), @r"
1410        hover: function myschema.foo() returns int
1411          ╭▸ 
1412        3 │ drop function myschema.foo();
1413          ╰╴                         ─ hover
1414        ");
1415    }
1416
1417    #[test]
1418    fn hover_on_create_function_definition() {
1419        assert_snapshot!(check_hover("
1420create function foo$0() returns int as $$ select 1 $$ language sql;
1421"), @r"
1422        hover: function public.foo() returns int
1423          ╭▸ 
1424        2 │ create function foo() returns int as $$ select 1 $$ language sql;
1425          ╰╴                  ─ hover
1426        ");
1427    }
1428
1429    #[test]
1430    fn hover_on_create_function_with_explicit_schema() {
1431        assert_snapshot!(check_hover("
1432create function myschema.foo$0() returns int as $$ select 1 $$ language sql;
1433"), @r"
1434        hover: function myschema.foo() returns int
1435          ╭▸ 
1436        2 │ create function myschema.foo() returns int as $$ select 1 $$ language sql;
1437          ╰╴                           ─ hover
1438        ");
1439    }
1440
1441    #[test]
1442    fn hover_on_drop_function_with_search_path() {
1443        assert_snapshot!(check_hover(r#"
1444set search_path to myschema;
1445create function foo() returns int as $$ select 1 $$ language sql;
1446drop function foo$0();
1447"#), @r"
1448        hover: function myschema.foo() returns int
1449          ╭▸ 
1450        4 │ drop function foo();
1451          ╰╴                ─ hover
1452        ");
1453    }
1454
1455    #[test]
1456    fn hover_on_drop_function_overloaded() {
1457        assert_snapshot!(check_hover("
1458create function add(complex) returns complex as $$ select null $$ language sql;
1459create function add(bigint) returns bigint as $$ select 1 $$ language sql;
1460drop function add$0(complex);
1461"), @r"
1462        hover: function public.add(complex) returns complex
1463          ╭▸ 
1464        4 │ drop function add(complex);
1465          ╰╴                ─ hover
1466        ");
1467    }
1468
1469    #[test]
1470    fn hover_on_drop_function_second_overload() {
1471        assert_snapshot!(check_hover("
1472create function add(complex) returns complex as $$ select null $$ language sql;
1473create function add(bigint) returns bigint as $$ select 1 $$ language sql;
1474drop function add$0(bigint);
1475"), @r"
1476        hover: function public.add(bigint) returns bigint
1477          ╭▸ 
1478        4 │ drop function add(bigint);
1479          ╰╴                ─ hover
1480        ");
1481    }
1482
1483    #[test]
1484    fn hover_on_drop_aggregate() {
1485        assert_snapshot!(check_hover("
1486create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
1487drop aggregate myavg$0(int);
1488"), @r"
1489        hover: aggregate public.myavg(int)
1490          ╭▸ 
1491        3 │ drop aggregate myavg(int);
1492          ╰╴                   ─ hover
1493        ");
1494    }
1495
1496    #[test]
1497    fn hover_on_drop_aggregate_with_schema() {
1498        assert_snapshot!(check_hover("
1499create aggregate myschema.myavg(int) (sfunc = int4_avg_accum, stype = _int8);
1500drop aggregate myschema.myavg$0(int);
1501"), @r"
1502        hover: aggregate myschema.myavg(int)
1503          ╭▸ 
1504        3 │ drop aggregate myschema.myavg(int);
1505          ╰╴                            ─ hover
1506        ");
1507    }
1508
1509    #[test]
1510    fn hover_on_create_aggregate_definition() {
1511        assert_snapshot!(check_hover("
1512create aggregate myavg$0(int) (sfunc = int4_avg_accum, stype = _int8);
1513"), @r"
1514        hover: aggregate public.myavg(int)
1515          ╭▸ 
1516        2 │ create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
1517          ╰╴                     ─ hover
1518        ");
1519    }
1520
1521    #[test]
1522    fn hover_on_drop_aggregate_with_search_path() {
1523        assert_snapshot!(check_hover(r#"
1524set search_path to myschema;
1525create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
1526drop aggregate myavg$0(int);
1527"#), @r"
1528        hover: aggregate myschema.myavg(int)
1529          ╭▸ 
1530        4 │ drop aggregate myavg(int);
1531          ╰╴                   ─ hover
1532        ");
1533    }
1534
1535    #[test]
1536    fn hover_on_drop_aggregate_overloaded() {
1537        assert_snapshot!(check_hover("
1538create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)');
1539create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0');
1540drop aggregate sum$0(complex);
1541"), @r"
1542        hover: aggregate public.sum(complex)
1543          ╭▸ 
1544        4 │ drop aggregate sum(complex);
1545          ╰╴                 ─ hover
1546        ");
1547    }
1548
1549    #[test]
1550    fn hover_on_drop_aggregate_second_overload() {
1551        assert_snapshot!(check_hover("
1552create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)');
1553create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0');
1554drop aggregate sum$0(bigint);
1555"), @r"
1556        hover: aggregate public.sum(bigint)
1557          ╭▸ 
1558        4 │ drop aggregate sum(bigint);
1559          ╰╴                 ─ hover
1560        ");
1561    }
1562
1563    #[test]
1564    fn hover_on_select_function_call() {
1565        assert_snapshot!(check_hover("
1566create function foo() returns int as $$ select 1 $$ language sql;
1567select foo$0();
1568"), @r"
1569        hover: function public.foo() returns int
1570          ╭▸ 
1571        3 │ select foo();
1572          ╰╴         ─ hover
1573        ");
1574    }
1575
1576    #[test]
1577    fn hover_on_select_function_call_with_schema() {
1578        assert_snapshot!(check_hover("
1579create function public.foo() returns int as $$ select 1 $$ language sql;
1580select public.foo$0();
1581"), @r"
1582        hover: function public.foo() returns int
1583          ╭▸ 
1584        3 │ select public.foo();
1585          ╰╴                ─ hover
1586        ");
1587    }
1588
1589    #[test]
1590    fn hover_on_select_function_call_with_search_path() {
1591        assert_snapshot!(check_hover(r#"
1592set search_path to myschema;
1593create function foo() returns int as $$ select 1 $$ language sql;
1594select foo$0();
1595"#), @r"
1596        hover: function myschema.foo() returns int
1597          ╭▸ 
1598        4 │ select foo();
1599          ╰╴         ─ hover
1600        ");
1601    }
1602
1603    #[test]
1604    fn hover_on_select_function_call_with_params() {
1605        assert_snapshot!(check_hover("
1606create function add(a int, b int) returns int as $$ select a + b $$ language sql;
1607select add$0(1, 2);
1608"), @r"
1609        hover: function public.add(a int, b int) returns int
1610          ╭▸ 
1611        3 │ select add(1, 2);
1612          ╰╴         ─ hover
1613        ");
1614    }
1615
1616    #[test]
1617    fn hover_on_function_call_style_column_access() {
1618        assert_snapshot!(check_hover("
1619create table t(a int, b int);
1620select a$0(t) from t;
1621"), @r"
1622        hover: column public.t.a int
1623          ╭▸ 
1624        3 │ select a(t) from t;
1625          ╰╴       ─ hover
1626        ");
1627    }
1628
1629    #[test]
1630    fn hover_on_function_call_style_column_access_with_function_precedence() {
1631        assert_snapshot!(check_hover("
1632create table t(a int, b int);
1633create function b(t) returns int as 'select 1' LANGUAGE sql;
1634select b$0(t) from t;
1635"), @r"
1636        hover: function public.b(t) returns int
1637          ╭▸ 
1638        4 │ select b(t) from t;
1639          ╰╴       ─ hover
1640        ");
1641    }
1642
1643    #[test]
1644    fn hover_on_function_call_style_table_arg() {
1645        assert_snapshot!(check_hover("
1646create table t(a int, b int);
1647select a(t$0) from t;
1648"), @r"
1649        hover: table public.t(a int, b int)
1650          ╭▸ 
1651        3 │ select a(t) from t;
1652          ╰╴         ─ hover
1653        ");
1654    }
1655
1656    #[test]
1657    fn hover_on_function_call_style_table_arg_with_function() {
1658        assert_snapshot!(check_hover("
1659create table t(a int, b int);
1660create function b(t) returns int as 'select 1' LANGUAGE sql;
1661select b(t$0) from t;
1662"), @r"
1663        hover: table public.t(a int, b int)
1664          ╭▸ 
1665        4 │ select b(t) from t;
1666          ╰╴         ─ hover
1667        ");
1668    }
1669
1670    #[test]
1671    fn hover_on_function_call_style_table_arg_in_where() {
1672        assert_snapshot!(check_hover("
1673create table t(a int);
1674select * from t where a(t$0) > 2;
1675"), @r"
1676        hover: table public.t(a int)
1677          ╭▸ 
1678        3 │ select * from t where a(t) > 2;
1679          ╰╴                        ─ hover
1680        ");
1681    }
1682
1683    #[test]
1684    fn hover_on_qualified_table_ref_in_where() {
1685        assert_snapshot!(check_hover("
1686create table t(a int);
1687create function b(t) returns int as 'select 1' language sql;
1688select * from t where t$0.b > 2;
1689"), @r"
1690        hover: table public.t(a int)
1691          ╭▸ 
1692        4 │ select * from t where t.b > 2;
1693          ╰╴                      ─ hover
1694        ");
1695    }
1696
1697    #[test]
1698    fn hover_on_field_style_function_call() {
1699        assert_snapshot!(check_hover("
1700create table t(a int);
1701create function b(t) returns int as 'select 1' language sql;
1702select t.b$0 from t;
1703"), @r"
1704        hover: function public.b(t) returns int
1705          ╭▸ 
1706        4 │ select t.b from t;
1707          ╰╴         ─ hover
1708        ");
1709    }
1710
1711    #[test]
1712    fn hover_on_field_style_function_call_column_precedence() {
1713        assert_snapshot!(check_hover("
1714create table t(a int, b int);
1715create function b(t) returns int as 'select 1' language sql;
1716select t.b$0 from t;
1717"), @r"
1718        hover: column public.t.b int
1719          ╭▸ 
1720        4 │ select t.b from t;
1721          ╰╴         ─ hover
1722        ");
1723    }
1724
1725    #[test]
1726    fn hover_on_field_style_function_call_table_ref() {
1727        assert_snapshot!(check_hover("
1728create table t(a int);
1729create function b(t) returns int as 'select 1' language sql;
1730select t$0.b from t;
1731"), @r"
1732        hover: table public.t(a int)
1733          ╭▸ 
1734        4 │ select t.b from t;
1735          ╰╴       ─ hover
1736        ");
1737    }
1738
1739    #[test]
1740    fn hover_on_select_from_table() {
1741        assert_snapshot!(check_hover("
1742create table users(id int, email text);
1743select * from users$0;
1744"), @r"
1745        hover: table public.users(id int, email text)
1746          ╭▸ 
1747        3 │ select * from users;
1748          ╰╴                  ─ hover
1749        ");
1750    }
1751
1752    #[test]
1753    fn hover_on_select_from_table_with_schema() {
1754        assert_snapshot!(check_hover("
1755create table public.users(id int, email text);
1756select * from public.users$0;
1757"), @r"
1758        hover: table public.users(id int, email text)
1759          ╭▸ 
1760        3 │ select * from public.users;
1761          ╰╴                         ─ hover
1762        ");
1763    }
1764
1765    #[test]
1766    fn hover_on_select_from_table_with_search_path() {
1767        assert_snapshot!(check_hover("
1768set search_path to foo;
1769create table foo.users(id int, email text);
1770select * from users$0;
1771"), @r"
1772        hover: table foo.users(id int, email text)
1773          ╭▸ 
1774        4 │ select * from users;
1775          ╰╴                  ─ hover
1776        ");
1777    }
1778
1779    #[test]
1780    fn hover_on_select_from_temp_table() {
1781        assert_snapshot!(check_hover("
1782create temp table users(id int, email text);
1783select * from users$0;
1784"), @r"
1785        hover: table pg_temp.users(id int, email text)
1786          ╭▸ 
1787        3 │ select * from users;
1788          ╰╴                  ─ hover
1789        ");
1790    }
1791
1792    #[test]
1793    fn hover_on_select_from_multiline_table() {
1794        assert_snapshot!(check_hover("
1795create table users(
1796    id int,
1797    email text,
1798    name varchar(100)
1799);
1800select * from users$0;
1801"), @r"
1802        hover: table public.users(
1803                  id int,
1804                  email text,
1805                  name varchar(100)
1806              )
1807          ╭▸ 
1808        7 │ select * from users;
1809          ╰╴                  ─ hover
1810        ");
1811    }
1812
1813    #[test]
1814    fn hover_on_select_column() {
1815        assert_snapshot!(check_hover("
1816create table users(id int, email text);
1817select id$0 from users;
1818"), @r"
1819        hover: column public.users.id int
1820          ╭▸ 
1821        3 │ select id from users;
1822          ╰╴        ─ hover
1823        ");
1824    }
1825
1826    #[test]
1827    fn hover_on_select_column_second() {
1828        assert_snapshot!(check_hover("
1829create table users(id int, email text);
1830select id, email$0 from users;
1831"), @r"
1832        hover: column public.users.email text
1833          ╭▸ 
1834        3 │ select id, email from users;
1835          ╰╴               ─ hover
1836        ");
1837    }
1838
1839    #[test]
1840    fn hover_on_select_column_with_schema() {
1841        assert_snapshot!(check_hover("
1842create table public.users(id int, email text);
1843select email$0 from public.users;
1844"), @r"
1845        hover: column public.users.email text
1846          ╭▸ 
1847        3 │ select email from public.users;
1848          ╰╴           ─ hover
1849        ");
1850    }
1851
1852    #[test]
1853    fn hover_on_select_column_with_search_path() {
1854        assert_snapshot!(check_hover("
1855set search_path to foo;
1856create table foo.users(id int, email text);
1857select id$0 from users;
1858"), @r"
1859        hover: column foo.users.id int
1860          ╭▸ 
1861        4 │ select id from users;
1862          ╰╴        ─ hover
1863        ");
1864    }
1865
1866    #[test]
1867    fn hover_on_insert_table() {
1868        assert_snapshot!(check_hover("
1869create table users(id int, email text);
1870insert into users$0(id, email) values (1, 'test');
1871"), @r"
1872        hover: table public.users(id int, email text)
1873          ╭▸ 
1874        3 │ insert into users(id, email) values (1, 'test');
1875          ╰╴                ─ hover
1876        ");
1877    }
1878
1879    #[test]
1880    fn hover_on_insert_table_with_schema() {
1881        assert_snapshot!(check_hover("
1882create table public.users(id int, email text);
1883insert into public.users$0(id, email) values (1, 'test');
1884"), @r"
1885        hover: table public.users(id int, email text)
1886          ╭▸ 
1887        3 │ insert into public.users(id, email) values (1, 'test');
1888          ╰╴                       ─ hover
1889        ");
1890    }
1891
1892    #[test]
1893    fn hover_on_insert_column() {
1894        assert_snapshot!(check_hover("
1895create table users(id int, email text);
1896insert into users(id$0, email) values (1, 'test');
1897"), @r"
1898        hover: column public.users.id int
1899          ╭▸ 
1900        3 │ insert into users(id, email) values (1, 'test');
1901          ╰╴                   ─ hover
1902        ");
1903    }
1904
1905    #[test]
1906    fn hover_on_insert_column_second() {
1907        assert_snapshot!(check_hover("
1908create table users(id int, email text);
1909insert into users(id, email$0) values (1, 'test');
1910"), @r"
1911        hover: column public.users.email text
1912          ╭▸ 
1913        3 │ insert into users(id, email) values (1, 'test');
1914          ╰╴                          ─ hover
1915        ");
1916    }
1917
1918    #[test]
1919    fn hover_on_insert_column_with_schema() {
1920        assert_snapshot!(check_hover("
1921create table public.users(id int, email text);
1922insert into public.users(email$0) values ('test');
1923"), @r"
1924        hover: column public.users.email text
1925          ╭▸ 
1926        3 │ insert into public.users(email) values ('test');
1927          ╰╴                             ─ hover
1928        ");
1929    }
1930
1931    #[test]
1932    fn hover_on_delete_table() {
1933        assert_snapshot!(check_hover("
1934create table users(id int, email text);
1935delete from users$0 where id = 1;
1936"), @r"
1937        hover: table public.users(id int, email text)
1938          ╭▸ 
1939        3 │ delete from users where id = 1;
1940          ╰╴                ─ hover
1941        ");
1942    }
1943
1944    #[test]
1945    fn hover_on_delete_table_with_schema() {
1946        assert_snapshot!(check_hover("
1947create table public.users(id int, email text);
1948delete from public.users$0 where id = 1;
1949"), @r"
1950        hover: table public.users(id int, email text)
1951          ╭▸ 
1952        3 │ delete from public.users where id = 1;
1953          ╰╴                       ─ hover
1954        ");
1955    }
1956
1957    #[test]
1958    fn hover_on_delete_where_column() {
1959        assert_snapshot!(check_hover("
1960create table users(id int, email text);
1961delete from users where id$0 = 1;
1962"), @r"
1963        hover: column public.users.id int
1964          ╭▸ 
1965        3 │ delete from users where id = 1;
1966          ╰╴                         ─ hover
1967        ");
1968    }
1969
1970    #[test]
1971    fn hover_on_delete_where_column_second() {
1972        assert_snapshot!(check_hover("
1973create table users(id int, email text, active boolean);
1974delete from users where id = 1 and email$0 = 'test';
1975"), @r"
1976        hover: column public.users.email text
1977          ╭▸ 
1978        3 │ delete from users where id = 1 and email = 'test';
1979          ╰╴                                       ─ hover
1980        ");
1981    }
1982
1983    #[test]
1984    fn hover_on_delete_where_column_with_schema() {
1985        assert_snapshot!(check_hover("
1986create table public.users(id int, email text);
1987delete from public.users where email$0 = 'test';
1988"), @r"
1989        hover: column public.users.email text
1990          ╭▸ 
1991        3 │ delete from public.users where email = 'test';
1992          ╰╴                                   ─ hover
1993        ");
1994    }
1995
1996    #[test]
1997    fn hover_on_select_table_as_column() {
1998        assert_snapshot!(check_hover("
1999create table t(x bigint, y bigint);
2000select t$0 from t;
2001"), @r"
2002        hover: table public.t(x bigint, y bigint)
2003          ╭▸ 
2004        3 │ select t from t;
2005          ╰╴       ─ hover
2006        ");
2007    }
2008
2009    #[test]
2010    fn hover_on_select_table_as_column_with_schema() {
2011        assert_snapshot!(check_hover("
2012create table public.t(x bigint, y bigint);
2013select t$0 from public.t;
2014"), @r"
2015        hover: table public.t(x bigint, y bigint)
2016          ╭▸ 
2017        3 │ select t from public.t;
2018          ╰╴       ─ hover
2019        ");
2020    }
2021
2022    #[test]
2023    fn hover_on_select_table_as_column_with_search_path() {
2024        assert_snapshot!(check_hover("
2025set search_path to foo;
2026create table foo.users(id int, email text);
2027select users$0 from users;
2028"), @r"
2029        hover: table foo.users(id int, email text)
2030          ╭▸ 
2031        4 │ select users from users;
2032          ╰╴           ─ hover
2033        ");
2034    }
2035
2036    #[test]
2037    fn hover_on_select_column_with_same_name_as_table() {
2038        assert_snapshot!(check_hover("
2039create table t(t int);
2040select t$0 from t;
2041"), @r"
2042        hover: column public.t.t int
2043          ╭▸ 
2044        3 │ select t from t;
2045          ╰╴       ─ hover
2046        ");
2047    }
2048
2049    #[test]
2050    fn hover_on_create_schema() {
2051        assert_snapshot!(check_hover("
2052create schema foo$0;
2053"), @r"
2054        hover: schema foo
2055          ╭▸ 
2056        2 │ create schema foo;
2057          ╰╴                ─ hover
2058        ");
2059    }
2060
2061    #[test]
2062    fn hover_on_drop_schema() {
2063        assert_snapshot!(check_hover("
2064create schema foo;
2065drop schema foo$0;
2066"), @r"
2067        hover: schema foo
2068          ╭▸ 
2069        3 │ drop schema foo;
2070          ╰╴              ─ hover
2071        ");
2072    }
2073
2074    #[test]
2075    fn hover_on_schema_after_definition() {
2076        assert_snapshot!(check_hover("
2077drop schema foo$0;
2078create schema foo;
2079"), @r"
2080        hover: schema foo
2081          ╭▸ 
2082        2 │ drop schema foo;
2083          ╰╴              ─ hover
2084        ");
2085    }
2086
2087    #[test]
2088    fn hover_on_cte_table() {
2089        assert_snapshot!(check_hover("
2090with t as (select 1 a)
2091select a from t$0;
2092"), @r"
2093        hover: with t as (select 1 a)
2094          ╭▸ 
2095        3 │ select a from t;
2096          ╰╴              ─ hover
2097        ");
2098    }
2099
2100    #[test]
2101    fn hover_on_cte_column() {
2102        assert_snapshot!(check_hover("
2103with t as (select 1 a)
2104select a$0 from t;
2105"), @r"
2106        hover: column t.a
2107          ╭▸ 
2108        3 │ select a from t;
2109          ╰╴       ─ hover
2110        ");
2111    }
2112
2113    #[test]
2114    fn hover_on_cte_with_multiple_columns() {
2115        assert_snapshot!(check_hover("
2116with t as (select 1 a, 2 b)
2117select b$0 from t;
2118"), @r"
2119        hover: column t.b
2120          ╭▸ 
2121        3 │ select b from t;
2122          ╰╴       ─ hover
2123        ");
2124    }
2125
2126    #[test]
2127    fn hover_on_cte_with_column_list() {
2128        assert_snapshot!(check_hover("
2129with t(a) as (select 1)
2130select a$0 from t;
2131"), @r"
2132        hover: column t.a
2133          ╭▸ 
2134        3 │ select a from t;
2135          ╰╴       ─ hover
2136        ");
2137    }
2138
2139    #[test]
2140    fn hover_on_nested_cte() {
2141        assert_snapshot!(check_hover("
2142with x as (select 1 a),
2143     y as (select a from x)
2144select a$0 from y;
2145"), @r"
2146        hover: column y.a
2147          ╭▸ 
2148        4 │ select a from y;
2149          ╰╴       ─ hover
2150        ");
2151    }
2152
2153    #[test]
2154    fn hover_on_cte_shadowing_table_with_star() {
2155        assert_snapshot!(check_hover("
2156create table t(a bigint);
2157with t as (select * from t)
2158select a$0 from t;
2159"), @r"
2160        hover: column public.t.a bigint
2161          ╭▸ 
2162        4 │ select a from t;
2163          ╰╴       ─ hover
2164        ");
2165    }
2166
2167    #[test]
2168    fn hover_on_cte_definition() {
2169        assert_snapshot!(check_hover("
2170with t$0 as (select 1 a)
2171select a from t;
2172"), @r"
2173        hover: with t as (select 1 a)
2174          ╭▸ 
2175        2 │ with t as (select 1 a)
2176          ╰╴     ─ hover
2177        ");
2178    }
2179
2180    #[test]
2181    fn hover_on_cte_values_column1() {
2182        assert_snapshot!(check_hover("
2183with t as (
2184    values (1, 2), (3, 4)
2185)
2186select column1$0, column2 from t;
2187"), @r"
2188        hover: column t.column1
2189          ╭▸ 
2190        5 │ select column1, column2 from t;
2191          ╰╴             ─ hover
2192        ");
2193    }
2194
2195    #[test]
2196    fn hover_on_cte_values_column2() {
2197        assert_snapshot!(check_hover("
2198with t as (
2199    values (1, 2), (3, 4)
2200)
2201select column1, column2$0 from t;
2202"), @r"
2203        hover: column t.column2
2204          ╭▸ 
2205        5 │ select column1, column2 from t;
2206          ╰╴                      ─ hover
2207        ");
2208    }
2209
2210    #[test]
2211    fn hover_on_cte_values_single_column() {
2212        assert_snapshot!(check_hover("
2213with t as (
2214    values (1), (2), (3)
2215)
2216select column1$0 from t;
2217"), @r"
2218        hover: column t.column1
2219          ╭▸ 
2220        5 │ select column1 from t;
2221          ╰╴             ─ hover
2222        ");
2223    }
2224
2225    #[test]
2226    fn hover_on_cte_values_uppercase_column_names() {
2227        assert_snapshot!(check_hover("
2228with t as (
2229    values (1, 2), (3, 4)
2230)
2231select COLUMN1$0, COLUMN2 from t;
2232"), @r"
2233        hover: column t.column1
2234          ╭▸ 
2235        5 │ select COLUMN1, COLUMN2 from t;
2236          ╰╴             ─ hover
2237        ");
2238    }
2239
2240    #[test]
2241    fn hover_on_drop_procedure() {
2242        assert_snapshot!(check_hover("
2243create procedure foo() language sql as $$ select 1 $$;
2244drop procedure foo$0();
2245"), @r"
2246        hover: procedure public.foo()
2247          ╭▸ 
2248        3 │ drop procedure foo();
2249          ╰╴                 ─ hover
2250        ");
2251    }
2252
2253    #[test]
2254    fn hover_on_drop_procedure_with_schema() {
2255        assert_snapshot!(check_hover("
2256create procedure myschema.foo() language sql as $$ select 1 $$;
2257drop procedure myschema.foo$0();
2258"), @r"
2259        hover: procedure myschema.foo()
2260          ╭▸ 
2261        3 │ drop procedure myschema.foo();
2262          ╰╴                          ─ hover
2263        ");
2264    }
2265
2266    #[test]
2267    fn hover_on_create_procedure_definition() {
2268        assert_snapshot!(check_hover("
2269create procedure foo$0() language sql as $$ select 1 $$;
2270"), @r"
2271        hover: procedure public.foo()
2272          ╭▸ 
2273        2 │ create procedure foo() language sql as $$ select 1 $$;
2274          ╰╴                   ─ hover
2275        ");
2276    }
2277
2278    #[test]
2279    fn hover_on_create_procedure_with_explicit_schema() {
2280        assert_snapshot!(check_hover("
2281create procedure myschema.foo$0() language sql as $$ select 1 $$;
2282"), @r"
2283        hover: procedure myschema.foo()
2284          ╭▸ 
2285        2 │ create procedure myschema.foo() language sql as $$ select 1 $$;
2286          ╰╴                            ─ hover
2287        ");
2288    }
2289
2290    #[test]
2291    fn hover_on_drop_procedure_with_search_path() {
2292        assert_snapshot!(check_hover(r#"
2293set search_path to myschema;
2294create procedure foo() language sql as $$ select 1 $$;
2295drop procedure foo$0();
2296"#), @r"
2297        hover: procedure myschema.foo()
2298          ╭▸ 
2299        4 │ drop procedure foo();
2300          ╰╴                 ─ hover
2301        ");
2302    }
2303
2304    #[test]
2305    fn hover_on_drop_procedure_overloaded() {
2306        assert_snapshot!(check_hover("
2307create procedure add(complex) language sql as $$ select null $$;
2308create procedure add(bigint) language sql as $$ select 1 $$;
2309drop procedure add$0(complex);
2310"), @r"
2311        hover: procedure public.add(complex)
2312          ╭▸ 
2313        4 │ drop procedure add(complex);
2314          ╰╴                 ─ hover
2315        ");
2316    }
2317
2318    #[test]
2319    fn hover_on_drop_procedure_second_overload() {
2320        assert_snapshot!(check_hover("
2321create procedure add(complex) language sql as $$ select null $$;
2322create procedure add(bigint) language sql as $$ select 1 $$;
2323drop procedure add$0(bigint);
2324"), @r"
2325        hover: procedure public.add(bigint)
2326          ╭▸ 
2327        4 │ drop procedure add(bigint);
2328          ╰╴                 ─ hover
2329        ");
2330    }
2331
2332    #[test]
2333    fn hover_on_call_procedure() {
2334        assert_snapshot!(check_hover("
2335create procedure foo() language sql as $$ select 1 $$;
2336call foo$0();
2337"), @r"
2338        hover: procedure public.foo()
2339          ╭▸ 
2340        3 │ call foo();
2341          ╰╴       ─ hover
2342        ");
2343    }
2344
2345    #[test]
2346    fn hover_on_call_procedure_with_schema() {
2347        assert_snapshot!(check_hover("
2348create procedure public.foo() language sql as $$ select 1 $$;
2349call public.foo$0();
2350"), @r"
2351        hover: procedure public.foo()
2352          ╭▸ 
2353        3 │ call public.foo();
2354          ╰╴              ─ hover
2355        ");
2356    }
2357
2358    #[test]
2359    fn hover_on_call_procedure_with_search_path() {
2360        assert_snapshot!(check_hover(r#"
2361set search_path to myschema;
2362create procedure foo() language sql as $$ select 1 $$;
2363call foo$0();
2364"#), @r"
2365        hover: procedure myschema.foo()
2366          ╭▸ 
2367        4 │ call foo();
2368          ╰╴       ─ hover
2369        ");
2370    }
2371
2372    #[test]
2373    fn hover_on_call_procedure_with_params() {
2374        assert_snapshot!(check_hover("
2375create procedure add(a int, b int) language sql as $$ select a + b $$;
2376call add$0(1, 2);
2377"), @r"
2378        hover: procedure public.add(a int, b int)
2379          ╭▸ 
2380        3 │ call add(1, 2);
2381          ╰╴       ─ hover
2382        ");
2383    }
2384
2385    #[test]
2386    fn hover_on_drop_routine_function() {
2387        assert_snapshot!(check_hover("
2388create function foo() returns int as $$ select 1 $$ language sql;
2389drop routine foo$0();
2390"), @r"
2391        hover: function public.foo() returns int
2392          ╭▸ 
2393        3 │ drop routine foo();
2394          ╰╴               ─ hover
2395        ");
2396    }
2397
2398    #[test]
2399    fn hover_on_drop_routine_aggregate() {
2400        assert_snapshot!(check_hover("
2401create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
2402drop routine myavg$0(int);
2403"), @r"
2404        hover: aggregate public.myavg(int)
2405          ╭▸ 
2406        3 │ drop routine myavg(int);
2407          ╰╴                 ─ hover
2408        ");
2409    }
2410
2411    #[test]
2412    fn hover_on_drop_routine_procedure() {
2413        assert_snapshot!(check_hover("
2414create procedure foo() language sql as $$ select 1 $$;
2415drop routine foo$0();
2416"), @r"
2417        hover: procedure public.foo()
2418          ╭▸ 
2419        3 │ drop routine foo();
2420          ╰╴               ─ hover
2421        ");
2422    }
2423
2424    #[test]
2425    fn hover_on_drop_routine_with_schema() {
2426        assert_snapshot!(check_hover("
2427set search_path to public;
2428create function foo() returns int as $$ select 1 $$ language sql;
2429drop routine public.foo$0();
2430"), @r"
2431        hover: function public.foo() returns int
2432          ╭▸ 
2433        4 │ drop routine public.foo();
2434          ╰╴                      ─ hover
2435        ");
2436    }
2437
2438    #[test]
2439    fn hover_on_drop_routine_with_search_path() {
2440        assert_snapshot!(check_hover(r#"
2441set search_path to myschema;
2442create function foo() returns int as $$ select 1 $$ language sql;
2443drop routine foo$0();
2444"#), @r"
2445        hover: function myschema.foo() returns int
2446          ╭▸ 
2447        4 │ drop routine foo();
2448          ╰╴               ─ hover
2449        ");
2450    }
2451
2452    #[test]
2453    fn hover_on_drop_routine_overloaded() {
2454        assert_snapshot!(check_hover("
2455create function add(complex) returns complex as $$ select null $$ language sql;
2456create function add(bigint) returns bigint as $$ select 1 $$ language sql;
2457drop routine add$0(complex);
2458"), @r"
2459        hover: function public.add(complex) returns complex
2460          ╭▸ 
2461        4 │ drop routine add(complex);
2462          ╰╴               ─ hover
2463        ");
2464    }
2465
2466    #[test]
2467    fn hover_on_drop_routine_prefers_function_over_procedure() {
2468        assert_snapshot!(check_hover("
2469create function foo() returns int as $$ select 1 $$ language sql;
2470create procedure foo() language sql as $$ select 1 $$;
2471drop routine foo$0();
2472"), @r"
2473        hover: function public.foo() returns int
2474          ╭▸ 
2475        4 │ drop routine foo();
2476          ╰╴               ─ hover
2477        ");
2478    }
2479
2480    #[test]
2481    fn hover_on_drop_routine_prefers_aggregate_over_procedure() {
2482        assert_snapshot!(check_hover("
2483create aggregate foo(int) (sfunc = int4_avg_accum, stype = _int8);
2484create procedure foo(int) language sql as $$ select 1 $$;
2485drop routine foo$0(int);
2486"), @r"
2487        hover: aggregate public.foo(int)
2488          ╭▸ 
2489        4 │ drop routine foo(int);
2490          ╰╴               ─ hover
2491        ");
2492    }
2493
2494    #[test]
2495    fn hover_on_update_table() {
2496        assert_snapshot!(check_hover("
2497create table users(id int, email text);
2498update users$0 set email = 'new@example.com';
2499"), @r"
2500        hover: table public.users(id int, email text)
2501          ╭▸ 
2502        3 │ update users set email = 'new@example.com';
2503          ╰╴           ─ hover
2504        ");
2505    }
2506
2507    #[test]
2508    fn hover_on_update_table_with_schema() {
2509        assert_snapshot!(check_hover("
2510create table public.users(id int, email text);
2511update public.users$0 set email = 'new@example.com';
2512"), @r"
2513        hover: table public.users(id int, email text)
2514          ╭▸ 
2515        3 │ update public.users set email = 'new@example.com';
2516          ╰╴                  ─ hover
2517        ");
2518    }
2519
2520    #[test]
2521    fn hover_on_update_set_column() {
2522        assert_snapshot!(check_hover("
2523create table users(id int, email text);
2524update users set email$0 = 'new@example.com' where id = 1;
2525"), @r"
2526        hover: column public.users.email text
2527          ╭▸ 
2528        3 │ update users set email = 'new@example.com' where id = 1;
2529          ╰╴                     ─ hover
2530        ");
2531    }
2532
2533    #[test]
2534    fn hover_on_update_set_column_with_schema() {
2535        assert_snapshot!(check_hover("
2536create table public.users(id int, email text);
2537update public.users set email$0 = 'new@example.com' where id = 1;
2538"), @r"
2539        hover: column public.users.email text
2540          ╭▸ 
2541        3 │ update public.users set email = 'new@example.com' where id = 1;
2542          ╰╴                            ─ hover
2543        ");
2544    }
2545
2546    #[test]
2547    fn hover_on_update_where_column() {
2548        assert_snapshot!(check_hover("
2549create table users(id int, email text);
2550update users set email = 'new@example.com' where id$0 = 1;
2551"), @r"
2552        hover: column public.users.id int
2553          ╭▸ 
2554        3 │ update users set email = 'new@example.com' where id = 1;
2555          ╰╴                                                  ─ hover
2556        ");
2557    }
2558
2559    #[test]
2560    fn hover_on_update_where_column_with_schema() {
2561        assert_snapshot!(check_hover("
2562create table public.users(id int, email text);
2563update public.users set email = 'new@example.com' where id$0 = 1;
2564"), @r"
2565        hover: column public.users.id int
2566          ╭▸ 
2567        3 │ update public.users set email = 'new@example.com' where id = 1;
2568          ╰╴                                                         ─ hover
2569        ");
2570    }
2571
2572    #[test]
2573    fn hover_on_update_from_table() {
2574        assert_snapshot!(check_hover("
2575create table users(id int, email text);
2576create table messages(id int, user_id int, email text);
2577update users set email = messages.email from messages$0 where users.id = messages.user_id;
2578"), @r"
2579        hover: table public.messages(id int, user_id int, email text)
2580          ╭▸ 
2581        4 │ update users set email = messages.email from messages where users.id = messages.user_id;
2582          ╰╴                                                    ─ hover
2583        ");
2584    }
2585
2586    #[test]
2587    fn hover_on_update_from_table_with_schema() {
2588        assert_snapshot!(check_hover("
2589create table users(id int, email text);
2590create table public.messages(id int, user_id int, email text);
2591update users set email = messages.email from public.messages$0 where users.id = messages.user_id;
2592"), @r"
2593        hover: table public.messages(id int, user_id int, email text)
2594          ╭▸ 
2595        4 │ update users set email = messages.email from public.messages where users.id = messages.user_id;
2596          ╰╴                                                           ─ hover
2597        ");
2598    }
2599
2600    #[test]
2601    fn hover_on_update_with_cte_table() {
2602        assert_snapshot!(check_hover("
2603create table users(id int, email text);
2604with new_data as (
2605    select 1 as id, 'new@example.com' as email
2606)
2607update users set email = new_data.email from new_data$0 where users.id = new_data.id;
2608"), @r"
2609        hover: with new_data as (select 1 as id, 'new@example.com' as email)
2610          ╭▸ 
2611        6 │ update users set email = new_data.email from new_data where users.id = new_data.id;
2612          ╰╴                                                    ─ hover
2613        ");
2614    }
2615
2616    #[test]
2617    fn hover_on_update_with_cte_column_in_set() {
2618        assert_snapshot!(check_hover("
2619create table users(id int, email text);
2620with new_data as (
2621    select 1 as id, 'new@example.com' as email
2622)
2623update users set email = new_data.email$0 from new_data where users.id = new_data.id;
2624"), @r"
2625        hover: column new_data.email
2626          ╭▸ 
2627        6 │ update users set email = new_data.email from new_data where users.id = new_data.id;
2628          ╰╴                                      ─ hover
2629        ");
2630    }
2631
2632    #[test]
2633    fn hover_on_update_with_cte_column_in_where() {
2634        assert_snapshot!(check_hover("
2635create table users(id int, email text);
2636with new_data as (
2637    select 1 as id, 'new@example.com' as email
2638)
2639update users set email = new_data.email from new_data where new_data.id$0 = users.id;
2640"), @r"
2641        hover: column new_data.id
2642          ╭▸ 
2643        6 │ update users set email = new_data.email from new_data where new_data.id = users.id;
2644          ╰╴                                                                      ─ hover
2645        ");
2646    }
2647}