php_parser/ast/
locator.rs

1use crate::ast::visitor::{Visitor, walk_class_member, walk_expr, walk_stmt};
2use crate::ast::*;
3use crate::span::Span;
4
5#[derive(Debug, Clone, Copy)]
6pub enum AstNode<'ast> {
7    Stmt(StmtId<'ast>),
8    Expr(ExprId<'ast>),
9    ClassMember(&'ast ClassMember<'ast>),
10}
11
12impl<'ast> AstNode<'ast> {
13    pub fn span(&self) -> Span {
14        match self {
15            AstNode::Stmt(s) => s.span(),
16            AstNode::Expr(e) => e.span(),
17            AstNode::ClassMember(m) => match m {
18                ClassMember::Property { span, .. } => *span,
19                ClassMember::PropertyHook { span, .. } => *span,
20                ClassMember::Method { span, .. } => *span,
21                ClassMember::Const { span, .. } => *span,
22                ClassMember::TraitUse { span, .. } => *span,
23                ClassMember::Case { span, .. } => *span,
24            },
25        }
26    }
27}
28
29pub struct Locator<'ast> {
30    target: usize,
31    pub path: Vec<AstNode<'ast>>,
32}
33
34impl<'ast> Locator<'ast> {
35    pub fn new(target: usize) -> Self {
36        Self {
37            target,
38            path: Vec::new(),
39        }
40    }
41
42    pub fn find(program: &'ast Program<'ast>, target: usize) -> Vec<AstNode<'ast>> {
43        let mut locator = Self::new(target);
44        locator.visit_program(program);
45        locator.path
46    }
47}
48
49impl<'ast> Visitor<'ast> for Locator<'ast> {
50    fn visit_stmt(&mut self, stmt: StmtId<'ast>) {
51        let span = stmt.span();
52        if span.start <= self.target && self.target <= span.end {
53            self.path.push(AstNode::Stmt(stmt));
54            walk_stmt(self, stmt);
55        }
56    }
57
58    fn visit_expr(&mut self, expr: ExprId<'ast>) {
59        let span = expr.span();
60        if span.start <= self.target && self.target <= span.end {
61            self.path.push(AstNode::Expr(expr));
62            walk_expr(self, expr);
63        }
64    }
65
66    fn visit_class_member(&mut self, member: &'ast ClassMember<'ast>) {
67        let span = match member {
68            ClassMember::Property { span, .. } => *span,
69            ClassMember::PropertyHook { span, .. } => *span,
70            ClassMember::Method { span, .. } => *span,
71            ClassMember::Const { span, .. } => *span,
72            ClassMember::TraitUse { span, .. } => *span,
73            ClassMember::Case { span, .. } => *span,
74        };
75
76        if span.start <= self.target && self.target <= span.end {
77            self.path.push(AstNode::ClassMember(member));
78            walk_class_member(self, member);
79        }
80    }
81}