use super::Matcher;
use crate::meta_var::MetaVarEnv;
use crate::node::KindId;
use crate::{Doc, Language, Node};
use std::borrow::Cow;
use std::marker::PhantomData;
use bit_set::BitSet;
use thiserror::Error;
const TS_BUILTIN_SYM_END: KindId = 0;
const TS_BUILTIN_SYM_ERROR: KindId = 65535;
#[derive(Debug, Error)]
pub enum KindMatcherError {
#[error("Kind `{0}` is invalid.")]
InvalidKindName(String),
}
#[derive(Clone)]
pub struct KindMatcher<L: Language> {
kind: KindId,
lang: PhantomData<L>,
}
impl<L: Language> KindMatcher<L> {
pub fn new(node_kind: &str, lang: L) -> Self {
Self {
kind: lang
.get_ts_language()
.id_for_node_kind(node_kind, true),
lang: PhantomData,
}
}
pub fn try_new(node_kind: &str, lang: L) -> Result<Self, KindMatcherError> {
let s = Self::new(node_kind, lang);
if s.is_invalid() {
Err(KindMatcherError::InvalidKindName(node_kind.into()))
} else {
Ok(s)
}
}
pub fn from_id(kind: KindId) -> Self {
Self {
kind,
lang: PhantomData,
}
}
pub fn is_invalid(&self) -> bool {
self.kind == TS_BUILTIN_SYM_END
}
pub fn is_error_matcher(&self) -> bool {
self.kind == TS_BUILTIN_SYM_ERROR
}
}
impl<L: Language> Matcher<L> for KindMatcher<L> {
fn match_node_with_env<'tree, D: Doc<Lang = L>>(
&self,
node: Node<'tree, D>,
_env: &mut Cow<MetaVarEnv<'tree, D>>,
) -> Option<Node<'tree, D>> {
if node.kind_id() == self.kind {
Some(node)
} else {
None
}
}
fn potential_kinds(&self) -> Option<BitSet> {
let mut set = BitSet::new();
set.insert(self.kind.into());
Some(set)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::language::Tsx;
use crate::{Root, StrDoc};
fn pattern_node(s: &str) -> Root<StrDoc<Tsx>> {
Root::new(s, Tsx)
}
#[test]
fn test_kind_match() {
let kind = "public_field_definition";
let cand = pattern_node("class A { a = 123 }");
let cand = cand.root();
let pattern = KindMatcher::new(kind, Tsx);
assert!(
pattern.find_node(cand.clone()).is_some(),
"goal: {}, candidate: {}",
kind,
cand.to_sexp(),
);
}
#[test]
fn test_kind_non_match() {
let kind = "field_definition";
let cand = pattern_node("const a = 123");
let cand = cand.root();
let pattern = KindMatcher::new(kind, Tsx);
assert!(
pattern.find_node(cand.clone()).is_none(),
"goal: {}, candidate: {}",
kind,
cand.to_sexp(),
);
}
#[test]
fn test_kind_potential_kinds() {
let kind = "field_definition";
let matcher = KindMatcher::new(kind, Tsx);
let potential_kinds = matcher
.potential_kinds()
.expect("should have potential kinds");
assert_eq!(potential_kinds.len(), 1);
}
}