diff --git a/Backend/Portals.py b/Backend/Portals.py index 9aa6f51..482a4de 100644 --- a/Backend/Portals.py +++ b/Backend/Portals.py @@ -23,6 +23,9 @@ MarkerPattern = re.compile( LoopMarkerPattern = re.compile( r"\{(?Pchildren|siblings|parents)\s+%\s+(?P[A-Za-z_][A-Za-z0-9_]*)\}" r"|\{(?Pchildren|siblings|parents)\s+%\}" + r"|\{(?:(?P[^\s{}']+)\s+)?\*\*\*(?P[^\s*{}]+)" + r"\s+%\s+(?P[A-Za-z_][A-Za-z0-9_]*)\}" + r"|\{\*\*\*(?P[^\s*{}]+)\s+%\}" ) @@ -147,7 +150,24 @@ def _ComputeLoopLists( return {"children": Children, "siblings": Siblings, "parents": Parents} -def _ResolveLoops(Content: str, Lists: dict[str, list[str]]) -> str: +def _FindLoopClose(Matches: list[re.Match], StartIndex: int, IsFixed: bool, Key: str) -> re.Match | None: + GroupName = "CloseSource" if IsFixed else "CloseCrossName" + + for Candidate in Matches[StartIndex:]: + if Candidate.group(GroupName) == Key: + return Candidate + + return None + + +def _ResolveLoops( + Content: str, + Lists: dict[str, list[str]], + RootName: str, + RefererDirectory: str, + Username: str, + UserGroups: list[str], +) -> str: Matches = list(LoopMarkerPattern.finditer(Content)) Output: list[str] = [] Cursor = 0 @@ -155,23 +175,28 @@ def _ResolveLoops(Content: str, Lists: dict[str, list[str]]) -> str: while Index < len(Matches): Match = Matches[Index] + IsFixed = bool(Match.group("LoopSource")) + IsCross = bool(Match.group("LoopCrossName")) - if not Match.group("LoopSource"): + if not IsFixed and not IsCross: Index += 1 continue - Source = Match.group("LoopSource") - LoopVar = Match.group("LoopVar") - - CloseMatch = None - for Candidate in Matches[Index + 1 :]: - if Candidate.group("CloseSource") == Source: - CloseMatch = Candidate - break + if IsFixed: + Key = Match.group("LoopSource") + LoopVar = Match.group("LoopVar") + Items = Lists.get(Key, []) + else: + Key = Match.group("LoopCrossName") + LoopVar = Match.group("LoopCrossVar") + PortalContent = _ResolveCrossFileReference( + RootName, RefererDirectory, Match.group("LoopCrossPath"), Key, Username, UserGroups + ) + Items = PortalContent.splitlines() if PortalContent is not None else [] + CloseMatch = _FindLoopClose(Matches, Index + 1, IsFixed, Key) BodyEnd = CloseMatch.start() if CloseMatch is not None else len(Content) BodyTemplate = Content[Match.end() : BodyEnd] - Items = Lists.get(Source, []) VariablePattern = re.compile(rf"%{re.escape(LoopVar)}\b") Rendered = "\n".join(VariablePattern.sub(Item, BodyTemplate) for Item in Items) @@ -319,10 +344,12 @@ def _ResolveLineNumberReference( def ResolvePortals( RootName: str, RelativePath: str, Content: str, Username: str, UserGroups: list[str] ) -> str: - LoopLists = _ComputeLoopLists(RootName, RelativePath, Username, UserGroups) - WithLoopsResolved = _ResolveLoops(Content, LoopLists) - LocallyResolved = _ResolveLocalPortals(WithLoopsResolved) RefererDirectory = RelativePath.rsplit("/", 1)[0] if "/" in RelativePath else "" + LoopLists = _ComputeLoopLists(RootName, RelativePath, Username, UserGroups) + WithLoopsResolved = _ResolveLoops( + Content, LoopLists, RootName, RefererDirectory, Username, UserGroups + ) + LocallyResolved = _ResolveLocalPortals(WithLoopsResolved) Matches = list(MarkerPattern.finditer(LocallyResolved)) Output: list[str] = []