backup: uncommitted changes from MAC-M9FQ0900T3 2026-05-17 15:52:31
This commit is contained in:
@@ -36,6 +36,34 @@ _IAC_SOURCE_PATHS = [
|
||||
pathlib.Path(__file__).parent.parent.parent / "iac_source_catalog.json", # dev
|
||||
]
|
||||
|
||||
# Principals catalog — known object IDs for array params (e.g. additionalAccess)
|
||||
# Format: {"params": {"additionalAccess": [{"id": "<guid>", "label": "...", "description": "..."}]}}
|
||||
_PRINCIPALS_CATALOG_PATHS = [
|
||||
pathlib.Path("/data/principals_catalog.json"), # volume-mount (freshest)
|
||||
pathlib.Path("/principals_catalog.json"), # baked into image (fallback)
|
||||
pathlib.Path(__file__).parent.parent.parent / "principals_catalog.json", # dev
|
||||
]
|
||||
|
||||
|
||||
def _load_principals_catalog() -> dict[str, list[dict[str, Any]]]:
|
||||
"""Load principals catalog for array param completions.
|
||||
|
||||
Returns dict keyed by param name (e.g. 'additionalAccess') → list of
|
||||
{id, label, description} entries.
|
||||
"""
|
||||
for path in _PRINCIPALS_CATALOG_PATHS:
|
||||
if path.exists():
|
||||
try:
|
||||
data = json.loads(path.read_text())
|
||||
params = data.get("params", {})
|
||||
count = sum(len(v) for v in params.values())
|
||||
logger.info("Principals catalog loaded from %s: %d entries", path, count)
|
||||
return params
|
||||
except Exception:
|
||||
logger.exception("Failed to parse principals catalog at %s", path)
|
||||
logger.debug("No principals_catalog.json found — array param completions unavailable")
|
||||
return {}
|
||||
|
||||
|
||||
def _load_iac_source_catalog() -> dict[str, dict[str, Any]]:
|
||||
"""Load IAC source catalog for enriched param descriptions.
|
||||
@@ -94,12 +122,14 @@ class BicepModuleCatalog:
|
||||
|
||||
_modules: list[dict[str, Any]] = []
|
||||
_iac: dict[str, dict[str, Any]] = {} # module name → IAC source info
|
||||
_principals: dict[str, list[dict[str, Any]]] = {} # param name → [{id, label, description}]
|
||||
|
||||
@classmethod
|
||||
def load(cls) -> None:
|
||||
"""Load both catalogs from disk. Call once at startup."""
|
||||
"""Load all catalogs from disk. Call once at startup."""
|
||||
cls._modules = _load_catalog()
|
||||
cls._iac = _load_iac_source_catalog()
|
||||
cls._principals = _load_principals_catalog()
|
||||
|
||||
@classmethod
|
||||
def _iac_param_map(cls, module_name: str) -> dict[str, dict[str, Any]]:
|
||||
@@ -246,6 +276,44 @@ class BicepModuleCatalog:
|
||||
})
|
||||
return items
|
||||
|
||||
@classmethod
|
||||
def param_array_item_completion_items(
|
||||
cls,
|
||||
module_name: str,
|
||||
version: str,
|
||||
param_name: str,
|
||||
has_open_quote: bool = False,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Completions for items inside an array param (e.g. additionalAccess objectIds).
|
||||
|
||||
Looks up known entries from the principals catalog keyed by param name.
|
||||
"""
|
||||
entries = cls._principals.get(param_name, [])
|
||||
if not entries:
|
||||
return []
|
||||
|
||||
items = []
|
||||
for i, entry in enumerate(entries):
|
||||
val = entry["id"]
|
||||
label = entry.get("label", val)
|
||||
description = entry.get("description", "")
|
||||
insert = f"{val}'" if has_open_quote else f"'{val}'"
|
||||
doc = f"**{label}**\n\n`{val}`"
|
||||
if description:
|
||||
doc += f"\n\n{description}"
|
||||
items.append({
|
||||
"label": label,
|
||||
"kind": 12, # Value
|
||||
"detail": val, # GUID shown as detail
|
||||
"insertText": insert,
|
||||
"sortText": f"0_lru_arr_{i:03d}_{label}",
|
||||
"documentation": {
|
||||
"kind": "markdown",
|
||||
"value": doc,
|
||||
},
|
||||
})
|
||||
return items
|
||||
|
||||
@classmethod
|
||||
def param_completion_items(cls, module_name: str, version: str) -> list[dict[str, Any]]:
|
||||
"""Param completions for a specific module+version combination."""
|
||||
|
||||
@@ -130,6 +130,19 @@ class _ProxySession:
|
||||
mod_name = last_mod.group(1)
|
||||
mod_ver = last_mod.group(2)
|
||||
|
||||
# Array item context: cursor inside [...] for an array param.
|
||||
# Must be checked BEFORE value_m, since array lines also match
|
||||
# the value pattern (e.g. `additionalAccess: ['`).
|
||||
array_m = re.search(r"^\s*(\w+):\s*\[", current)
|
||||
if array_m and current.count("[") > current.count("]"):
|
||||
return {
|
||||
"type": "param_array_item",
|
||||
"module": mod_name,
|
||||
"version": mod_ver,
|
||||
"param": array_m.group(1),
|
||||
"has_open_quote": bool(re.search(r"'[^']*$", current)),
|
||||
}
|
||||
|
||||
# Check if cursor is after 'paramname: ' on the current line
|
||||
# (value context — inject enum/allowed values)
|
||||
value_m = re.search(r"^\s*(\w+):\s*('?)([^'{}]*)$", current)
|
||||
@@ -197,11 +210,18 @@ def _inject_completions(msg: dict[str, Any], context: dict | None = None) -> byt
|
||||
context["param"],
|
||||
context.get("has_open_quote", False),
|
||||
)
|
||||
elif ctx_type == "param_array_item":
|
||||
lru_items = BicepModuleCatalog.param_array_item_completion_items(
|
||||
context["module"],
|
||||
context["version"],
|
||||
context["param"],
|
||||
context.get("has_open_quote", False),
|
||||
)
|
||||
else:
|
||||
# Default: module name completions
|
||||
lru_items = BicepModuleCatalog.as_completion_items()
|
||||
|
||||
if ctx_type in ("version", "param", "param_value"):
|
||||
if ctx_type in ("version", "param", "param_value", "param_array_item"):
|
||||
# Always replace LS completions for private-registry contexts — the
|
||||
# Bicep LS doesn't know about our ACR, so anything it returns is noise.
|
||||
# Even if lru_items is empty (no enum values for a param), suppress LS.
|
||||
|
||||
Reference in New Issue
Block a user