|
|
""" |
|
|
Usage checker for bibliography entries in TeX files. |
|
|
""" |
|
|
from dataclasses import dataclass |
|
|
from typing import Optional |
|
|
|
|
|
from ..parsers.bib_parser import BibEntry |
|
|
from ..parsers.tex_parser import TexParser, CitationContext |
|
|
|
|
|
|
|
|
@dataclass |
|
|
class UsageResult: |
|
|
"""Result of checking if a bib entry is used.""" |
|
|
entry_key: str |
|
|
is_used: bool |
|
|
usage_count: int |
|
|
contexts: list[CitationContext] |
|
|
line_numbers: list[int] |
|
|
|
|
|
@property |
|
|
def first_usage_line(self) -> Optional[int]: |
|
|
return self.line_numbers[0] if self.line_numbers else None |
|
|
|
|
|
|
|
|
class UsageChecker: |
|
|
"""Checks if bibliography entries are used in TeX files.""" |
|
|
|
|
|
def __init__(self, tex_parser: TexParser): |
|
|
self.tex_parser = tex_parser |
|
|
self._cited_keys = tex_parser.get_all_cited_keys() |
|
|
|
|
|
def check_usage(self, entry: BibEntry) -> UsageResult: |
|
|
"""Check if a bib entry is used in the TeX document.""" |
|
|
key = entry.key |
|
|
is_used = key in self._cited_keys |
|
|
contexts = self.tex_parser.get_citation_contexts(key) |
|
|
|
|
|
return UsageResult( |
|
|
entry_key=key, |
|
|
is_used=is_used, |
|
|
usage_count=len(contexts), |
|
|
contexts=contexts, |
|
|
line_numbers=[ctx.line_number for ctx in contexts] |
|
|
) |
|
|
|
|
|
def get_unused_entries(self, entries: list[BibEntry]) -> list[BibEntry]: |
|
|
"""Get list of entries that are not cited in the document.""" |
|
|
unused = [] |
|
|
for entry in entries: |
|
|
if entry.key not in self._cited_keys: |
|
|
unused.append(entry) |
|
|
return unused |
|
|
|
|
|
def get_missing_entries(self, entries: list[BibEntry]) -> list[str]: |
|
|
"""Get list of citation keys that don't have corresponding bib entries.""" |
|
|
entry_keys = {e.key for e in entries} |
|
|
missing = [] |
|
|
for key in self._cited_keys: |
|
|
if key not in entry_keys: |
|
|
missing.append(key) |
|
|
return missing |
|
|
|
|
|
def get_combined_context(self, key: str, max_chars: int = 1000) -> str: |
|
|
"""Get combined context for all usages of a key.""" |
|
|
contexts = self.tex_parser.get_citation_contexts(key) |
|
|
if not contexts: |
|
|
return "" |
|
|
|
|
|
combined = [] |
|
|
total_chars = 0 |
|
|
|
|
|
for ctx in contexts: |
|
|
if total_chars + len(ctx.full_context) > max_chars: |
|
|
|
|
|
remaining = max_chars - total_chars |
|
|
if remaining > 100: |
|
|
combined.append(ctx.full_context[:remaining] + "...") |
|
|
break |
|
|
combined.append(ctx.full_context) |
|
|
total_chars += len(ctx.full_context) |
|
|
|
|
|
return "\n---\n".join(combined) |
|
|
|