dprint_swc_ext/view/
types.rs

1use super::generated::*;
2use crate::common::*;
3use crate::swc::ast as swc_ast;
4use crate::swc::common::comments::SingleThreadedCommentsMapInner;
5use crate::swc::parser::token::TokenAndSpan;
6
7pub enum NodeOrToken<'a> {
8  Node(Node<'a>),
9  Token(&'a TokenAndSpan),
10}
11
12impl<'a> NodeOrToken<'a> {
13  pub fn unwrap_token(&self) -> &'a TokenAndSpan {
14    match self {
15      NodeOrToken::Token(token) => token,
16      NodeOrToken::Node(node) => panic!("Expected to unwrap a token, but it was a node of kind {}.", node.kind()),
17    }
18  }
19
20  pub fn unwrap_node(&self) -> &Node<'a> {
21    match self {
22      NodeOrToken::Node(node) => node,
23      NodeOrToken::Token(token) => panic!("Expected to unwrap a node, but it was a token with text '{:?}'.", token.token),
24    }
25  }
26}
27
28impl<'a> SourceRanged for NodeOrToken<'a> {
29  fn start(&self) -> SourcePos {
30    match self {
31      NodeOrToken::Node(node) => node.start(),
32      NodeOrToken::Token(token) => token.start(),
33    }
34  }
35
36  fn end(&self) -> SourcePos {
37    match self {
38      NodeOrToken::Node(node) => node.end(),
39      NodeOrToken::Token(token) => token.end(),
40    }
41  }
42}
43
44macro_rules! implement_root_node {
45  ($name:ty) => {
46    impl<'a> RootNode<'a> for $name {
47      fn maybe_text_info(&self) -> Option<&'a SourceTextInfo> {
48        self.text_info
49      }
50
51      fn maybe_token_container(&self) -> Option<&'a TokenContainer<'a>> {
52        self.tokens
53      }
54
55      fn maybe_comment_container(&self) -> Option<&'a CommentContainer<'a>> {
56        self.comments
57      }
58    }
59  };
60}
61
62implement_root_node!(Module<'a>);
63implement_root_node!(&Module<'a>);
64implement_root_node!(Script<'a>);
65implement_root_node!(&Script<'a>);
66
67/// A Module or Script node.
68#[derive(Clone, Copy)]
69pub enum Program<'a> {
70  Module(&'a Module<'a>),
71  Script(&'a Script<'a>),
72}
73
74impl<'a> SourceRanged for Program<'a> {
75  fn start(&self) -> SourcePos {
76    match self {
77      Program::Module(node) => node.start(),
78      Program::Script(node) => node.start(),
79    }
80  }
81
82  fn end(&self) -> SourcePos {
83    match self {
84      Program::Module(node) => node.end(),
85      Program::Script(node) => node.end(),
86    }
87  }
88}
89
90impl<'a> NodeTrait<'a> for Program<'a> {
91  fn parent(&self) -> Option<Node<'a>> {
92    None
93  }
94
95  fn children(&self) -> Vec<Node<'a>> {
96    match self {
97      Program::Module(node) => node.children(),
98      Program::Script(node) => node.children(),
99    }
100  }
101
102  fn as_node(&self) -> Node<'a> {
103    match self {
104      Program::Module(node) => node.as_node(),
105      Program::Script(node) => node.as_node(),
106    }
107  }
108
109  fn kind(&self) -> NodeKind {
110    match self {
111      Program::Module(node) => node.kind(),
112      Program::Script(node) => node.kind(),
113    }
114  }
115}
116
117impl<'a> From<&Program<'a>> for Node<'a> {
118  fn from(node: &Program<'a>) -> Node<'a> {
119    match node {
120      Program::Module(node) => (*node).into(),
121      Program::Script(node) => (*node).into(),
122    }
123  }
124}
125
126impl<'a> From<Program<'a>> for Node<'a> {
127  fn from(node: Program<'a>) -> Node<'a> {
128    match node {
129      Program::Module(node) => node.into(),
130      Program::Script(node) => node.into(),
131    }
132  }
133}
134
135impl<'a> RootNode<'a> for Program<'a> {
136  fn maybe_text_info(&self) -> Option<&'a SourceTextInfo> {
137    match self {
138      Program::Module(module) => module.text_info,
139      Program::Script(script) => script.text_info,
140    }
141  }
142
143  fn maybe_token_container(&self) -> Option<&'a TokenContainer<'a>> {
144    match self {
145      Program::Module(module) => module.tokens,
146      Program::Script(script) => script.tokens,
147    }
148  }
149
150  fn maybe_comment_container(&self) -> Option<&'a CommentContainer<'a>> {
151    match self {
152      Program::Module(module) => module.comments,
153      Program::Script(script) => script.comments,
154    }
155  }
156}
157
158pub trait NodeTrait<'a>: SourceRanged + Sized {
159  fn parent(&self) -> Option<Node<'a>>;
160  fn children(&self) -> Vec<Node<'a>>;
161  fn as_node(&self) -> Node<'a>;
162  fn kind(&self) -> NodeKind;
163
164  fn ancestors(&self) -> AncestorIterator<'a> {
165    AncestorIterator::new(self.as_node())
166  }
167
168  fn start_line(&self) -> usize {
169    self.start_line_fast(self.program())
170  }
171
172  fn end_line(&self) -> usize {
173    self.end_line_fast(self.program())
174  }
175
176  fn start_column(&self) -> usize {
177    self.start_column_fast(self.program())
178  }
179
180  fn end_column(&self) -> usize {
181    self.end_column_fast(self.program())
182  }
183
184  fn char_width(&self) -> usize {
185    self.char_width_fast(self.program())
186  }
187
188  fn child_index(&self) -> usize {
189    if let Some(parent) = self.parent() {
190      let start_pos = self.start();
191      for (i, child) in parent.children().iter().enumerate() {
192        if child.start() == start_pos {
193          return i;
194        }
195      }
196      panic!("Could not find the child index for some reason.");
197    } else {
198      0
199    }
200  }
201
202  fn previous_sibling(&self) -> Option<Node<'a>> {
203    if let Some(parent) = self.parent() {
204      let child_index = self.child_index();
205      if child_index > 0 {
206        Some(parent.children().remove(child_index - 1))
207      } else {
208        None
209      }
210    } else {
211      None
212    }
213  }
214
215  /// Gets the previous siblings in the order they appear in the file.
216  fn previous_siblings(&self) -> Vec<Node<'a>> {
217    if let Some(parent) = self.parent() {
218      let child_index = self.child_index();
219      if child_index > 0 {
220        let mut parent_children = parent.children();
221        parent_children.drain(child_index..);
222        parent_children
223      } else {
224        Vec::new()
225      }
226    } else {
227      Vec::new()
228    }
229  }
230
231  /// Gets the next siblings in the order they appear in the file.
232  fn next_sibling(&self) -> Option<Node<'a>> {
233    if let Some(parent) = self.parent() {
234      let next_index = self.child_index() + 1;
235      let mut parent_children = parent.children();
236      if next_index < parent_children.len() {
237        Some(parent_children.remove(next_index))
238      } else {
239        None
240      }
241    } else {
242      None
243    }
244  }
245
246  fn next_siblings(&self) -> Vec<Node<'a>> {
247    if let Some(parent) = self.parent() {
248      let next_index = self.child_index() + 1;
249      let mut parent_children = parent.children();
250      if next_index < parent_children.len() {
251        parent_children.drain(0..next_index);
252        parent_children
253      } else {
254        Vec::new()
255      }
256    } else {
257      Vec::new()
258    }
259  }
260
261  fn tokens(&self) -> &'a [TokenAndSpan] {
262    self.tokens_fast(self.program())
263  }
264
265  fn children_with_tokens(&self) -> Vec<NodeOrToken<'a>> {
266    self.children_with_tokens_fast(self.program())
267  }
268
269  fn children_with_tokens_fast(&self, program: impl RootNode<'a>) -> Vec<NodeOrToken<'a>> {
270    let children = self.children();
271    let tokens = self.tokens_fast(program);
272    let mut result = Vec::new();
273    let mut tokens_index = 0;
274
275    for child in children {
276      let child_range = child.range();
277
278      // get the tokens before the current child
279      for token in &tokens[tokens_index..] {
280        if token.start() < child_range.start {
281          result.push(NodeOrToken::Token(token));
282          tokens_index += 1;
283        } else {
284          break;
285        }
286      }
287
288      // push current child
289      result.push(NodeOrToken::Node(child));
290
291      // skip past all the tokens within the token
292      for token in &tokens[tokens_index..] {
293        if token.end() <= child_range.end {
294          tokens_index += 1;
295        } else {
296          break;
297        }
298      }
299    }
300
301    // get the tokens after the children
302    for token in &tokens[tokens_index..] {
303      result.push(NodeOrToken::Token(token));
304    }
305
306    result
307  }
308
309  fn leading_comments(&self) -> CommentsIterator<'a> {
310    self.leading_comments_fast(self.program())
311  }
312
313  fn trailing_comments(&self) -> CommentsIterator<'a> {
314    self.trailing_comments_fast(self.program())
315  }
316
317  /// Gets the root node.
318  fn program(&self) -> Program<'a> {
319    let mut current: Node<'a> = self.as_node();
320    while let Some(parent) = current.parent() {
321      current = parent;
322    }
323
324    // the top-most node will always be a script or module
325    match current {
326      Node::Module(module) => Program::Module(module),
327      Node::Script(script) => Program::Script(script),
328      _ => panic!("Expected the root node to be a Module or Script, but it was a {}.", current.kind()),
329    }
330  }
331
332  /// Gets the root node if the view was created from a Module; otherwise panics.
333  fn module(&self) -> &Module<'a> {
334    match self.program() {
335      Program::Module(module) => module,
336      Program::Script(_) => {
337        panic!("The root node was a Script and not a Module. Use .script() or .program() instead.")
338      }
339    }
340  }
341
342  /// Gets the root node if the view was created from a Script; otherwise panics.
343  fn script(&self) -> &Script<'a> {
344    match self.program() {
345      Program::Script(script) => script,
346      Program::Module(_) => {
347        panic!("The root node was a Module and not a Script. Use .module() or .program() instead.")
348      }
349    }
350  }
351
352  fn text(&self) -> &'a str {
353    self.text_fast(self.program())
354  }
355
356  fn previous_token(&self) -> Option<&'a TokenAndSpan> {
357    self.previous_token_fast(self.program())
358  }
359
360  fn next_token(&self) -> Option<&'a TokenAndSpan> {
361    self.next_token_fast(self.program())
362  }
363
364  /// Gets the previous tokens in the order they appear in the file.
365  fn previous_tokens(&self) -> &'a [TokenAndSpan] {
366    self.previous_tokens_fast(self.program())
367  }
368
369  /// Gets the next tokens in the order they appear in the file.
370  fn next_tokens(&self) -> &'a [TokenAndSpan] {
371    self.next_tokens_fast(self.program())
372  }
373}
374
375pub trait CastableNode<'a> {
376  fn to(node: &Node<'a>) -> Option<&'a Self>;
377  fn kind() -> NodeKind;
378}
379
380#[derive(Clone, Copy)]
381pub struct Comments<'a> {
382  pub leading: &'a SingleThreadedCommentsMapInner,
383  pub trailing: &'a SingleThreadedCommentsMapInner,
384}
385
386#[derive(Clone, Copy)]
387pub enum ProgramRef<'a> {
388  Module(&'a swc_ast::Module),
389  Script(&'a swc_ast::Script),
390}
391
392impl<'a> From<&'a swc_ast::Program> for ProgramRef<'a> {
393  fn from(program: &'a swc_ast::Program) -> Self {
394    use swc_ast::Program;
395
396    match program {
397      Program::Module(module) => ProgramRef::Module(module),
398      Program::Script(script) => ProgramRef::Script(script),
399    }
400  }
401}
402
403impl<'a> From<&'a swc_ast::Module> for ProgramRef<'a> {
404  fn from(module: &'a swc_ast::Module) -> Self {
405    ProgramRef::Module(module)
406  }
407}
408
409impl<'a> From<&'a swc_ast::Script> for ProgramRef<'a> {
410  fn from(script: &'a swc_ast::Script) -> Self {
411    ProgramRef::Script(script)
412  }
413}
414
415impl<'a> SourceRanged for ProgramRef<'a> {
416  fn start(&self) -> SourcePos {
417    match self {
418      ProgramRef::Module(node) => node.range().start,
419      ProgramRef::Script(node) => node.range().start,
420    }
421  }
422
423  fn end(&self) -> SourcePos {
424    match self {
425      ProgramRef::Module(node) => node.range().end,
426      ProgramRef::Script(node) => node.range().end,
427    }
428  }
429}
430
431#[derive(Clone, Copy)]
432pub struct ProgramInfo<'a> {
433  pub program: ProgramRef<'a>,
434  pub text_info: Option<&'a SourceTextInfo>,
435  pub tokens: Option<&'a [TokenAndSpan]>,
436  pub comments: Option<Comments<'a>>,
437}
438
439#[derive(Clone, Copy)]
440pub struct ModuleInfo<'a> {
441  pub module: &'a swc_ast::Module,
442  pub text_info: Option<&'a SourceTextInfo>,
443  pub tokens: Option<&'a [TokenAndSpan]>,
444  pub comments: Option<Comments<'a>>,
445}
446
447#[derive(Clone, Copy)]
448pub struct ScriptInfo<'a> {
449  pub script: &'a swc_ast::Script,
450  pub text_info: Option<&'a SourceTextInfo>,
451  pub tokens: Option<&'a [TokenAndSpan]>,
452  pub comments: Option<Comments<'a>>,
453}
454
455#[derive(Clone)]
456pub struct AncestorIterator<'a> {
457  current: Node<'a>,
458}
459
460impl<'a> AncestorIterator<'a> {
461  pub fn new(node: Node<'a>) -> AncestorIterator<'a> {
462    AncestorIterator { current: node }
463  }
464}
465
466impl<'a> Iterator for AncestorIterator<'a> {
467  type Item = Node<'a>;
468
469  fn next(&mut self) -> Option<Node<'a>> {
470    let parent = self.current.parent();
471    if let Some(parent) = parent {
472      self.current = parent;
473    }
474    parent
475  }
476}
477
478pub trait TokenExt {
479  fn token_index<'a, N: RootNode<'a>>(&self, root_node: N) -> usize;
480}
481
482impl TokenExt for TokenAndSpan {
483  fn token_index<'a, N: RootNode<'a>>(&self, root_node: N) -> usize {
484    root_node.token_container().get_token_index_at_start(self.start()).unwrap()
485  }
486}
487
488#[cfg(test)]
489mod test {
490  use super::super::test_helpers::run_test;
491  use crate::common::*;
492  use crate::view::*;
493
494  #[test]
495  fn it_should_get_children() {
496    run_test("class Test { a: string; b: number; }", |program| {
497      let class_decl = program.children()[0].expect::<ClassDecl>();
498      let children = class_decl.class.children();
499      assert_eq!(children.len(), 2);
500      assert_eq!(children[0].text(), "a: string;");
501      assert_eq!(children[1].text(), "b: number;");
502    });
503  }
504
505  #[test]
506  fn it_should_get_all_comments() {
507    run_test(
508      r#"
509/// <reference path="foo" />
510const a = 42;
511
512/*
513 * block comment
514 */
515let b = true;
516
517// line comment
518let c = "";
519
520function foo(name: /* inline comment */ string) {
521  console.log(`hello, ${name}`); // greeting!
522}
523
524// trailing comment
525"#,
526      |program| {
527        assert_eq!(program.maybe_comment_container().unwrap().all_comments().count(), 6);
528      },
529    );
530  }
531}