Release 0.2.0: object tree, refs/modules options, docs
Add query object (--clear), modules -n, composite refs labels, FillChecking/TypeSet parsing, VERSION/CHANGELOG and --version. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+92
-23
@@ -123,14 +123,18 @@ def search_modules(
|
||||
*,
|
||||
configs: list[str] | None = None,
|
||||
limit: int = 30,
|
||||
names_only: bool = False,
|
||||
) -> list[dict[str, Any]]:
|
||||
fts = _fts_query(query)
|
||||
sql = """
|
||||
snip_col = "'' AS snip" if names_only else "snippet(modules_fts, 4, '[', ']', '…', 16) AS snip"
|
||||
sql = f"""
|
||||
SELECT m.config, m.rel_path, m.owner, m.module_kind, m.symbols, m.line_count,
|
||||
snippet(modules_fts, 4, '[', ']', '…', 16) AS snip,
|
||||
o.synonym AS owner_synonym,
|
||||
{snip_col},
|
||||
bm25(modules_fts, 2.0, 5.0, 2.0, 8.0, 1.0) AS score
|
||||
FROM modules_fts
|
||||
JOIN modules m ON m.id = modules_fts.module_id
|
||||
LEFT JOIN objects o ON o.config = m.config AND o.full_name = m.owner
|
||||
WHERE modules_fts MATCH ?
|
||||
"""
|
||||
params: list[Any] = [fts]
|
||||
@@ -144,13 +148,15 @@ def search_modules(
|
||||
except sqlite3.OperationalError:
|
||||
like = f"%{query}%"
|
||||
sql2 = """
|
||||
SELECT config, rel_path, owner, module_kind, symbols, line_count, '' AS snip, 0 AS score
|
||||
FROM modules
|
||||
WHERE rel_path LIKE ? OR owner LIKE ? OR symbols LIKE ?
|
||||
SELECT m.config, m.rel_path, m.owner, m.module_kind, m.symbols, m.line_count,
|
||||
o.synonym AS owner_synonym, '' AS snip, 0 AS score
|
||||
FROM modules m
|
||||
LEFT JOIN objects o ON o.config = m.config AND o.full_name = m.owner
|
||||
WHERE m.rel_path LIKE ? OR m.owner LIKE ? OR m.symbols LIKE ?
|
||||
"""
|
||||
params2: list[Any] = [like, like, like]
|
||||
if configs:
|
||||
sql2 += f" AND config IN ({','.join('?' * len(configs))})"
|
||||
sql2 += f" AND m.config IN ({','.join('?' * len(configs))})"
|
||||
params2.extend(configs)
|
||||
sql2 += " LIMIT ?"
|
||||
params2.append(limit)
|
||||
@@ -163,7 +169,13 @@ def find_refs_to(
|
||||
*,
|
||||
configs: list[str] | None = None,
|
||||
limit: int = 100,
|
||||
) -> list[dict[str, Any]]:
|
||||
) -> tuple[list[dict[str, Any]], dict[str, int]]:
|
||||
"""Обратные ссылки на объект.
|
||||
|
||||
Возвращает (строки, totals_by_config).
|
||||
При поиске по нескольким конфигурациям ``limit`` — максимум **на каждую**
|
||||
конфигурацию (чтобы crm3-26 не вытеснял crm3-dev из-за ORDER BY + LIMIT).
|
||||
"""
|
||||
kind = ""
|
||||
name = target
|
||||
if "." in target:
|
||||
@@ -183,25 +195,82 @@ def find_refs_to(
|
||||
}:
|
||||
kind_ref = kind + "Ref"
|
||||
|
||||
where = "r.to_name = ?"
|
||||
params: list[Any] = [name]
|
||||
if kind_ref:
|
||||
sql = """
|
||||
SELECT config, from_full_name, from_field, to_kind, to_name
|
||||
FROM refs
|
||||
WHERE to_name = ? AND (to_kind = ? OR to_kind = ?)
|
||||
"""
|
||||
params: list[Any] = [name, kind_ref, kind]
|
||||
else:
|
||||
sql = """
|
||||
SELECT config, from_full_name, from_field, to_kind, to_name
|
||||
FROM refs WHERE to_name = ?
|
||||
"""
|
||||
params = [name]
|
||||
where += " AND (r.to_kind = ? OR r.to_kind = ?)"
|
||||
params.extend([kind_ref, kind])
|
||||
if configs:
|
||||
sql += f" AND config IN ({','.join('?' * len(configs))})"
|
||||
where += f" AND r.config IN ({','.join('?' * len(configs))})"
|
||||
params.extend(configs)
|
||||
sql += " ORDER BY config, from_full_name LIMIT ?"
|
||||
params.append(limit)
|
||||
return [dict(r) for r in conn.execute(sql, params)]
|
||||
|
||||
# Итоги по конфигурациям (без LIMIT)
|
||||
totals: dict[str, int] = {
|
||||
r["config"]: int(r["n"])
|
||||
for r in conn.execute(
|
||||
f"SELECT r.config, COUNT(*) AS n FROM refs r WHERE {where} GROUP BY r.config",
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
||||
# limit на каждую конфигурацию — иначе алфавитный ORDER BY отрезает остальные
|
||||
sql = f"""
|
||||
WITH matched AS (
|
||||
SELECT
|
||||
r.config,
|
||||
r.from_full_name,
|
||||
r.from_field,
|
||||
r.to_kind,
|
||||
r.to_name,
|
||||
f.types AS field_types,
|
||||
f.refs_json AS field_refs_json,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY r.config
|
||||
ORDER BY r.from_full_name, r.from_field
|
||||
) AS rn
|
||||
FROM refs r
|
||||
LEFT JOIN objects o
|
||||
ON o.config = r.config AND o.full_name = r.from_full_name
|
||||
LEFT JOIN fields f
|
||||
ON f.object_id = o.id
|
||||
AND (
|
||||
(COALESCE(f.tabular_section, '') = '' AND f.name = r.from_field)
|
||||
OR (f.tabular_section || '.' || f.name = r.from_field)
|
||||
)
|
||||
WHERE {where}
|
||||
)
|
||||
SELECT *
|
||||
FROM matched
|
||||
WHERE rn <= ?
|
||||
ORDER BY config, from_full_name, from_field
|
||||
"""
|
||||
rows = [dict(r) for r in conn.execute(sql, [*params, limit])]
|
||||
|
||||
for row in rows:
|
||||
types_s = row.get("field_types") or ""
|
||||
refs_raw = row.get("field_refs_json") or "[]"
|
||||
try:
|
||||
refs_list = json.loads(refs_raw) if refs_raw else []
|
||||
except json.JSONDecodeError:
|
||||
refs_list = []
|
||||
type_parts = [t.strip() for t in types_s.split(",") if t.strip()]
|
||||
# составной: несколько cfg:* или несколько ссылок в типе
|
||||
composite = len(refs_list) > 1 or len(type_parts) > 1
|
||||
row["is_composite"] = composite
|
||||
others: list[str] = []
|
||||
target_full = f"{row['to_kind']}.{row['to_name']}"
|
||||
for ref in refs_list:
|
||||
label = f"{ref.get('kind')}.{ref.get('name')}"
|
||||
if label != target_full and label not in others:
|
||||
others.append(label)
|
||||
for t in type_parts:
|
||||
# cfg:DocumentRef.X → DocumentRef.X
|
||||
short = t[4:] if t.startswith("cfg:") else t
|
||||
if short != target_full and short not in others and "Ref." in short:
|
||||
others.append(short)
|
||||
row["other_types"] = others
|
||||
|
||||
return rows, totals
|
||||
|
||||
|
||||
def get_object(
|
||||
|
||||
Reference in New Issue
Block a user