Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions graphify/affected.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable
import unicodedata

import networkx as nx

Expand Down Expand Up @@ -45,18 +46,22 @@ def _format_location(data: dict) -> str:

def _bare_name(label: str) -> str:
"""Lowercased label with the callable decoration (trailing "()") removed."""
label = label.lower()
label = _normalize_label(label)
return label[:-2] if label.endswith("()") else label


def _normalize_label(label: str) -> str:
return unicodedata.normalize("NFC", label).casefold()


def resolve_seed(graph: nx.Graph, query: str) -> str | None:
if query in graph:
return query
query_lower = query.lower()
query_lower = _normalize_label(query)
exact_label_matches = [
str(node_id)
for node_id, data in graph.nodes(data=True)
if str(data.get("label", "")).lower() == query_lower
if _normalize_label(str(data.get("label", ""))) == query_lower
]
if len(exact_label_matches) == 1:
return exact_label_matches[0]
Expand All @@ -74,14 +79,14 @@ def resolve_seed(graph: nx.Graph, query: str) -> str | None:
exact_source_matches = [
str(node_id)
for node_id, data in graph.nodes(data=True)
if str(data.get("source_file", "")).lower() == query_lower
if _normalize_label(str(data.get("source_file", ""))) == query_lower
]
if len(exact_source_matches) == 1:
return exact_source_matches[0]
contains_matches = [
str(node_id)
for node_id, data in graph.nodes(data=True)
if query_lower in str(data.get("label", "")).lower()
if query_lower in _normalize_label(str(data.get("label", "")))
]
if len(contains_matches) == 1:
return contains_matches[0]
Expand Down
12 changes: 9 additions & 3 deletions graphify/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,12 +464,18 @@ def _find_node(G: nx.Graph, label: str) -> list[str]:
for nid, d in G.nodes(data=True):
norm_label = d.get("norm_label") or _strip_diacritics(d.get("label") or "").lower()
bare_label = norm_label.rstrip("()")
label_tokens = " ".join(_search_tokens(d.get("label") or ""))
nid_lower = nid.lower()
if term == norm_label or term == bare_label or term == nid_lower:
if term == norm_label or term == bare_label or term == label_tokens or term == nid_lower:
exact.append(nid)
elif norm_label.startswith(term) or bare_label.startswith(term) or nid_lower.startswith(term):
elif (
norm_label.startswith(term)
or bare_label.startswith(term)
or label_tokens.startswith(term)
or nid_lower.startswith(term)
):
prefix.append(nid)
elif term in norm_label:
elif term in norm_label or term in label_tokens:
substring.append(nid)
return exact + prefix + substring

Expand Down
21 changes: 21 additions & 0 deletions tests/test_affected_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,27 @@ def test_resolve_seed_decorated_query_matches_bare_label():
assert resolve_seed(graph, "Foo()") == "a"


def test_resolve_seed_matches_unicode_normalized_label():
import unicodedata

from graphify.affected import resolve_seed

graph = nx.DiGraph()
graph.add_node("a", label="Auditoría", source_file="pkg/auditoria.py")

assert resolve_seed(graph, unicodedata.normalize("NFD", "Auditoría")) == "a"


def test_resolve_seed_preserves_distinct_accents():
from graphify.affected import resolve_seed

graph = nx.DiGraph()
graph.add_node("a", label="resume", source_file="pkg/resume.py")
graph.add_node("b", label="résumé", source_file="pkg/resume_accented.py")

assert resolve_seed(graph, "resume") == "a"


def test_resolve_seed_bare_name_tie_still_returns_none():
from graphify.affected import resolve_seed

Expand Down
7 changes: 7 additions & 0 deletions tests/test_serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ def test_find_node_ignores_trailing_punctuation():
assert _find_node(G, "extract?") == ["n1"]


def test_find_node_matches_full_punctuated_unicode_label():
G = nx.Graph()
G.add_node("n1", label="Skill /auditar — Auditoría inquisitiva de enlaces")

assert _find_node(G, "Skill /auditar — Auditoría inquisitiva de enlaces") == ["n1"]


def test_query_terms_strips_search_punctuation():
assert _query_terms("what calls extract?") == ["what", "calls", "extract"]

Expand Down
Loading