Skip to content

Commit 58126e5

Browse files
authored
ide: goto def for insert and delete (#770)
1 parent 20d44cc commit 58126e5

File tree

3 files changed

+328
-4
lines changed

3 files changed

+328
-4
lines changed

crates/squawk_ide/src/goto_definition.rs

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,4 +1041,232 @@ select myschema.foo$0();
10411041
╰╴ ─ 1. source
10421042
");
10431043
}
1044+
1045+
#[test]
1046+
fn goto_insert_table() {
1047+
assert_snapshot!(goto("
1048+
create table users(id int, email text);
1049+
insert into users$0(id, email) values (1, '[email protected]');
1050+
"), @r"
1051+
╭▸
1052+
2 │ create table users(id int, email text);
1053+
│ ───── 2. destination
1054+
3 │ insert into users(id, email) values (1, '[email protected]');
1055+
╰╴ ─ 1. source
1056+
");
1057+
}
1058+
1059+
#[test]
1060+
fn goto_insert_table_with_schema() {
1061+
assert_snapshot!(goto("
1062+
create table public.users(id int, email text);
1063+
insert into public.users$0(id, email) values (1, '[email protected]');
1064+
"), @r"
1065+
╭▸
1066+
2 │ create table public.users(id int, email text);
1067+
│ ───── 2. destination
1068+
3 │ insert into public.users(id, email) values (1, '[email protected]');
1069+
╰╴ ─ 1. source
1070+
");
1071+
}
1072+
1073+
#[test]
1074+
fn goto_insert_column() {
1075+
assert_snapshot!(goto("
1076+
create table users(id int, email text);
1077+
insert into users(id$0, email) values (1, '[email protected]');
1078+
"), @r"
1079+
╭▸
1080+
2 │ create table users(id int, email text);
1081+
│ ── 2. destination
1082+
3 │ insert into users(id, email) values (1, '[email protected]');
1083+
╰╴ ─ 1. source
1084+
");
1085+
}
1086+
1087+
#[test]
1088+
fn goto_insert_column_second() {
1089+
assert_snapshot!(goto("
1090+
create table users(id int, email text);
1091+
insert into users(id, email$0) values (1, '[email protected]');
1092+
"), @r"
1093+
╭▸
1094+
2 │ create table users(id int, email text);
1095+
│ ───── 2. destination
1096+
3 │ insert into users(id, email) values (1, '[email protected]');
1097+
╰╴ ─ 1. source
1098+
");
1099+
}
1100+
1101+
#[test]
1102+
fn goto_insert_column_with_schema() {
1103+
assert_snapshot!(goto("
1104+
create table public.users(id int, email text);
1105+
insert into public.users(email$0) values ('[email protected]');
1106+
"), @r"
1107+
╭▸
1108+
2 │ create table public.users(id int, email text);
1109+
│ ───── 2. destination
1110+
3 │ insert into public.users(email) values ('[email protected]');
1111+
╰╴ ─ 1. source
1112+
");
1113+
}
1114+
1115+
#[test]
1116+
fn goto_insert_table_with_search_path() {
1117+
assert_snapshot!(goto("
1118+
set search_path to foo;
1119+
create table foo.users(id int, email text);
1120+
insert into users$0(id, email) values (1, '[email protected]');
1121+
"), @r"
1122+
╭▸
1123+
3 │ create table foo.users(id int, email text);
1124+
│ ───── 2. destination
1125+
4 │ insert into users(id, email) values (1, '[email protected]');
1126+
╰╴ ─ 1. source
1127+
");
1128+
}
1129+
1130+
#[test]
1131+
fn goto_insert_column_with_search_path() {
1132+
assert_snapshot!(goto("
1133+
set search_path to myschema;
1134+
create table myschema.users(id int, email text, name text);
1135+
insert into users(email$0, name) values ('[email protected]', 'Test');
1136+
"), @r"
1137+
╭▸
1138+
3 │ create table myschema.users(id int, email text, name text);
1139+
│ ───── 2. destination
1140+
4 │ insert into users(email, name) values ('[email protected]', 'Test');
1141+
╰╴ ─ 1. source
1142+
");
1143+
}
1144+
1145+
#[test]
1146+
fn goto_delete_table() {
1147+
assert_snapshot!(goto("
1148+
create table users(id int, email text);
1149+
delete from users$0 where id = 1;
1150+
"), @r"
1151+
╭▸
1152+
2 │ create table users(id int, email text);
1153+
│ ───── 2. destination
1154+
3 │ delete from users where id = 1;
1155+
╰╴ ─ 1. source
1156+
");
1157+
}
1158+
1159+
#[test]
1160+
fn goto_delete_table_with_schema() {
1161+
assert_snapshot!(goto("
1162+
create table public.users(id int, email text);
1163+
delete from public.users$0 where id = 1;
1164+
"), @r"
1165+
╭▸
1166+
2 │ create table public.users(id int, email text);
1167+
│ ───── 2. destination
1168+
3 │ delete from public.users where id = 1;
1169+
╰╴ ─ 1. source
1170+
");
1171+
}
1172+
1173+
#[test]
1174+
fn goto_delete_table_with_search_path() {
1175+
assert_snapshot!(goto("
1176+
set search_path to foo;
1177+
create table foo.users(id int, email text);
1178+
delete from users$0 where id = 1;
1179+
"), @r"
1180+
╭▸
1181+
3 │ create table foo.users(id int, email text);
1182+
│ ───── 2. destination
1183+
4 │ delete from users where id = 1;
1184+
╰╴ ─ 1. source
1185+
");
1186+
}
1187+
1188+
#[test]
1189+
fn goto_delete_temp_table() {
1190+
assert_snapshot!(goto("
1191+
create temp table users(id int, email text);
1192+
delete from users$0 where id = 1;
1193+
"), @r"
1194+
╭▸
1195+
2 │ create temp table users(id int, email text);
1196+
│ ───── 2. destination
1197+
3 │ delete from users where id = 1;
1198+
╰╴ ─ 1. source
1199+
");
1200+
}
1201+
1202+
#[test]
1203+
fn goto_delete_where_column() {
1204+
assert_snapshot!(goto("
1205+
create table users(id int, email text);
1206+
delete from users where id$0 = 1;
1207+
"), @r"
1208+
╭▸
1209+
2 │ create table users(id int, email text);
1210+
│ ── 2. destination
1211+
3 │ delete from users where id = 1;
1212+
╰╴ ─ 1. source
1213+
");
1214+
}
1215+
1216+
#[test]
1217+
fn goto_delete_where_column_second() {
1218+
assert_snapshot!(goto("
1219+
create table users(id int, email text);
1220+
delete from users where email$0 = '[email protected]';
1221+
"), @r"
1222+
╭▸
1223+
2 │ create table users(id int, email text);
1224+
│ ───── 2. destination
1225+
3 │ delete from users where email = '[email protected]';
1226+
╰╴ ─ 1. source
1227+
");
1228+
}
1229+
1230+
#[test]
1231+
fn goto_delete_where_column_with_schema() {
1232+
assert_snapshot!(goto("
1233+
create table public.users(id int, email text, name text);
1234+
delete from public.users where name$0 = 'Test';
1235+
"), @r"
1236+
╭▸
1237+
2 │ create table public.users(id int, email text, name text);
1238+
│ ──── 2. destination
1239+
3 │ delete from public.users where name = 'Test';
1240+
╰╴ ─ 1. source
1241+
");
1242+
}
1243+
1244+
#[test]
1245+
fn goto_delete_where_column_with_search_path() {
1246+
assert_snapshot!(goto("
1247+
set search_path to myschema;
1248+
create table myschema.users(id int, email text, active boolean);
1249+
delete from users where active$0 = true;
1250+
"), @r"
1251+
╭▸
1252+
3 │ create table myschema.users(id int, email text, active boolean);
1253+
│ ────── 2. destination
1254+
4 │ delete from users where active = true;
1255+
╰╴ ─ 1. source
1256+
");
1257+
}
1258+
1259+
#[test]
1260+
fn goto_delete_where_multiple_columns() {
1261+
assert_snapshot!(goto("
1262+
create table users(id int, email text, active boolean);
1263+
delete from users where id$0 = 1 and active = true;
1264+
"), @r"
1265+
╭▸
1266+
2 │ create table users(id int, email text, active boolean);
1267+
│ ── 2. destination
1268+
3 │ delete from users where id = 1 and active = true;
1269+
╰╴ ─ 1. source
1270+
");
1271+
}
10441272
}

crates/squawk_ide/src/resolve.rs

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use squawk_syntax::{
77
use crate::binder::Binder;
88
pub(crate) use crate::symbols::Schema;
99
use crate::symbols::{Name, SymbolKind};
10-
use squawk_syntax::SyntaxNode;
1110

1211
#[derive(Debug)]
1312
enum NameRefContext {
@@ -18,13 +17,21 @@ enum NameRefContext {
1817
CreateIndex,
1918
CreateIndexColumn,
2019
SelectFunctionCall,
20+
InsertTable,
21+
InsertColumn,
22+
DeleteTable,
23+
DeleteWhereColumn,
2124
}
2225

2326
pub(crate) fn resolve_name_ref(binder: &Binder, name_ref: &ast::NameRef) -> Option<SyntaxNodePtr> {
2427
let context = classify_name_ref_context(name_ref)?;
2528

2629
match context {
27-
NameRefContext::DropTable | NameRefContext::Table | NameRefContext::CreateIndex => {
30+
NameRefContext::DropTable
31+
| NameRefContext::Table
32+
| NameRefContext::CreateIndex
33+
| NameRefContext::InsertTable
34+
| NameRefContext::DeleteTable => {
2835
let path = find_containing_path(name_ref)?;
2936
let table_name = extract_table_name(&path)?;
3037
let schema = extract_schema_name(&path);
@@ -61,12 +68,16 @@ pub(crate) fn resolve_name_ref(binder: &Binder, name_ref: &ast::NameRef) -> Opti
6168
resolve_function(binder, &function_name, &schema, position)
6269
}
6370
NameRefContext::CreateIndexColumn => resolve_create_index_column(binder, name_ref),
71+
NameRefContext::InsertColumn => resolve_insert_column(binder, name_ref),
72+
NameRefContext::DeleteWhereColumn => resolve_delete_where_column(binder, name_ref),
6473
}
6574
}
6675

6776
fn classify_name_ref_context(name_ref: &ast::NameRef) -> Option<NameRefContext> {
6877
let mut in_partition_item = false;
6978
let mut in_call_expr = false;
79+
let mut in_column_list = false;
80+
let mut in_where_clause = false;
7081

7182
for ancestor in name_ref.syntax().ancestors() {
7283
if ast::DropTable::can_cast(ancestor.kind()) {
@@ -96,6 +107,24 @@ fn classify_name_ref_context(name_ref: &ast::NameRef) -> Option<NameRefContext>
96107
if ast::Select::can_cast(ancestor.kind()) && in_call_expr {
97108
return Some(NameRefContext::SelectFunctionCall);
98109
}
110+
if ast::ColumnList::can_cast(ancestor.kind()) {
111+
in_column_list = true;
112+
}
113+
if ast::Insert::can_cast(ancestor.kind()) {
114+
if in_column_list {
115+
return Some(NameRefContext::InsertColumn);
116+
}
117+
return Some(NameRefContext::InsertTable);
118+
}
119+
if ast::WhereClause::can_cast(ancestor.kind()) {
120+
in_where_clause = true;
121+
}
122+
if ast::Delete::can_cast(ancestor.kind()) {
123+
if in_where_clause {
124+
return Some(NameRefContext::DeleteWhereColumn);
125+
}
126+
return Some(NameRefContext::DeleteTable);
127+
}
99128
}
100129

101130
None
@@ -179,7 +208,74 @@ fn resolve_create_index_column(binder: &Binder, name_ref: &ast::NameRef) -> Opti
179208

180209
let table_ptr = resolve_table(binder, &table_name, &schema, position)?;
181210

182-
let root: &SyntaxNode = &name_ref.syntax().ancestors().last()?;
211+
let root = &name_ref.syntax().ancestors().last()?;
212+
let table_name_node = table_ptr.to_node(root);
213+
214+
let create_table = table_name_node
215+
.ancestors()
216+
.find_map(ast::CreateTable::cast)?;
217+
218+
let table_arg_list = create_table.table_arg_list()?;
219+
220+
for arg in table_arg_list.args() {
221+
if let ast::TableArg::Column(column) = arg
222+
&& let Some(col_name) = column.name()
223+
&& Name::new(col_name.syntax().text().to_string()) == column_name
224+
{
225+
return Some(SyntaxNodePtr::new(col_name.syntax()));
226+
}
227+
}
228+
229+
None
230+
}
231+
232+
fn resolve_insert_column(binder: &Binder, name_ref: &ast::NameRef) -> Option<SyntaxNodePtr> {
233+
let column_name = Name::new(name_ref.syntax().text().to_string());
234+
235+
let insert = name_ref.syntax().ancestors().find_map(ast::Insert::cast)?;
236+
let path = insert.path()?;
237+
238+
let table_name = extract_table_name(&path)?;
239+
let schema = extract_schema_name(&path);
240+
let position = name_ref.syntax().text_range().start();
241+
242+
let table_ptr = resolve_table(binder, &table_name, &schema, position)?;
243+
244+
let root = &name_ref.syntax().ancestors().last()?;
245+
let table_name_node = table_ptr.to_node(root);
246+
247+
let create_table = table_name_node
248+
.ancestors()
249+
.find_map(ast::CreateTable::cast)?;
250+
251+
let table_arg_list = create_table.table_arg_list()?;
252+
253+
for arg in table_arg_list.args() {
254+
if let ast::TableArg::Column(column) = arg
255+
&& let Some(col_name) = column.name()
256+
&& Name::new(col_name.syntax().text().to_string()) == column_name
257+
{
258+
return Some(SyntaxNodePtr::new(col_name.syntax()));
259+
}
260+
}
261+
262+
None
263+
}
264+
265+
fn resolve_delete_where_column(binder: &Binder, name_ref: &ast::NameRef) -> Option<SyntaxNodePtr> {
266+
let column_name = Name::new(name_ref.syntax().text().to_string());
267+
268+
let delete = name_ref.syntax().ancestors().find_map(ast::Delete::cast)?;
269+
let relation_name = delete.relation_name()?;
270+
let path = relation_name.path()?;
271+
272+
let table_name = extract_table_name(&path)?;
273+
let schema = extract_schema_name(&path);
274+
let position = name_ref.syntax().text_range().start();
275+
276+
let table_ptr = resolve_table(binder, &table_name, &schema, position)?;
277+
278+
let root = &name_ref.syntax().ancestors().last()?;
183279
let table_name_node = table_ptr.to_node(root);
184280

185281
let create_table = table_name_node

crates/squawk_syntax/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ fn api_walkthrough() {
259259

260260
// Besides the "typed" AST API, there's an untyped CST one as well.
261261
// To switch from AST to CST, call `.syntax()` method:
262-
let func_option_syntax: &SyntaxNode = func_option.syntax();
262+
let func_option_syntax = func_option.syntax();
263263

264264
// Note how `expr` and `bin_expr` are in fact the same node underneath:
265265
assert!(func_option_syntax == option.syntax());

0 commit comments

Comments
 (0)