Skip to content

Commit 706257c

Browse files
authored
ide: basic goto def for create table/drop table (#736)
just the start
1 parent 4bbc7a7 commit 706257c

File tree

10 files changed

+385
-6
lines changed

10 files changed

+385
-6
lines changed

Cargo.lock

Lines changed: 8 additions & 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
@@ -23,6 +23,7 @@ jsonwebtoken = "9.3.1"
2323
lazy_static = "1.5.0"
2424
log = "0.4.25"
2525
reqwest = { version = "0.11.27", features = ["native-tls-vendored", "blocking", "json"] }
26+
la-arena = "0.3.1"
2627
serde = { version = "1.0.215", features = ["derive"] }
2728
serde_json = "1.0"
2829
serde_plain = "1.0"

PLAN.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,26 @@ FROM (
581581
WHERE total_amount > 1000;
582582
```
583583

584+
### Rule: aggregate free having condition
585+
586+
```sql
587+
select a from t group by a having a > 10;
588+
-- ^^^^^^
589+
590+
-- quick fix to:
591+
select a from t where a > 10 group by a;
592+
```
593+
594+
### Rule: order direction is redundent
595+
596+
```sql
597+
select * from t order by a asc;
598+
-- ^^^ order direction is redundent. asc is the default.
599+
600+
-- quick fix to:
601+
select * from t order by a;
602+
```
603+
584604
### Rule: sum(boolean) to case stmt
585605

586606
```sql
@@ -1076,18 +1096,19 @@ also show lex command
10761096
### Snippets
10771097
10781098
- [datagrip live templates](https://blog.jetbrains.com/datagrip/2019/03/11/top-9-sql-features-of-datagrip-you-have-to-know/#live_templates)
1099+
- insert
1100+
- select
1101+
- create table
10791102
- [postgresql-snippets](https://github.com/Manuel7806/postgresql-snippets/blob/main/snippets/snippets.code-snippets)
10801103
10811104
### Quick Fix: alias query
10821105
10831106
```sql
10841107
select * from bar
10851108
-- ^$ action:rename-alias
1086-
```
10871109

1088-
becomes after filling in alias name with `b`
1110+
-- becomes after filling in alias name with `b`
10891111

1090-
```sql
10911112
select b.* from bar b
10921113
```
10931114
@@ -1096,11 +1117,9 @@ another example:
10961117
```sql
10971118
select name, email from bar
10981119
-- ^$ action:rename-alias
1099-
```
11001120

1101-
becomes after filling in alias name with `b`
1121+
-- becomes after filling in alias name with `b`
11021122

1103-
```sql
11041123
select b.name, b.email from bar
11051124
```
11061125

crates/squawk_ide/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ rowan.workspace = true
1818
line-index.workspace = true
1919
annotate-snippets.workspace = true
2020
log.workspace = true
21+
smol_str.workspace = true
22+
la-arena.workspace = true
2123

2224
[dev-dependencies]
2325
insta.workspace = true

crates/squawk_ide/src/binder.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/// Loosely based on TypeScript's binder
2+
/// see: typescript-go/internal/binder/binder.go
3+
use la_arena::Arena;
4+
use squawk_syntax::{SyntaxNodePtr, ast, ast::AstNode};
5+
6+
use crate::scope::{Scope, ScopeId};
7+
use crate::symbols::{Name, Schema, Symbol, SymbolKind};
8+
9+
pub(crate) struct Binder {
10+
pub(crate) scopes: Arena<Scope>,
11+
pub(crate) symbols: Arena<Symbol>,
12+
}
13+
14+
impl Binder {
15+
fn new() -> Self {
16+
let mut scopes = Arena::new();
17+
let _root_scope = scopes.alloc(Scope::with_parent(None));
18+
Binder {
19+
scopes,
20+
symbols: Arena::new(),
21+
}
22+
}
23+
24+
pub(crate) fn root_scope(&self) -> ScopeId {
25+
self.scopes
26+
.iter()
27+
.next()
28+
.map(|(id, _)| id)
29+
.expect("root scope must exist")
30+
}
31+
}
32+
33+
pub(crate) fn bind(file: &ast::SourceFile) -> Binder {
34+
let mut binder = Binder::new();
35+
36+
bind_file(&mut binder, file);
37+
38+
binder
39+
}
40+
41+
fn bind_file(b: &mut Binder, file: &ast::SourceFile) {
42+
for stmt in file.stmts() {
43+
bind_stmt(b, stmt);
44+
}
45+
}
46+
47+
fn bind_stmt(b: &mut Binder, stmt: ast::Stmt) {
48+
if let ast::Stmt::CreateTable(create_table) = stmt {
49+
bind_create_table(b, create_table)
50+
}
51+
}
52+
53+
fn bind_create_table(b: &mut Binder, create_table: ast::CreateTable) {
54+
let Some(path) = create_table.path() else {
55+
return;
56+
};
57+
let Some(table_name) = item_name(&path) else {
58+
return;
59+
};
60+
let name_ptr = path_to_ptr(&path);
61+
let schema = schema_name(&path);
62+
63+
let table_id = b.symbols.alloc(Symbol {
64+
kind: SymbolKind::Table,
65+
ptr: name_ptr,
66+
schema,
67+
});
68+
69+
let root = b.root_scope();
70+
b.scopes[root].insert(table_name, table_id);
71+
}
72+
73+
fn item_name(path: &ast::Path) -> Option<Name> {
74+
let segment = path.segment()?;
75+
76+
if let Some(name) = segment.name() {
77+
return Some(Name::new(name.syntax().text().to_string()));
78+
}
79+
if let Some(name) = segment.name_ref() {
80+
return Some(Name::new(name.syntax().text().to_string()));
81+
}
82+
83+
None
84+
}
85+
86+
fn path_to_ptr(path: &ast::Path) -> SyntaxNodePtr {
87+
if let Some(segment) = path.segment() {
88+
if let Some(name) = segment.name() {
89+
return SyntaxNodePtr::new(name.syntax());
90+
}
91+
if let Some(name_ref) = segment.name_ref() {
92+
return SyntaxNodePtr::new(name_ref.syntax());
93+
}
94+
}
95+
SyntaxNodePtr::new(path.syntax())
96+
}
97+
98+
fn schema_name(path: &ast::Path) -> Schema {
99+
let Some(qualifier) = path.qualifier() else {
100+
return Schema::Public;
101+
};
102+
let Some(segment) = qualifier.segment() else {
103+
return Schema::Public;
104+
};
105+
106+
let schema_name = if let Some(name) = segment.name() {
107+
Name::new(name.syntax().text().to_string())
108+
} else if let Some(name_ref) = segment.name_ref() {
109+
Name::new(name_ref.syntax().text().to_string())
110+
} else {
111+
return Schema::Public;
112+
};
113+
114+
Schema::from_name(schema_name)
115+
}

crates/squawk_ide/src/goto_definition.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use crate::binder;
12
use crate::offsets::token_from_offset;
3+
use crate::resolve;
24
use rowan::{TextRange, TextSize};
35
use squawk_syntax::{
46
SyntaxKind,
@@ -45,6 +47,14 @@ pub fn goto_definition(file: ast::SourceFile, offset: TextSize) -> Option<TextRa
4547
}
4648
}
4749

50+
if let Some(name_ref) = ast::NameRef::cast(parent.clone()) {
51+
let binder_output = binder::bind(&file);
52+
if let Some(ptr) = resolve::resolve_name_ref(&binder_output, &name_ref) {
53+
let node = ptr.to_node(file.syntax());
54+
return Some(node.text_range());
55+
}
56+
}
57+
4858
return None;
4959
}
5060

@@ -204,6 +214,69 @@ rollback$0;
204214
");
205215
}
206216

217+
#[test]
218+
fn goto_drop_table() {
219+
assert_snapshot!(goto("
220+
create table t();
221+
drop table t$0;
222+
"), @r"
223+
╭▸
224+
2 │ create table t();
225+
│ ─ 2. destination
226+
3 │ drop table t;
227+
╰╴ ─ 1. source
228+
");
229+
}
230+
231+
#[test]
232+
fn goto_drop_table_with_schema() {
233+
assert_snapshot!(goto("
234+
create table public.t();
235+
drop table t$0;
236+
"), @r"
237+
╭▸
238+
2 │ create table public.t();
239+
│ ─ 2. destination
240+
3 │ drop table t;
241+
╰╴ ─ 1. source
242+
");
243+
244+
assert_snapshot!(goto("
245+
create table foo.t();
246+
drop table foo.t$0;
247+
"), @r"
248+
╭▸
249+
2 │ create table foo.t();
250+
│ ─ 2. destination
251+
3 │ drop table foo.t;
252+
╰╴ ─ 1. source
253+
");
254+
255+
goto_not_found(
256+
"
257+
-- defaults to public schema
258+
create table t();
259+
drop table foo.t$0;
260+
",
261+
);
262+
263+
// todo: temp tables
264+
}
265+
266+
#[test]
267+
fn goto_drop_table_defined_after() {
268+
assert_snapshot!(goto("
269+
drop table t$0;
270+
create table t();
271+
"), @r"
272+
╭▸
273+
2 │ drop table t;
274+
│ ─ 1. source
275+
3 │ create table t();
276+
╰╴ ─ 2. destination
277+
");
278+
}
279+
207280
#[test]
208281
fn begin_to_rollback() {
209282
assert_snapshot!(goto(

crates/squawk_ide/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
mod binder;
12
pub mod code_actions;
23
pub mod column_name;
34
pub mod expand_selection;
45
mod generated;
56
pub mod goto_definition;
67
mod offsets;
8+
mod resolve;
9+
mod scope;
10+
mod symbols;
711
#[cfg(test)]
812
pub mod test_utils;

0 commit comments

Comments
 (0)