squawk_syntax/ast/
node_ext.rs

1// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast/node_ext.rs
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27use std::borrow::Cow;
28
29use rowan::{GreenNodeData, GreenTokenData, NodeOrToken};
30
31use crate::ast;
32use crate::ast::AstNode;
33use crate::{SyntaxNode, TokenText};
34
35use super::support;
36
37impl ast::Constraint {
38    #[inline]
39    pub fn name(&self) -> Option<ast::Name> {
40        support::child(self.syntax())
41    }
42}
43
44impl ast::BinExpr {
45    #[inline]
46    pub fn lhs(&self) -> Option<ast::Expr> {
47        support::children(self.syntax()).next()
48    }
49
50    #[inline]
51    pub fn rhs(&self) -> Option<ast::Expr> {
52        support::children(self.syntax()).nth(1)
53    }
54}
55
56impl ast::FieldExpr {
57    // We have NameRef as a variant of Expr which complicates things (and it
58    // might not be worth it).
59    // Rust analyzer doesn't do this so it doesn't have to special case this.
60    #[inline]
61    pub fn base(&self) -> Option<ast::Expr> {
62        support::children(self.syntax()).next()
63    }
64    #[inline]
65    pub fn field(&self) -> Option<ast::NameRef> {
66        support::children(self.syntax()).last()
67    }
68}
69
70impl ast::IndexExpr {
71    #[inline]
72    pub fn base(&self) -> Option<ast::Expr> {
73        support::children(&self.syntax).next()
74    }
75    #[inline]
76    pub fn index(&self) -> Option<ast::Expr> {
77        support::children(&self.syntax).nth(1)
78    }
79}
80
81impl ast::SliceExpr {
82    #[inline]
83    pub fn base(&self) -> Option<ast::Expr> {
84        support::children(&self.syntax).next()
85    }
86
87    #[inline]
88    pub fn start(&self) -> Option<ast::Expr> {
89        // With `select x[1:]`, we have two exprs, `x` and `1`.
90        // We skip over the first one, and then we want the second one, but we
91        // want to make sure we don't choose the end expr if instead we had:
92        // `select x[:1]`
93        let colon = self.colon_token()?;
94        support::children(&self.syntax)
95            .skip(1)
96            .find(|expr: &ast::Expr| expr.syntax().text_range().end() <= colon.text_range().start())
97    }
98
99    #[inline]
100    pub fn end(&self) -> Option<ast::Expr> {
101        // We want to make sure we get the last expr after the `:` which is the
102        // end of the slice, i.e., `2` in: `select x[:2]`
103        let colon = self.colon_token()?;
104        support::children(&self.syntax)
105            .find(|expr: &ast::Expr| expr.syntax().text_range().start() >= colon.text_range().end())
106    }
107}
108
109impl ast::RenameColumn {
110    #[inline]
111    pub fn from(&self) -> Option<ast::NameRef> {
112        support::children(&self.syntax).nth(0)
113    }
114    #[inline]
115    pub fn to(&self) -> Option<ast::NameRef> {
116        support::children(&self.syntax).nth(1)
117    }
118}
119
120impl ast::ForeignKeyConstraint {
121    #[inline]
122    pub fn from_columns(&self) -> Option<ast::ColumnList> {
123        support::children(&self.syntax).nth(0)
124    }
125    #[inline]
126    pub fn to_columns(&self) -> Option<ast::ColumnList> {
127        support::children(&self.syntax).nth(1)
128    }
129}
130
131impl ast::BetweenExpr {
132    #[inline]
133    pub fn target(&self) -> Option<ast::Expr> {
134        support::children(&self.syntax).nth(0)
135    }
136    #[inline]
137    pub fn start(&self) -> Option<ast::Expr> {
138        support::children(&self.syntax).nth(1)
139    }
140    #[inline]
141    pub fn end(&self) -> Option<ast::Expr> {
142        support::children(&self.syntax).nth(2)
143    }
144}
145
146impl ast::WhenClause {
147    #[inline]
148    pub fn condition(&self) -> Option<ast::Expr> {
149        support::children(&self.syntax).next()
150    }
151    #[inline]
152    pub fn then(&self) -> Option<ast::Expr> {
153        support::children(&self.syntax).nth(1)
154    }
155}
156
157impl ast::CompoundSelect {
158    #[inline]
159    pub fn lhs(&self) -> Option<ast::SelectVariant> {
160        support::children(&self.syntax).next()
161    }
162    #[inline]
163    pub fn rhs(&self) -> Option<ast::SelectVariant> {
164        support::children(&self.syntax).nth(1)
165    }
166}
167
168impl ast::NameRef {
169    #[inline]
170    pub fn text(&self) -> TokenText<'_> {
171        text_of_first_token(self.syntax())
172    }
173}
174
175impl ast::Name {
176    #[inline]
177    pub fn text(&self) -> TokenText<'_> {
178        text_of_first_token(self.syntax())
179    }
180}
181
182impl ast::CharType {
183    #[inline]
184    pub fn text(&self) -> TokenText<'_> {
185        text_of_first_token(self.syntax())
186    }
187}
188
189pub(crate) fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
190    fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
191        green_ref
192            .children()
193            .next()
194            .and_then(NodeOrToken::into_token)
195            .unwrap()
196    }
197
198    match node.green() {
199        Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
200        Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
201    }
202}