From b06231ea5e9b9308adb4369154fc7fc78a7aa2bc Mon Sep 17 00:00:00 2001 From: SRHTNIN Date: Mon, 21 Sep 2026 23:40:04 +0200 Subject: [PATCH] Added clipping function. --- Backend/VarFunctions.py | 68 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/Backend/VarFunctions.py b/Backend/VarFunctions.py index cd15666..e9e7159 100644 --- a/Backend/VarFunctions.py +++ b/Backend/VarFunctions.py @@ -13,9 +13,12 @@ LoopSources = ("children", "siblings", "parents") MarkerPattern = re.compile( r"\{(?:(?P[^\s{}']+)\s+)?\*\*\*(?P[^\s*{}]+)" - r"(?:\s+(?Phead|tail)\s+(?P\d+))?\}" - r"|\{(?P[^\s{}']+)\s+(?Phead|tail)\s+(?P\d+)\}" - r"|\{(?P[^\s{}']+)\s+'(?P\d+)'\}" + r"(?:\s+(?Phead|tail)\s+(?P\d+))?" + r"(?:\s*\\\s*(?P[^{}]*))?\}" + r"|\{(?P[^\s{}']+)\s+(?Phead|tail)\s+(?P\d+)" + r"(?:\s*\\\s*(?P[^{}]*))?\}" + r"|\{(?P[^\s{}']+)\s+'(?P\d+)'" + r"(?:\s*\\\s*(?P[^{}]*))?\}" r"|\{\*\*(?P[^\s*{}]+)\}" r"|\{\*(?P[^\s*{}]+)\}" ) @@ -30,6 +33,39 @@ LoopMarkerPattern = re.compile( ) +def _CompileClipPattern(Pattern: str) -> re.Pattern: + RegexParts = [] + LastIndex = len(Pattern) - 1 + + for Index, Char in enumerate(Pattern): + IsLast = Index == LastIndex + if Char == "!": + RegexParts.append("(.*)" if IsLast else "(.*?)") + elif Char == "?": + RegexParts.append(".*" if IsLast else ".*?") + else: + RegexParts.append(re.escape(Char)) + + if not Pattern or Pattern[-1] not in "!?": + RegexParts.append(".*") + + return re.compile("".join(RegexParts), re.DOTALL) + + +def _ApplyClip(Value: str, Pattern: str) -> str: + Pattern = Pattern.strip() + + if not Pattern: + return Value + + Match = _CompileClipPattern(Pattern).match(Value) + + if Match is None: + return Value + + return "".join(Match.groups()) + + def _LineBounds(Content: str, Position: int) -> tuple[int, int]: LineStart = Content.rfind("\n", 0, Position) + 1 LineEnd = Content.find("\n", Position) @@ -206,9 +242,25 @@ def _ResolveLoops( CloseMatch = _FindLoopClose(Matches, Index + 1, IsFixed, Key) BodyEnd = CloseMatch.start() if CloseMatch is not None else len(Content) BodyTemplate = Content[Match.end() : BodyEnd] - VariablePattern = re.compile(rf"%{re.escape(LoopVar)}\b") + EscapedVar = re.escape(LoopVar) + VariablePattern = re.compile( + r"\{%" + EscapedVar + r"\s*\\\s*(?P[^{}]*)\}" + r"|%" + EscapedVar + r"\b" + ) - Rendered = "\n".join(VariablePattern.sub(Item, BodyTemplate) for Item in Items) + RenderedItems = [] + for Item in Items: + ItemRendered = VariablePattern.sub( + lambda VariableMatch, CurrentItem=Item: ( + _ApplyClip(CurrentItem, VariableMatch.group("Clip")) + if VariableMatch.group("Clip") is not None + else CurrentItem + ), + BodyTemplate, + ) + RenderedItems.append(ItemRendered) + + Rendered = "\n".join(RenderedItems) Output.append(Content[Cursor : Match.start()]) Output.append(Rendered) @@ -383,6 +435,8 @@ def ResolvePortals( Replacement = _ApplyPipe( Replacement, Match.group("CrossPipe"), int(Match.group("CrossPipeCount")) ) + if Replacement is not None and Match.group("CrossClip"): + Replacement = _ApplyClip(Replacement, Match.group("CrossClip")) elif Match.group("PipeMode"): Replacement = _ResolveWholeFilePipe( RootName, @@ -393,6 +447,8 @@ def ResolvePortals( Username, UserGroups, ) + if Replacement is not None and Match.group("PipeClip"): + Replacement = _ApplyClip(Replacement, Match.group("PipeClip")) elif Match.group("LineNumber"): Replacement = _ResolveLineNumberReference( RootName, @@ -402,6 +458,8 @@ def ResolvePortals( Username, UserGroups, ) + if Replacement is not None and Match.group("LineClip"): + Replacement = _ApplyClip(Replacement, Match.group("LineClip")) if Replacement is not None: Output.append(LocallyResolved[Cursor : Match.start()])