fix: Bicep module ref path — strip bicep/ prefix, lookup by ref_path
Two bugs fixed in BicepModuleCatalog:
1. as_completion_items() generated 'br/modules:bicep/modules/appservice:...'
but bicepconfig modulePath='bicep' means Bicep prepends 'bicep/' automatically.
Fix: store ref_path = path.removeprefix('bicep/') and use it in insertText.
Correct output: 'br/modules:modules/appservice:2.3.x'
2. version/param lookups used get_module_by_name() which only matches the last
path segment ('appservice'). New-style refs capture 'modules/appservice' from
the regex, so lookup returned empty. Fix: add get_module_by_ref() that matches
both ref_path and bare name.
Also fixes _iac_param_map to strip path prefix so IAC source descriptions
still enrich completions for 'modules/appservice' refs.
All 63 tests pass.
This commit is contained in:
@@ -67,9 +67,14 @@ def _load_catalog() -> list[dict[str, Any]]:
|
|||||||
for mod_path, info in modules_raw.items():
|
for mod_path, info in modules_raw.items():
|
||||||
versions = info.get("versions", ["latest"])
|
versions = info.get("versions", ["latest"])
|
||||||
name = mod_path.split("/")[-1] if "/" in mod_path else mod_path
|
name = mod_path.split("/")[-1] if "/" in mod_path else mod_path
|
||||||
|
# ref_path: what the user writes after 'br/modules:' in a .bicep file.
|
||||||
|
# bicepconfig modulePath "bicep" means Bicep prepends "bicep/" to the
|
||||||
|
# registry path automatically, so strip that prefix here.
|
||||||
|
ref_path = mod_path.removeprefix("bicep/")
|
||||||
modules.append({
|
modules.append({
|
||||||
"name": name,
|
"name": name,
|
||||||
"path": mod_path,
|
"path": mod_path,
|
||||||
|
"ref_path": ref_path, # e.g. "modules/appservice", "util/types"
|
||||||
"versions": versions,
|
"versions": versions,
|
||||||
"latest": versions[-1] if versions else "latest",
|
"latest": versions[-1] if versions else "latest",
|
||||||
"registry": registry,
|
"registry": registry,
|
||||||
@@ -97,8 +102,13 @@ class BicepModuleCatalog:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _iac_param_map(cls, module_name: str) -> dict[str, dict[str, Any]]:
|
def _iac_param_map(cls, module_name: str) -> dict[str, dict[str, Any]]:
|
||||||
"""Return {param_name: {description, required, type}} from IAC source catalog."""
|
"""Return {param_name: {description, required, type}} from IAC source catalog.
|
||||||
iac_mod = cls._iac.get(module_name, {})
|
|
||||||
|
IAC catalog is keyed by bare module name (last path segment), so strip any
|
||||||
|
path prefix (e.g. 'modules/appservice' → 'appservice').
|
||||||
|
"""
|
||||||
|
bare_name = module_name.split("/")[-1]
|
||||||
|
iac_mod = cls._iac.get(bare_name, {})
|
||||||
return {p["name"]: p for p in iac_mod.get("params", [])}
|
return {p["name"]: p for p in iac_mod.get("params", [])}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -112,12 +122,24 @@ class BicepModuleCatalog:
|
|||||||
return mod
|
return mod
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_module_by_ref(cls, ref_path: str) -> dict[str, Any] | None:
|
||||||
|
"""Look up a module by the path that appears after 'br/modules:' in a .bicep file.
|
||||||
|
|
||||||
|
Matches both new-style ('modules/appservice', 'util/types') and old-style
|
||||||
|
bare names ('appservice') so completions work regardless of module version.
|
||||||
|
"""
|
||||||
|
for mod in cls._modules:
|
||||||
|
if mod["ref_path"] == ref_path or mod["name"] == ref_path:
|
||||||
|
return mod
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_completion_items(cls) -> list[dict[str, Any]]:
|
def as_completion_items(cls) -> list[dict[str, Any]]:
|
||||||
"""Module name completions — shown when typing a module reference string."""
|
"""Module name completions — shown when typing a module reference string."""
|
||||||
items = []
|
items = []
|
||||||
for mod in cls._modules:
|
for mod in cls._modules:
|
||||||
ref = f"br/modules:{mod['path']}:{mod['latest']}"
|
ref = f"br/modules:{mod['ref_path']}:{mod['latest']}"
|
||||||
items.append({
|
items.append({
|
||||||
"label": mod["name"],
|
"label": mod["name"],
|
||||||
"kind": 9, # Module
|
"kind": 9, # Module
|
||||||
@@ -140,7 +162,7 @@ class BicepModuleCatalog:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def version_completion_items(cls, module_name: str) -> list[dict[str, Any]]:
|
def version_completion_items(cls, module_name: str) -> list[dict[str, Any]]:
|
||||||
"""Version completions for a specific module (newest first)."""
|
"""Version completions for a specific module (newest first)."""
|
||||||
mod = cls.get_module_by_name(module_name)
|
mod = cls.get_module_by_ref(module_name)
|
||||||
if not mod:
|
if not mod:
|
||||||
return []
|
return []
|
||||||
schema = mod.get("schema", {})
|
schema = mod.get("schema", {})
|
||||||
@@ -180,7 +202,7 @@ class BicepModuleCatalog:
|
|||||||
has_open_quote: bool = False,
|
has_open_quote: bool = False,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
"""Enum/allowed-value completions for a specific param (e.g. principalType, environmentType)."""
|
"""Enum/allowed-value completions for a specific param (e.g. principalType, environmentType)."""
|
||||||
mod = cls.get_module_by_name(module_name)
|
mod = cls.get_module_by_ref(module_name)
|
||||||
allowed: list[str] = []
|
allowed: list[str] = []
|
||||||
|
|
||||||
if mod:
|
if mod:
|
||||||
@@ -224,7 +246,7 @@ class BicepModuleCatalog:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def param_completion_items(cls, module_name: str, version: str) -> list[dict[str, Any]]:
|
def param_completion_items(cls, module_name: str, version: str) -> list[dict[str, Any]]:
|
||||||
"""Param completions for a specific module+version combination."""
|
"""Param completions for a specific module+version combination."""
|
||||||
mod = cls.get_module_by_name(module_name)
|
mod = cls.get_module_by_ref(module_name)
|
||||||
if not mod:
|
if not mod:
|
||||||
return []
|
return []
|
||||||
schema = mod.get("schema", {})
|
schema = mod.get("schema", {})
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ def _completion_response(items: list) -> dict:
|
|||||||
|
|
||||||
def _make_module(name, versions=None, schema=None):
|
def _make_module(name, versions=None, schema=None):
|
||||||
versions = versions or ["1.0.0", "latest"]
|
versions = versions or ["1.0.0", "latest"]
|
||||||
|
path = f"bicep/modules/{name}"
|
||||||
return {
|
return {
|
||||||
"name": name,
|
"name": name,
|
||||||
"path": f"bicep/modules/{name}",
|
"path": path,
|
||||||
|
"ref_path": f"modules/{name}", # what appears after 'br/modules:' in bicep files
|
||||||
"versions": versions,
|
"versions": versions,
|
||||||
"latest": versions[-1],
|
"latest": versions[-1],
|
||||||
"registry": "iactemplatereg.azurecr.io",
|
"registry": "iactemplatereg.azurecr.io",
|
||||||
|
|||||||
Reference in New Issue
Block a user