Skip to content

Commit c4c95bc

Browse files
authored
ide: add hover for create table (#764)
also support hover on create table and column defs instead of just refs
1 parent 67b9150 commit c4c95bc

File tree

1 file changed

+329
-15
lines changed

1 file changed

+329
-15
lines changed

crates/squawk_ide/src/hover.rs

Lines changed: 329 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,38 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
88
let token = token_from_offset(file, offset)?;
99
let parent = token.parent()?;
1010

11-
let name_ref = ast::NameRef::cast(parent)?;
11+
let binder = binder::bind(file);
12+
13+
if let Some(name_ref) = ast::NameRef::cast(parent.clone()) {
14+
if is_column_ref(&name_ref) {
15+
return hover_column(file, &name_ref, &binder);
16+
}
1217

13-
if !is_column_ref(&name_ref) {
14-
return None;
18+
if is_table_ref(&name_ref) {
19+
return hover_table(file, &name_ref, &binder);
20+
}
1521
}
1622

23+
if let Some(name) = ast::Name::cast(parent) {
24+
if let Some(column) = name.syntax().parent().and_then(ast::Column::cast)
25+
&& let Some(create_table) = column.syntax().ancestors().find_map(ast::CreateTable::cast)
26+
{
27+
return hover_column_definition(&create_table, &column, &binder);
28+
}
29+
30+
if let Some(create_table) = name.syntax().ancestors().find_map(ast::CreateTable::cast) {
31+
return format_create_table(&create_table, &binder);
32+
}
33+
}
34+
35+
None
36+
}
37+
38+
fn hover_column(
39+
file: &ast::SourceFile,
40+
name_ref: &ast::NameRef,
41+
binder: &binder::Binder,
42+
) -> Option<String> {
1743
let column_name = name_ref.syntax().text().to_string();
1844

1945
let create_index = name_ref
@@ -24,11 +50,9 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
2450
let relation_name = create_index.relation_name()?;
2551
let path = relation_name.path()?;
2652

27-
let binder = binder::bind(file);
28-
29-
let (schema, table_name) = resolve::resolve_table_info(&binder, &path)?;
53+
let (schema, table_name) = resolve::resolve_table_info(binder, &path)?;
3054

31-
let column_ptr = resolve::resolve_name_ref(&binder, &name_ref)?;
55+
let column_ptr = resolve::resolve_name_ref(binder, name_ref)?;
3256

3357
let root = file.syntax();
3458
let column_name_node = column_ptr.to_node(root);
@@ -43,14 +67,128 @@ pub fn hover(file: &ast::SourceFile, offset: TextSize) -> Option<String> {
4367
))
4468
}
4569

70+
fn hover_column_definition(
71+
create_table: &ast::CreateTable,
72+
column: &ast::Column,
73+
binder: &binder::Binder,
74+
) -> Option<String> {
75+
let column_name = column.name()?.syntax().text().to_string();
76+
let ty = column.ty()?;
77+
let path = create_table.path()?;
78+
let table_name = path.segment()?.name()?.syntax().text().to_string();
79+
80+
let schema = if let Some(qualifier) = path.qualifier() {
81+
qualifier.syntax().text().to_string()
82+
} else if let Some(schema) = table_schema(create_table, binder) {
83+
schema
84+
} else {
85+
return Some(format!(
86+
"{}.{} {}",
87+
table_name,
88+
column_name,
89+
ty.syntax().text()
90+
));
91+
};
92+
93+
Some(format!(
94+
"{}.{}.{} {}",
95+
schema,
96+
table_name,
97+
column_name,
98+
ty.syntax().text()
99+
))
100+
}
101+
102+
fn hover_table(
103+
file: &ast::SourceFile,
104+
name_ref: &ast::NameRef,
105+
binder: &binder::Binder,
106+
) -> Option<String> {
107+
let table_ptr = resolve::resolve_name_ref(binder, name_ref)?;
108+
109+
let root = file.syntax();
110+
let table_name_node = table_ptr.to_node(root);
111+
112+
let create_table = table_name_node
113+
.ancestors()
114+
.find_map(ast::CreateTable::cast)?;
115+
116+
format_create_table(&create_table, binder)
117+
}
118+
119+
// Insert inferred schema into the create table hover info
120+
fn format_create_table(create_table: &ast::CreateTable, binder: &binder::Binder) -> Option<String> {
121+
let path = create_table.path()?;
122+
let mut text = create_table.syntax().text().to_string();
123+
124+
if path.qualifier().is_some() {
125+
return Some(text);
126+
}
127+
128+
let Some(schema) = table_schema(create_table, binder) else {
129+
return Some(text);
130+
};
131+
132+
let Some(offset) = table_name_offset(create_table, &path) else {
133+
return Some(text);
134+
};
135+
136+
text.insert_str(offset, &format!("{}.", schema));
137+
Some(text)
138+
}
139+
140+
fn table_schema(create_table: &ast::CreateTable, binder: &binder::Binder) -> Option<String> {
141+
let is_temp = create_table.temp_token().is_some() || create_table.temporary_token().is_some();
142+
if is_temp {
143+
return Some("pg_temp".to_string());
144+
}
145+
146+
let position = create_table.syntax().text_range().start();
147+
let search_path = binder.search_path_at(position);
148+
search_path.first().map(|s| s.to_string())
149+
}
150+
151+
fn table_name_offset(create_table: &ast::CreateTable, path: &ast::Path) -> Option<usize> {
152+
let segment = path.segment()?;
153+
let name = segment.name()?;
154+
let name_start = name.syntax().text_range().start();
155+
let create_table_start = create_table.syntax().text_range().start();
156+
Some((name_start - create_table_start).into())
157+
}
158+
46159
fn is_column_ref(name_ref: &ast::NameRef) -> bool {
160+
let mut in_partition_item = false;
161+
47162
for ancestor in name_ref.syntax().ancestors() {
48163
if ast::PartitionItem::can_cast(ancestor.kind()) {
49-
return true;
164+
in_partition_item = true;
50165
}
51166
if ast::CreateIndex::can_cast(ancestor.kind()) {
167+
return in_partition_item;
168+
}
169+
}
170+
false
171+
}
172+
173+
fn is_table_ref(name_ref: &ast::NameRef) -> bool {
174+
let mut in_partition_item = false;
175+
176+
for ancestor in name_ref.syntax().ancestors() {
177+
if ast::DropTable::can_cast(ancestor.kind()) {
178+
return true;
179+
}
180+
if ast::Table::can_cast(ancestor.kind()) {
52181
return true;
53182
}
183+
if ast::DropIndex::can_cast(ancestor.kind()) {
184+
return false;
185+
}
186+
if ast::PartitionItem::can_cast(ancestor.kind()) {
187+
in_partition_item = true;
188+
}
189+
if ast::CreateIndex::can_cast(ancestor.kind()) {
190+
return !in_partition_item;
191+
}
54192
}
55193
false
56194
}
@@ -239,13 +377,16 @@ create index idx_email on public.users(email$0);
239377
}
240378

241379
#[test]
242-
fn hover_not_on_table_name() {
243-
hover_not_found(
244-
"
245-
create table users(id int);
246-
create index idx on users$0(id);
247-
",
248-
);
380+
fn hover_on_table_name() {
381+
assert_snapshot!(check_hover("
382+
create table t(id int);
383+
create index idx on t$0(id);
384+
"), @r"
385+
hover: create table public.t(id int)
386+
╭▸
387+
3 │ create index idx on t(id);
388+
╰╴ ─ hover
389+
");
249390
}
250391

251392
#[test]
@@ -257,4 +398,177 @@ create index idx$0 on users(id);
257398
",
258399
);
259400
}
401+
402+
#[test]
403+
fn hover_table_in_create_index() {
404+
assert_snapshot!(check_hover("
405+
create table users(id int, email text);
406+
create index idx_email on users$0(email);
407+
"), @r"
408+
hover: create table public.users(id int, email text)
409+
╭▸
410+
3 │ create index idx_email on users(email);
411+
╰╴ ─ hover
412+
");
413+
}
414+
415+
#[test]
416+
fn hover_table_with_schema() {
417+
assert_snapshot!(check_hover("
418+
create table public.users(id int, email text);
419+
create index idx on public.users$0(id);
420+
"), @r"
421+
hover: create table public.users(id int, email text)
422+
╭▸
423+
3 │ create index idx on public.users(id);
424+
╰╴ ─ hover
425+
");
426+
}
427+
428+
#[test]
429+
fn hover_table_temp() {
430+
assert_snapshot!(check_hover("
431+
create temp table users(id int, email text);
432+
create index idx on users$0(id);
433+
"), @r"
434+
hover: create temp table pg_temp.users(id int, email text)
435+
╭▸
436+
3 │ create index idx on users(id);
437+
╰╴ ─ hover
438+
");
439+
}
440+
441+
#[test]
442+
fn hover_table_multiline() {
443+
assert_snapshot!(check_hover("
444+
create table users(
445+
id int,
446+
email text,
447+
name varchar(100)
448+
);
449+
create index idx on users$0(id);
450+
"), @r"
451+
hover: create table public.users(
452+
id int,
453+
email text,
454+
name varchar(100)
455+
)
456+
╭▸
457+
7 │ create index idx on users(id);
458+
╰╴ ─ hover
459+
");
460+
}
461+
462+
#[test]
463+
fn hover_table_with_search_path() {
464+
assert_snapshot!(check_hover(r#"
465+
set search_path to myschema;
466+
create table users(id int, email text);
467+
create index idx on users$0(id);
468+
"#), @r"
469+
hover: create table myschema.users(id int, email text)
470+
╭▸
471+
4 │ create index idx on users(id);
472+
╰╴ ─ hover
473+
");
474+
}
475+
476+
#[test]
477+
fn hover_table_search_path_at_definition() {
478+
assert_snapshot!(check_hover(r#"
479+
set search_path to myschema;
480+
create table users(id int, email text);
481+
set search_path to myschema, otherschema;
482+
create index idx on users$0(id);
483+
"#), @r"
484+
hover: create table myschema.users(id int, email text)
485+
╭▸
486+
5 │ create index idx on users(id);
487+
╰╴ ─ hover
488+
");
489+
}
490+
491+
#[test]
492+
fn hover_on_create_table_definition() {
493+
assert_snapshot!(check_hover("
494+
create table t$0(x bigint);
495+
"), @r"
496+
hover: create table public.t(x bigint)
497+
╭▸
498+
2 │ create table t(x bigint);
499+
╰╴ ─ hover
500+
");
501+
}
502+
503+
#[test]
504+
fn hover_on_create_table_definition_with_schema() {
505+
assert_snapshot!(check_hover("
506+
create table myschema.users$0(id int);
507+
"), @r"
508+
hover: create table myschema.users(id int)
509+
╭▸
510+
2 │ create table myschema.users(id int);
511+
╰╴ ─ hover
512+
");
513+
}
514+
515+
#[test]
516+
fn hover_on_create_temp_table_definition() {
517+
assert_snapshot!(check_hover("
518+
create temp table t$0(x bigint);
519+
"), @r"
520+
hover: create temp table pg_temp.t(x bigint)
521+
╭▸
522+
2 │ create temp table t(x bigint);
523+
╰╴ ─ hover
524+
");
525+
}
526+
527+
#[test]
528+
fn hover_on_column_in_create_table() {
529+
assert_snapshot!(check_hover("
530+
create table t(id$0 int);
531+
"), @r"
532+
hover: public.t.id int
533+
╭▸
534+
2 │ create table t(id int);
535+
╰╴ ─ hover
536+
");
537+
}
538+
539+
#[test]
540+
fn hover_on_column_in_create_table_with_schema() {
541+
assert_snapshot!(check_hover("
542+
create table myschema.users(id$0 int, name text);
543+
"), @r"
544+
hover: myschema.users.id int
545+
╭▸
546+
2 │ create table myschema.users(id int, name text);
547+
╰╴ ─ hover
548+
");
549+
}
550+
551+
#[test]
552+
fn hover_on_column_in_temp_table() {
553+
assert_snapshot!(check_hover("
554+
create temp table t(x$0 bigint);
555+
"), @r"
556+
hover: pg_temp.t.x bigint
557+
╭▸
558+
2 │ create temp table t(x bigint);
559+
╰╴ ─ hover
560+
");
561+
}
562+
563+
#[test]
564+
fn hover_on_multiple_columns() {
565+
assert_snapshot!(check_hover("
566+
create table t(id int, email$0 text, name varchar(100));
567+
"), @r"
568+
hover: public.t.email text
569+
╭▸
570+
2 │ create table t(id int, email text, name varchar(100));
571+
╰╴ ─ hover
572+
");
573+
}
260574
}

0 commit comments

Comments
 (0)