Skip to content
Open
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
34 changes: 27 additions & 7 deletions src/codesurface/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ def _build_search_text(record: dict) -> str:
val = record.get(field, "")
if val:
tokens.append(split_identifier(val))
# Last namespace segment (e.g. "Services" from "CampGame.Services")
# Last namespace segment (e.g. "Services" from "CampGame.Services",
# or "Utils" from "MyLib::Utils")
ns = record.get("namespace", "")
if ns:
last_part = ns.rsplit(".", 1)[-1]
last_part = re.split(r"[.:]", ns)[-1]
tokens.append(split_identifier(last_part))
return " ".join(tokens)

Expand Down Expand Up @@ -188,13 +189,32 @@ def get_by_fqn(conn: sqlite3.Connection, fqn: str) -> dict | None:
return dict(row) if row else None


def get_class_members(conn: sqlite3.Connection, class_name: str) -> list[dict]:
"""Get all members of a class by class name."""
def get_class_members(conn: sqlite3.Connection, class_name: str,
namespace: str | None = None) -> list[dict]:
"""Get all members of a class by class name, optionally filtered by namespace."""
if namespace is not None:
rows = conn.execute(
"SELECT * FROM api_records WHERE class_name = ? AND namespace = ? "
"ORDER BY member_type, member_name",
(class_name, namespace),
).fetchall()
else:
rows = conn.execute(
"SELECT * FROM api_records WHERE class_name = ? ORDER BY member_type, member_name",
(class_name,),
).fetchall()
return [dict(row) for row in rows]


def get_class_namespaces(conn: sqlite3.Connection, class_name: str) -> list[str]:
"""Get all distinct namespaces that contain a class with this name."""
rows = conn.execute(
"SELECT * FROM api_records WHERE class_name = ? ORDER BY member_type, member_name",
"SELECT DISTINCT namespace FROM api_records "
"WHERE class_name = ? AND member_type = 'type' "
"ORDER BY namespace",
(class_name,),
).fetchall()
return [dict(row) for row in rows]
return [row["namespace"] for row in rows]


def resolve_namespace(conn: sqlite3.Connection, name: str) -> list[dict]:
Expand Down Expand Up @@ -249,7 +269,7 @@ def _escape_fts(query: str) -> str:
"ICommand" → (ICommand*) OR (I Command*)
"""
q = query
for ch in '."-*()':
for ch in '."-*():,;{}[]!@#$%^&+|\\~`':
q = q.replace(ch, " ")
terms = [t for t in q.split() if t]
Comment on lines +272 to 274
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing characters via a loop repeatedly scans the string (O(k·n) per query, where k is the number of escaped characters). Since this runs on every search, consider switching to a single-pass approach (e.g., str.translate with a translation table, or a precompiled regex) to reduce per-query overhead.

Copilot uses AI. Check for mistakes.
if not terms:
Expand Down
2 changes: 2 additions & 0 deletions src/codesurface/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ def all_extensions() -> list[str]:

# --- Auto-register built-in parsers ---

from .cpp import CppParser # noqa: E402
from .csharp import CSharpParser # noqa: E402
from .go import GoParser # noqa: E402
from .java import JavaParser # noqa: E402
from .python_parser import PythonParser # noqa: E402
from .typescript import TypeScriptParser # noqa: E402

register("cpp", CppParser)
register("csharp", CSharpParser)
register("go", GoParser)
register("java", JavaParser)
Expand Down
Loading
Loading