Skip to content

Commit 79a3d63

Browse files
authored
ide: add document symbol support for CTEs (#806)
1 parent 835715e commit 79a3d63

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

crates/squawk_ide/src/document_symbols.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,66 @@ pub fn document_symbols(file: &ast::SourceFile) -> Vec<DocumentSymbol> {
4747
symbols.push(symbol);
4848
}
4949
}
50+
ast::Stmt::Select(select) => {
51+
symbols.extend(cte_table_symbols(select));
52+
}
53+
ast::Stmt::SelectInto(select_into) => {
54+
symbols.extend(cte_table_symbols(select_into));
55+
}
56+
ast::Stmt::Insert(insert) => {
57+
symbols.extend(cte_table_symbols(insert));
58+
}
59+
ast::Stmt::Update(update) => {
60+
symbols.extend(cte_table_symbols(update));
61+
}
62+
ast::Stmt::Delete(delete) => {
63+
symbols.extend(cte_table_symbols(delete));
64+
}
65+
5066
_ => {}
5167
}
5268
}
5369

5470
symbols
5571
}
5672

73+
fn cte_table_symbols(stmt: impl ast::HasWithClause) -> Vec<DocumentSymbol> {
74+
let Some(with_clause) = stmt.with_clause() else {
75+
return vec![];
76+
};
77+
78+
with_clause
79+
.with_tables()
80+
.filter_map(create_cte_table_symbol)
81+
.collect()
82+
}
83+
84+
fn create_cte_table_symbol(with_table: ast::WithTable) -> Option<DocumentSymbol> {
85+
let name_node = with_table.name()?;
86+
let name = name_node.syntax().text().to_string();
87+
88+
let full_range = with_table.syntax().text_range();
89+
let focus_range = name_node.syntax().text_range();
90+
91+
let mut children = vec![];
92+
if let Some(column_list) = with_table.column_list() {
93+
for column in column_list.columns() {
94+
if let Some(column_symbol) = create_column_symbol(column) {
95+
children.push(column_symbol);
96+
}
97+
}
98+
}
99+
100+
Some(DocumentSymbol {
101+
name,
102+
detail: None,
103+
kind: DocumentSymbolKind::Table,
104+
full_range,
105+
focus_range,
106+
children,
107+
})
108+
}
109+
57110
fn create_table_symbol(
58111
binder: &binder::Binder,
59112
create_table: ast::CreateTable,
@@ -559,4 +612,64 @@ create function my_schema.hello() returns void as $$ select 1; $$ language sql;
559612
fn non_create_statements() {
560613
symbols_not_found("select * from users;")
561614
}
615+
616+
#[test]
617+
fn cte_table() {
618+
assert_snapshot!(
619+
symbols("
620+
with recent_users as (
621+
select id, email as user_email
622+
from users
623+
)
624+
select * from recent_users;
625+
"),
626+
@r"
627+
info: table: recent_users
628+
╭▸
629+
2 │ with recent_users as (
630+
│ │━━━━━━━━━━━
631+
│ │
632+
│ ┌──────focus range
633+
│ │
634+
3 │ │ select id, email as user_email
635+
4 │ │ from users
636+
5 │ │ )
637+
╰╴└─┘ full range
638+
"
639+
);
640+
}
641+
642+
#[test]
643+
fn cte_table_with_column_list() {
644+
assert_snapshot!(
645+
symbols("
646+
with t(a, b, c) as (
647+
select 1, 2, 3
648+
)
649+
select * from t;
650+
"),
651+
@r"
652+
info: table: t
653+
╭▸
654+
2 │ with t(a, b, c) as (
655+
│ ━ focus range
656+
│ ┌──────┘
657+
│ │
658+
3 │ │ select 1, 2, 3
659+
4 │ │ )
660+
│ └─┘ full range
661+
662+
663+
2 │ with t(a, b, c) as (
664+
│ ┯ ┯ ┯
665+
│ │ │ │
666+
│ │ │ full range for `column: c`
667+
│ │ │ focus range
668+
│ │ full range for `column: b`
669+
│ │ focus range
670+
│ full range for `column: a`
671+
╰╴ focus range
672+
"
673+
);
674+
}
562675
}

crates/squawk_syntax/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub use self::{
5555
// HasGenericParams, HasLoopBody,
5656
HasName,
5757
HasParamList,
58+
HasWithClause,
5859
NameLike,
5960
},
6061
};

crates/squawk_syntax/src/ast/node_ext.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,16 @@ pub(crate) fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
231231

232232
impl ast::HasParamList for ast::FunctionSig {}
233233
impl ast::HasParamList for ast::Aggregate {}
234+
234235
impl ast::NameLike for ast::Name {}
235236
impl ast::NameLike for ast::NameRef {}
236237

238+
impl ast::HasWithClause for ast::Select {}
239+
impl ast::HasWithClause for ast::SelectInto {}
240+
impl ast::HasWithClause for ast::Insert {}
241+
impl ast::HasWithClause for ast::Update {}
242+
impl ast::HasWithClause for ast::Delete {}
243+
237244
#[test]
238245
fn index_expr() {
239246
let source_code = "

crates/squawk_syntax/src/ast/traits.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ pub trait HasName: AstNode {
1111

1212
pub trait NameLike: AstNode {}
1313

14+
pub trait HasWithClause: AstNode {
15+
#[inline]
16+
fn with_clause(&self) -> Option<ast::WithClause> {
17+
support::child(self.syntax())
18+
}
19+
}
20+
1421
pub trait HasArgList: AstNode {
1522
fn arg_list(&self) -> Option<ast::ArgList> {
1623
support::child(self.syntax())

0 commit comments

Comments
 (0)