Skip to content

Commit 221895d

Browse files
authored
ide: support hover for views (#804)
1 parent 1aca32b commit 221895d

File tree

2 files changed

+217
-1
lines changed

2 files changed

+217
-1
lines changed

crates/squawk_ide/src/hover.rs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,23 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
133133
if let Some(create_schema) = name.syntax().ancestors().find_map(ast::CreateSchema::cast) {
134134
return format_create_schema(&create_schema);
135135
}
136+
137+
// create view t(x) as select 1;
138+
// ^
139+
if let Some(column_list) = name.syntax().ancestors().find_map(ast::ColumnList::cast)
140+
&& let Some(create_view) = column_list
141+
.syntax()
142+
.ancestors()
143+
.find_map(ast::CreateView::cast)
144+
{
145+
return format_view_column(&create_view, Name::from_node(&name), &binder);
146+
}
147+
148+
// create view t as select 1;
149+
// ^
150+
if let Some(create_view) = name.syntax().ancestors().find_map(ast::CreateView::cast) {
151+
return format_create_view(&create_view, &binder);
152+
}
136153
}
137154

138155
None
@@ -170,6 +187,16 @@ fn hover_column(
170187
return Some(format!("column {}.{}", cte_name, column_name));
171188
}
172189

190+
// create view v(a) as select 1;
191+
// select a from v;
192+
// ^
193+
if let Some(create_view) = column_name_node.ancestors().find_map(ast::CreateView::cast)
194+
&& let Some(column_name) =
195+
ast::Name::cast(column_name_node.clone()).map(|name| Name::from_node(&name))
196+
{
197+
return format_view_column(&create_view, column_name, binder);
198+
}
199+
173200
let column = column_name_node.ancestors().find_map(ast::Column::cast)?;
174201
let column_name = column.name()?.syntax().text().to_string();
175202
let ty = column.ty()?;
@@ -233,6 +260,13 @@ fn hover_table(
233260
return format_with_table(&with_table);
234261
}
235262

263+
// create view v as select 1 a;
264+
// select a from v;
265+
// ^
266+
if let Some(create_view) = table_name_node.ancestors().find_map(ast::CreateView::cast) {
267+
return format_create_view(&create_view, binder);
268+
}
269+
236270
let create_table = table_name_node
237271
.ancestors()
238272
.find_map(ast::CreateTable::cast)?;
@@ -289,6 +323,48 @@ fn format_create_table(create_table: &ast::CreateTable, binder: &binder::Binder)
289323
Some(format!("table {}.{}{}", schema, table_name, args))
290324
}
291325

326+
fn format_create_view(create_view: &ast::CreateView, binder: &binder::Binder) -> Option<String> {
327+
let path = create_view.path()?;
328+
let segment = path.segment()?;
329+
let view_name = segment.name()?.syntax().text().to_string();
330+
331+
let schema = if let Some(qualifier) = path.qualifier() {
332+
qualifier.syntax().text().to_string()
333+
} else {
334+
view_schema(create_view, binder)?
335+
};
336+
337+
let column_list = create_view
338+
.column_list()
339+
.map(|cl| cl.syntax().text().to_string())
340+
.unwrap_or_default();
341+
342+
let query = create_view.query()?.syntax().text().to_string();
343+
344+
Some(format!(
345+
"view {}.{}{} as {}",
346+
schema, view_name, column_list, query
347+
))
348+
}
349+
350+
fn format_view_column(
351+
create_view: &ast::CreateView,
352+
column_name: Name,
353+
binder: &binder::Binder,
354+
) -> Option<String> {
355+
let path = create_view.path()?;
356+
let segment = path.segment()?;
357+
let view_name = Name::from_node(&segment.name()?);
358+
359+
let schema = if let Some(qualifier) = path.qualifier() {
360+
Name::from_string(qualifier.syntax().text().to_string())
361+
} else {
362+
Name::from_string(view_schema(create_view, binder)?)
363+
};
364+
365+
Some(format!("column {}.{}.{}", schema, view_name, column_name))
366+
}
367+
292368
fn format_with_table(with_table: &ast::WithTable) -> Option<String> {
293369
let name = with_table.name()?.syntax().text().to_string();
294370
let query = with_table.query()?.syntax().text().to_string();
@@ -306,6 +382,17 @@ fn table_schema(create_table: &ast::CreateTable, binder: &binder::Binder) -> Opt
306382
search_path.first().map(|s| s.to_string())
307383
}
308384

385+
fn view_schema(create_view: &ast::CreateView, binder: &binder::Binder) -> Option<String> {
386+
let is_temp = create_view.temp_token().is_some() || create_view.temporary_token().is_some();
387+
if is_temp {
388+
return Some("pg_temp".to_string());
389+
}
390+
391+
let position = create_view.syntax().text_range().start();
392+
let search_path = binder.search_path_at(position);
393+
search_path.first().map(|s| s.to_string())
394+
}
395+
309396
fn format_create_index(create_index: &ast::CreateIndex, binder: &binder::Binder) -> Option<String> {
310397
let index_name = create_index.name()?.syntax().text().to_string();
311398

@@ -414,6 +501,9 @@ fn is_table_ref(name_ref: &ast::NameRef) -> bool {
414501
if ast::DropTable::can_cast(ancestor.kind()) {
415502
return true;
416503
}
504+
if ast::DropView::can_cast(ancestor.kind()) {
505+
return true;
506+
}
417507
if ast::Table::can_cast(ancestor.kind()) {
418508
return true;
419509
}
@@ -2644,4 +2734,130 @@ update users set email = new_data.email from new_data where new_data.id$0 = user
26442734
╰╴ ─ hover
26452735
");
26462736
}
2737+
2738+
#[test]
2739+
fn hover_on_create_view_definition() {
2740+
assert_snapshot!(check_hover("
2741+
create view v$0 as select 1;
2742+
"), @r"
2743+
hover: view public.v as select 1
2744+
╭▸
2745+
2 │ create view v as select 1;
2746+
╰╴ ─ hover
2747+
");
2748+
}
2749+
2750+
#[test]
2751+
fn hover_on_create_view_definition_with_schema() {
2752+
assert_snapshot!(check_hover("
2753+
create view myschema.v$0 as select 1;
2754+
"), @r"
2755+
hover: view myschema.v as select 1
2756+
╭▸
2757+
2 │ create view myschema.v as select 1;
2758+
╰╴ ─ hover
2759+
");
2760+
}
2761+
2762+
#[test]
2763+
fn hover_on_create_temp_view_definition() {
2764+
assert_snapshot!(check_hover("
2765+
create temp view v$0 as select 1;
2766+
"), @r"
2767+
hover: view pg_temp.v as select 1
2768+
╭▸
2769+
2 │ create temp view v as select 1;
2770+
╰╴ ─ hover
2771+
");
2772+
}
2773+
2774+
#[test]
2775+
fn hover_on_create_view_with_column_list() {
2776+
assert_snapshot!(check_hover("
2777+
create view v(col1$0) as select 1;
2778+
"), @r"
2779+
hover: column public.v.col1
2780+
╭▸
2781+
2 │ create view v(col1) as select 1;
2782+
╰╴ ─ hover
2783+
");
2784+
}
2785+
2786+
#[test]
2787+
fn hover_on_select_from_view() {
2788+
assert_snapshot!(check_hover("
2789+
create view v as select 1;
2790+
select * from v$0;
2791+
"), @r"
2792+
hover: view public.v as select 1
2793+
╭▸
2794+
3 │ select * from v;
2795+
╰╴ ─ hover
2796+
");
2797+
}
2798+
2799+
#[test]
2800+
fn hover_on_select_column_from_view_column_list() {
2801+
assert_snapshot!(check_hover("
2802+
create view v(a) as select 1;
2803+
select a$0 from v;
2804+
"), @r"
2805+
hover: column public.v.a
2806+
╭▸
2807+
3 │ select a from v;
2808+
╰╴ ─ hover
2809+
");
2810+
}
2811+
2812+
#[test]
2813+
fn hover_on_select_column_from_view_column_list_overrides_target() {
2814+
assert_snapshot!(check_hover("
2815+
create view v(a) as select 1, 2 b;
2816+
select a, b$0 from v;
2817+
"), @r"
2818+
hover: column public.v.b
2819+
╭▸
2820+
3 │ select a, b from v;
2821+
╰╴ ─ hover
2822+
");
2823+
}
2824+
2825+
#[test]
2826+
fn hover_on_select_column_from_view_target_list() {
2827+
assert_snapshot!(check_hover("
2828+
create view v as select 1 a, 2 b;
2829+
select a$0, b from v;
2830+
"), @r"
2831+
hover: column public.v.a
2832+
╭▸
2833+
3 │ select a, b from v;
2834+
╰╴ ─ hover
2835+
");
2836+
}
2837+
2838+
#[test]
2839+
fn hover_on_select_from_view_with_schema() {
2840+
assert_snapshot!(check_hover("
2841+
create view myschema.v as select 1;
2842+
select * from myschema.v$0;
2843+
"), @r"
2844+
hover: view myschema.v as select 1
2845+
╭▸
2846+
3 │ select * from myschema.v;
2847+
╰╴ ─ hover
2848+
");
2849+
}
2850+
2851+
#[test]
2852+
fn hover_on_drop_view() {
2853+
assert_snapshot!(check_hover("
2854+
create view v as select 1;
2855+
drop view v$0;
2856+
"), @r"
2857+
hover: view public.v as select 1
2858+
╭▸
2859+
3 │ drop view v;
2860+
╰╴ ─ hover
2861+
");
2862+
}
26472863
}

crates/squawk_syntax/src/postgresql.ungram

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2568,7 +2568,7 @@ CreateView =
25682568
'create' OrReplace? ('temp' | 'temporary')? 'recursive'? 'view' Path ColumnList?
25692569
WithParams?
25702570
'as' query:SelectVariant
2571-
('with' ('cascaded' | 'local') 'check' 'option')
2571+
('with' ('cascaded' | 'local')? 'check' 'option')?
25722572

25732573
Prepare =
25742574
'prepare' Name 'as' PreparableStmt

0 commit comments

Comments
 (0)