#![allow(dead_code)]
use badness::bib;
use texlab_syntax::bibtex;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BibAtom {
Entry(String, Vec<BibAtom>),
StringDef,
Preamble,
Field(String),
}
pub fn project_badness(text: &str) -> Vec<BibAtom> {
use bib::syntax::SyntaxKind::*;
let root = bib::parse(text).syntax();
let mut out = Vec::new();
for node in root.children() {
match node.kind() {
ENTRY => out.push(BibAtom::Entry(
badness_entry_type(&node),
badness_fields(&node),
)),
STRING_ENTRY => out.push(BibAtom::StringDef),
PREAMBLE_ENTRY => out.push(BibAtom::Preamble),
_ => {}
}
}
out
}
fn badness_entry_type(entry: &bib::syntax::SyntaxNode) -> String {
use bib::syntax::SyntaxKind::ENTRY_TYPE;
entry
.children()
.find(|n| n.kind() == ENTRY_TYPE)
.map(|n| n.text().to_string().trim().to_ascii_lowercase())
.unwrap_or_default()
}
fn badness_fields(entry: &bib::syntax::SyntaxNode) -> Vec<BibAtom> {
use bib::syntax::SyntaxKind::{FIELD, FIELD_NAME};
entry
.children()
.filter(|n| n.kind() == FIELD)
.map(|field| {
let name = field
.children()
.find(|n| n.kind() == FIELD_NAME)
.map(|n| n.text().to_string().trim().to_ascii_lowercase())
.unwrap_or_default();
BibAtom::Field(name)
})
.collect()
}
pub fn project_texlab(text: &str) -> Vec<BibAtom> {
use bibtex::SyntaxKind::*;
let root = texlab_root(text);
let mut out = Vec::new();
for node in root.children() {
match node.kind() {
ENTRY => out.push(BibAtom::Entry(
texlab_entry_type(&node),
texlab_fields(&node),
)),
STRING => out.push(BibAtom::StringDef),
PREAMBLE => out.push(BibAtom::Preamble),
_ => {}
}
}
out
}
fn texlab_root(text: &str) -> bibtex::SyntaxNode {
let green = texlab_parser::parse_bibtex(text);
bibtex::SyntaxNode::new_root(green)
}
fn texlab_entry_type(entry: &bibtex::SyntaxNode) -> String {
use bibtex::SyntaxKind::TYPE;
entry
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| t.kind() == TYPE)
.map(|t| t.text().trim().trim_start_matches('@').to_ascii_lowercase())
.unwrap_or_default()
}
fn texlab_fields(entry: &bibtex::SyntaxNode) -> Vec<BibAtom> {
use bibtex::SyntaxKind::{FIELD, NAME};
entry
.children()
.filter(|n| n.kind() == FIELD)
.map(|field| {
let name = field
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| t.kind() == NAME)
.map(|t| t.text().trim().to_ascii_lowercase())
.unwrap_or_default();
BibAtom::Field(name)
})
.collect()
}
pub fn count_entries(forest: &[BibAtom]) -> usize {
forest
.iter()
.filter(|a| {
matches!(
a,
BibAtom::Entry(..) | BibAtom::StringDef | BibAtom::Preamble
)
})
.count()
}
pub fn render_lines(forest: &[BibAtom]) -> Vec<String> {
let mut out = Vec::new();
for atom in forest {
render_atom(atom, 0, &mut out);
}
out
}
fn render_atom(atom: &BibAtom, depth: usize, out: &mut Vec<String>) {
let pad = " ".repeat(depth);
let (head, children): (String, &[BibAtom]) = match atom {
BibAtom::Entry(ty, ch) => (format!("(entry {ty})"), ch),
BibAtom::StringDef => ("(string)".to_string(), &[]),
BibAtom::Preamble => ("(preamble)".to_string(), &[]),
BibAtom::Field(name) => (format!("(field {name})"), &[]),
};
out.push(format!("{pad}{head}"));
for child in children {
render_atom(child, depth + 1, out);
}
}
pub fn lcs_len(a: &[String], b: &[String]) -> usize {
if a.is_empty() || b.is_empty() {
return 0;
}
let mut prev = vec![0usize; b.len() + 1];
for line_a in a {
let mut cur = vec![0usize; b.len() + 1];
for (j, line_b) in b.iter().enumerate() {
cur[j + 1] = if line_a == line_b {
prev[j] + 1
} else {
cur[j].max(prev[j + 1])
};
}
prev = cur;
}
prev[b.len()]
}
pub fn dice(a: &[String], b: &[String]) -> f64 {
let denom = a.len() + b.len();
if denom == 0 {
return 1.0;
}
2.0 * lcs_len(a, b) as f64 / denom as f64
}