Skip to content

Commit 216770d

Browse files
authored
ide: goto def for select into & refactoring (#1086)
1 parent 0aecdc3 commit 216770d

11 files changed

Lines changed: 803 additions & 702 deletions

File tree

crates/squawk_ide/src/ast_nav.rs

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,165 @@
1-
use squawk_syntax::ast;
1+
/// [`ast_nav`] operates on ast nodes. Functions should take in and return ast nodes.
2+
///
3+
/// There shouldn't be any dependency on Salsa.
4+
use squawk_syntax::{
5+
SyntaxNode,
6+
ast::{self, AstNode},
7+
};
8+
9+
use crate::symbols::Name;
10+
11+
pub(crate) fn find_cte_with_table(
12+
name_ref: &ast::NameRef,
13+
cte_name: &Name,
14+
) -> Option<ast::WithTable> {
15+
let with_clause = name_ref
16+
.syntax()
17+
.ancestors()
18+
.find_map(|query| ast::WithQuery::cast(query)?.with_clause())?;
19+
20+
let is_recursive = with_clause.recursive_token().is_some();
21+
for with_table in with_clause.with_tables() {
22+
if let Some(name) = with_table.name()
23+
&& Name::from_node(&name) == *cte_name
24+
{
25+
// Skip if we're inside this CTE's definition (CTE doesn't shadow itself)
26+
if !is_recursive
27+
&& with_table
28+
.syntax()
29+
.text_range()
30+
.contains_range(name_ref.syntax().text_range())
31+
{
32+
continue;
33+
}
34+
return Some(with_table);
35+
}
36+
}
37+
None
38+
}
39+
40+
#[derive(Debug)]
41+
pub(crate) enum ParentQuery {
42+
Select(ast::Select),
43+
Update(ast::Update),
44+
Delete(ast::Delete),
45+
Insert(ast::Insert),
46+
Merge(ast::Merge),
47+
}
48+
49+
pub(crate) fn target_parent_query(target: ast::Target) -> Option<ParentQuery> {
50+
node_parent_query(target.syntax())
51+
}
52+
53+
pub(crate) fn node_parent_query(node: &SyntaxNode) -> Option<ParentQuery> {
54+
use ParentQuery::*;
55+
56+
for ancestor in node.ancestors() {
57+
let result = if let Some(select) = ast::Select::cast(ancestor.clone()) {
58+
Select(select)
59+
} else if let Some(update) = ast::Update::cast(ancestor.clone()) {
60+
Update(update)
61+
} else if let Some(insert) = ast::Insert::cast(ancestor.clone()) {
62+
Insert(insert)
63+
} else if let Some(delete) = ast::Delete::cast(ancestor.clone()) {
64+
Delete(delete)
65+
} else if let Some(merge) = ast::Merge::cast(ancestor) {
66+
Merge(merge)
67+
} else {
68+
continue;
69+
};
70+
71+
return Some(result);
72+
}
73+
74+
None
75+
}
76+
77+
///
78+
/// ```sql
79+
/// with t as (select 1)
80+
/// select * from t;
81+
/// -- becomes
82+
/// select 1
83+
/// ```
84+
pub(crate) fn select_from_with_query(query: ast::WithQuery) -> Option<ast::Select> {
85+
let select_variant = match query {
86+
ast::WithQuery::Select(select) => ast::SelectVariant::Select(select),
87+
ast::WithQuery::ParenSelect(paren_select) => paren_select.select()?,
88+
ast::WithQuery::CompoundSelect(compound_select) => {
89+
ast::SelectVariant::CompoundSelect(compound_select)
90+
}
91+
_ => return None,
92+
};
93+
94+
select_from_variant(select_variant)
95+
}
96+
97+
/// Extract nested select ignoring, select into, table, values
98+
///
99+
/// ```sql
100+
/// ((select 1))
101+
/// -- or
102+
/// select 1 union select 2
103+
/// -- become
104+
/// select 1
105+
/// ```
106+
pub(crate) fn select_from_variant(select_variant: ast::SelectVariant) -> Option<ast::Select> {
107+
match select_variant {
108+
ast::SelectVariant::Select(select) => return Some(select),
109+
ast::SelectVariant::CompoundSelect(compound) => {
110+
return select_from_variant(compound.lhs()?);
111+
}
112+
ast::SelectVariant::ParenSelect(paren_select) => {
113+
return select_from_variant(paren_select.select()?);
114+
}
115+
ast::SelectVariant::SelectInto(_)
116+
| ast::SelectVariant::Table(_)
117+
| ast::SelectVariant::Values(_) => {
118+
return None;
119+
}
120+
}
121+
}
122+
123+
pub(crate) enum ParentSouce {
124+
Alias(ast::Alias),
125+
CreateTable(ast::CreateTableLike),
126+
CreateTableAs(ast::CreateTableAs),
127+
CreateView(ast::CreateViewLike),
128+
ParenSelect(ast::ParenSelect),
129+
WithTable(ast::WithTable),
130+
}
131+
132+
pub(crate) fn parent_source(node: &SyntaxNode) -> Option<ParentSouce> {
133+
if let Some(paren_select) = ast::ParenSelect::cast(node.clone()) {
134+
return Some(ParentSouce::ParenSelect(paren_select));
135+
}
136+
137+
for ancestor in node.ancestors() {
138+
if let Some(alias) = ast::Alias::cast(ancestor.clone())
139+
&& alias.column_list().is_some()
140+
{
141+
return Some(ParentSouce::Alias(alias));
142+
}
143+
144+
if let Some(with_table) = ast::WithTable::cast(ancestor.clone()) {
145+
return Some(ParentSouce::WithTable(with_table));
146+
}
147+
148+
if let Some(create_view) = ast::CreateViewLike::cast(ancestor.clone()) {
149+
return Some(ParentSouce::CreateView(create_view));
150+
}
151+
152+
if let Some(create_table_as) = ast::CreateTableAs::cast(ancestor.clone()) {
153+
return Some(ParentSouce::CreateTableAs(create_table_as));
154+
}
155+
156+
if let Some(create_table) = ast::CreateTableLike::cast(ancestor.clone()) {
157+
return Some(ParentSouce::CreateTable(create_table));
158+
}
159+
}
160+
161+
None
162+
}
2163

3164
pub(crate) fn iter_from_clause(
4165
from_clause: &ast::FromClause,

0 commit comments

Comments
 (0)