Skip to content

Commit 734f69f

Browse files
authored
ide: support goto def on using clause in join (#815)
1 parent a672bab commit 734f69f

File tree

15 files changed

+542
-139
lines changed

15 files changed

+542
-139
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ quote = "1.0.40"
5757
xshell = "0.2.7"
5858
proc-macro2 = "1.0.95"
5959
snapbox = { version = "0.6.0", features = ["diff", "term-svg", "cmd"] }
60+
smallvec = "1.13.2"
6061

6162
# local
6263
# we have to make the versions explicit otherwise `cargo publish` won't work

crates/squawk_ide/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ annotate-snippets.workspace = true
2020
log.workspace = true
2121
smol_str.workspace = true
2222
la-arena.workspace = true
23+
smallvec.workspace = true
2324

2425
[dev-dependencies]
2526
insta.workspace = true

crates/squawk_ide/src/binder.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn bind_stmt(b: &mut Binder, stmt: ast::Stmt) {
9292
ast::Stmt::CreateTablespace(create_tablespace) => {
9393
bind_create_tablespace(b, create_tablespace)
9494
}
95+
ast::Stmt::CreateDatabase(create_database) => bind_create_database(b, create_database),
9596
ast::Stmt::Set(set) => bind_set(b, set),
9697
_ => {}
9798
}
@@ -384,6 +385,25 @@ fn bind_create_tablespace(b: &mut Binder, create_tablespace: ast::CreateTablespa
384385
b.scopes[root].insert(tablespace_name, tablespace_id);
385386
}
386387

388+
fn bind_create_database(b: &mut Binder, create_database: ast::CreateDatabase) {
389+
let Some(name) = create_database.name() else {
390+
return;
391+
};
392+
393+
let database_name = Name::from_node(&name);
394+
let name_ptr = SyntaxNodePtr::new(name.syntax());
395+
396+
let database_id = b.symbols.alloc(Symbol {
397+
kind: SymbolKind::Database,
398+
ptr: name_ptr,
399+
schema: Schema::new("pg_database"),
400+
params: None,
401+
});
402+
403+
let root = b.root_scope();
404+
b.scopes[root].insert(database_name, database_id);
405+
}
406+
387407
fn item_name(path: &ast::Path) -> Option<Name> {
388408
let segment = path.segment()?;
389409

crates/squawk_ide/src/classify.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub(crate) enum NameRefClass {
99
DropView,
1010
DropMaterializedView,
1111
DropSequence,
12+
SequenceOwnedByColumn,
1213
Tablespace,
14+
DropDatabase,
1315
ForeignKeyTable,
1416
ForeignKeyColumn,
1517
ForeignKeyLocalColumn,
@@ -32,6 +34,7 @@ pub(crate) enum NameRefClass {
3234
CreateSchema,
3335
CreateIndex,
3436
CreateIndexColumn,
37+
DefaultConstraintFunctionCall,
3538
SelectFunctionCall,
3639
SelectFromTable,
3740
SelectColumn,
@@ -46,6 +49,7 @@ pub(crate) enum NameRefClass {
4649
UpdateWhereColumn,
4750
UpdateSetColumn,
4851
UpdateFromTable,
52+
JoinUsingColumn,
4953
SchemaQualifier,
5054
TypeReference,
5155
}
@@ -56,6 +60,7 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
5660
let mut in_column_list = false;
5761
let mut in_where_clause = false;
5862
let mut in_from_clause = false;
63+
let mut in_on_clause = false;
5964
let mut in_set_clause = false;
6065
let mut in_constraint_exclusion_list = false;
6166
let mut in_constraint_include_clause = false;
@@ -83,11 +88,15 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
8388
.is_some();
8489

8590
let mut in_from_clause = false;
91+
let mut in_on_clause = false;
8692
for ancestor in parent.ancestors() {
93+
if ast::OnClause::can_cast(ancestor.kind()) {
94+
in_on_clause = true;
95+
}
8796
if ast::FromClause::can_cast(ancestor.kind()) {
8897
in_from_clause = true;
8998
}
90-
if ast::Select::can_cast(ancestor.kind()) && !in_from_clause {
99+
if ast::Select::can_cast(ancestor.kind()) && (!in_from_clause || in_on_clause) {
91100
if is_function_call || is_schema_table_col {
92101
return Some(NameRefClass::SchemaQualifier);
93102
} else {
@@ -119,15 +128,19 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
119128
.is_some();
120129

121130
let mut in_from_clause = false;
131+
let mut in_on_clause = false;
122132
let mut in_cast_expr = false;
123133
for ancestor in parent.ancestors() {
134+
if ast::OnClause::can_cast(ancestor.kind()) {
135+
in_on_clause = true;
136+
}
124137
if ast::CastExpr::can_cast(ancestor.kind()) {
125138
in_cast_expr = true;
126139
}
127140
if ast::FromClause::can_cast(ancestor.kind()) {
128141
in_from_clause = true;
129142
}
130-
if ast::Select::can_cast(ancestor.kind()) && !in_from_clause {
143+
if ast::Select::can_cast(ancestor.kind()) && (!in_from_clause || in_on_clause) {
131144
if in_cast_expr {
132145
return Some(NameRefClass::TypeReference);
133146
}
@@ -191,6 +204,15 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
191204
if ast::DropSequence::can_cast(ancestor.kind()) {
192205
return Some(NameRefClass::DropSequence);
193206
}
207+
if ast::DropDatabase::can_cast(ancestor.kind()) {
208+
return Some(NameRefClass::DropDatabase);
209+
}
210+
if let Some(sequence_option) = ast::SequenceOption::cast(ancestor.clone())
211+
&& sequence_option.owned_token().is_some()
212+
&& sequence_option.by_token().is_some()
213+
{
214+
return Some(NameRefClass::SequenceOwnedByColumn);
215+
}
194216
if ast::DropTablespace::can_cast(ancestor.kind())
195217
|| ast::Tablespace::can_cast(ancestor.kind())
196218
|| ast::SetTablespace::can_cast(ancestor.kind())
@@ -296,14 +318,20 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
296318
if ast::CallExpr::can_cast(ancestor.kind()) {
297319
in_call_expr = true;
298320
}
321+
if ast::DefaultConstraint::can_cast(ancestor.kind()) && in_call_expr && !in_arg_list {
322+
return Some(NameRefClass::DefaultConstraintFunctionCall);
323+
}
324+
if ast::OnClause::can_cast(ancestor.kind()) {
325+
in_on_clause = true;
326+
}
299327
if ast::FromClause::can_cast(ancestor.kind()) {
300328
in_from_clause = true;
301329
}
302330
if ast::Select::can_cast(ancestor.kind()) {
303331
if in_call_expr && !in_arg_list {
304332
return Some(NameRefClass::SelectFunctionCall);
305333
}
306-
if in_from_clause {
334+
if in_from_clause && !in_on_clause {
307335
return Some(NameRefClass::SelectFromTable);
308336
}
309337
// Classify as SelectColumn for target list, WHERE, ORDER BY, GROUP BY, etc.
@@ -331,6 +359,9 @@ pub(crate) fn classify_name_ref(name_ref: &ast::NameRef) -> Option<NameRefClass>
331359
}
332360
return Some(NameRefClass::InsertTable);
333361
}
362+
if ast::JoinUsingClause::can_cast(ancestor.kind()) && in_column_list {
363+
return Some(NameRefClass::JoinUsingColumn);
364+
}
334365
if ast::WhereClause::can_cast(ancestor.kind()) {
335366
in_where_clause = true;
336367
}
@@ -371,6 +402,7 @@ pub(crate) enum NameClass {
371402
CreateIndex(ast::CreateIndex),
372403
CreateSequence(ast::CreateSequence),
373404
CreateTablespace(ast::CreateTablespace),
405+
CreateDatabase(ast::CreateDatabase),
374406
CreateType(ast::CreateType),
375407
CreateFunction(ast::CreateFunction),
376408
CreateAggregate(ast::CreateAggregate),
@@ -411,6 +443,9 @@ pub(crate) fn classify_name(name: &ast::Name) -> Option<NameClass> {
411443
if let Some(create_tablespace) = ast::CreateTablespace::cast(ancestor.clone()) {
412444
return Some(NameClass::CreateTablespace(create_tablespace));
413445
}
446+
if let Some(create_database) = ast::CreateDatabase::cast(ancestor.clone()) {
447+
return Some(NameClass::CreateDatabase(create_database));
448+
}
414449
if let Some(create_type) = ast::CreateType::cast(ancestor.clone()) {
415450
return Some(NameClass::CreateType(create_type));
416451
}

crates/squawk_ide/src/find_references.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ pub fn find_references(file: &ast::SourceFile, offset: TextSize) -> Vec<TextRang
2020
match_ast! {
2121
match node {
2222
ast::NameRef(name_ref) => {
23-
if let Some(found) = resolve::resolve_name_ref(&binder, &name_ref)
24-
&& found == target
23+
if let Some(found_refs) = resolve::resolve_name_ref(&binder, &name_ref)
24+
&& found_refs.contains(&target)
2525
{
2626
refs.push(name_ref.syntax().text_range());
2727
}
@@ -49,10 +49,12 @@ fn find_target(file: &ast::SourceFile, offset: TextSize, binder: &Binder) -> Opt
4949
return Some(SyntaxNodePtr::new(name.syntax()));
5050
}
5151

52-
if let Some(name_ref) = ast::NameRef::cast(parent.clone())
53-
&& let Some(ptr) = resolve::resolve_name_ref(binder, &name_ref)
54-
{
55-
return Some(ptr);
52+
if let Some(name_ref) = ast::NameRef::cast(parent.clone()) {
53+
// TODO: I think we want to return a list of targets so we can support cases like:
54+
// select * from t join u using (id);
55+
// ^ find refs
56+
return resolve::resolve_name_ref(binder, &name_ref)
57+
.and_then(|ptrs| ptrs.into_iter().next());
5658
}
5759

5860
None

0 commit comments

Comments
 (0)