mod const_exprs;
mod style;
pub use const_exprs::*;
pub use style::*;
use crate::rule_prelude::*;
use ast::*;
use rslint_parser::TextRange;
use std::borrow::Borrow;
use std::cmp;
use std::cmp::{Eq, Ord, Reverse};
use std::collections::{BinaryHeap, HashMap};
use std::hash::Hash;
use SyntaxKind::*;
#[rustfmt::skip]
pub fn most_frequent<T>(items: Vec<T>) -> T
where
T: Hash + Eq + Ord + Clone,
{
let mut map = HashMap::new();
for x in items {
*map.entry(x).or_insert(0) += 1;
}
let mut heap = BinaryHeap::with_capacity(2);
for (x, count) in map.into_iter() {
heap.push(Reverse((count, x)));
if heap.len() > 1 {
heap.pop();
}
}
heap.into_sorted_vec()[0].0.1.to_owned()
}
pub fn get_assignment_expr_value(expr: AssignExpr) -> std::string::String {
assert!(expr.op().is_some());
let tok = expr.syntax().first_lossy_token().unwrap();
let op_str = tok.text();
if op_str == "=" {
expr.rhs()
.map(|e| e.syntax().trimmed_text().to_string())
.unwrap_or_default()
} else {
format!(
"{} {} {}",
expr.lhs()
.map(|e| e.syntax().trimmed_text().to_string())
.unwrap_or_default(),
op_str[..op_str.len() - 1].to_string(),
expr.rhs()
.map(|e| e.syntax().trimmed_text().to_string())
.unwrap_or_default()
)
}
}
pub fn multi_node_range(mut nodes: impl Iterator<Item = SyntaxNode>) -> TextRange {
TextRange::new(
nodes
.next()
.map(|x| x.trimmed_range().start())
.unwrap_or_else(|| 0.into()),
nodes
.last()
.map(|x| x.trimmed_range().end())
.unwrap_or_else(|| 0.into()),
)
}
pub fn token_list_range<I>(items: I) -> TextRange
where
I: IntoIterator,
I::Item: Borrow<SyntaxToken>,
{
let collection = items
.into_iter()
.map(|x| x.borrow().clone())
.collect::<Vec<_>>();
let start = collection
.first()
.expect("Empty token list")
.text_range()
.start();
let end = collection
.last()
.expect("Empty token list")
.text_range()
.end();
TextRange::new(start, end)
}
pub fn string_token_eq<L, R>(left: L, right: R) -> bool
where
L: IntoIterator,
R: IntoIterator,
L::Item: Borrow<SyntaxToken>,
R::Item: Borrow<SyntaxToken>,
{
let left_vec: Vec<L::Item> = left.into_iter().collect();
let right_vec: Vec<R::Item> = right.into_iter().collect();
if left_vec.len() != right_vec.len() {
return false;
}
left_vec
.into_iter()
.zip(right_vec.into_iter())
.all(|(l, r)| l.borrow().to_string() == r.borrow().to_string())
}
pub fn levenshtein_distance(a: &str, b: &str) -> usize {
if a.is_empty() {
return b.chars().count();
} else if b.is_empty() {
return a.chars().count();
}
let mut dcol: Vec<_> = (0..=b.len()).collect();
let mut t_last = 0;
for (i, sc) in a.chars().enumerate() {
let mut current = i;
dcol[0] = current + 1;
for (j, tc) in b.chars().enumerate() {
let next = dcol[j + 1];
if sc == tc {
dcol[j + 1] = current;
} else {
dcol[j + 1] = cmp::min(current, next);
dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
}
current = next;
t_last = j;
}
}
dcol[t_last + 1]
}
pub fn find_best_match_for_name<'a>(
iter_names: impl Iterator<Item = &'a str>,
lookup: &str,
dist: impl Into<Option<usize>>,
) -> Option<&'a str> {
let max_dist = dist
.into()
.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
let name_vec = iter_names.collect::<Vec<_>>();
let (case_insensitive_match, levenshtein_match) = name_vec
.iter()
.filter_map(|&name| {
let dist = levenshtein_distance(lookup, name);
if dist <= max_dist {
Some((name, dist))
} else {
None
}
})
.fold((None, None), |result, (candidate, dist)| {
(
if candidate.to_uppercase() == lookup.to_uppercase() {
Some(candidate)
} else {
result.0
},
match result.1 {
None => Some((candidate, dist)),
Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }),
},
)
});
if let Some(candidate) = case_insensitive_match {
Some(candidate)
} else if levenshtein_match.is_some() {
levenshtein_match.map(|x| x.0)
} else {
find_match_by_sorted_words(name_vec, lookup)
}
}
fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a str>, lookup: &str) -> Option<&'a str> {
iter_names.iter().fold(None, |result, candidate| {
if sort_by_words(candidate) == sort_by_words(lookup) {
Some(candidate)
} else {
result
}
})
}
fn sort_by_words(name: &str) -> std::string::String {
let mut split_words: Vec<&str> = name.split('_').collect();
split_words.sort_unstable();
split_words.join("_")
}
pub fn constructor_or_call_with_callee(
node: impl Borrow<SyntaxNode>,
name: impl AsRef<str>,
) -> bool {
let node = node.borrow();
match node.kind() {
NEW_EXPR | CALL_EXPR => node.children().any(|child| child.text() == name.as_ref()),
_ => false,
}
}
pub fn outer_function(node: impl Borrow<SyntaxNode>) -> Option<SyntaxNode> {
node.borrow()
.ancestors()
.skip(1)
.find(|ancestor| matches!(ancestor.kind(), ARROW_EXPR | FN_DECL | FN_EXPR))
}