From 609ab9c765be14708cfb1d25520cbad7934c325b Mon Sep 17 00:00:00 2001 From: Vishal Date: Mon, 1 May 2023 19:53:18 +0530 Subject: [PATCH] [MM-52527] (#23197) * change recursive to iterative --------- Co-authored-by: Mattermost Build --- server/platform/shared/markdown/inspect.go | 108 ++++++++++++++------- 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/server/platform/shared/markdown/inspect.go b/server/platform/shared/markdown/inspect.go index 2c14412949..b3b5ead33d 100644 --- a/server/platform/shared/markdown/inspect.go +++ b/server/platform/shared/markdown/inspect.go @@ -26,53 +26,89 @@ func Inspect(markdown string, f func(any) bool) { // InspectBlock traverses the blocks in depth-first order, starting with block. If f returns true, // InspectBlock invokes f recursively for each child of the block, followed by a call of f(nil). func InspectBlock(block Block, f func(Block) bool) { - if !f(block) { - return - } - switch v := block.(type) { - case *Document: - for _, child := range v.Children { - InspectBlock(child, f) + stack := []Block{block} + // Using seen for backtracking + seen := map[Block]bool{} + + for len(stack) > 0 { + // "peek" the node from the stack + block := stack[len(stack)-1] + + if seen[block] { + // "pop" the node only when backtracking(seen) + stack = stack[:len(stack)-1] + f(nil) + continue } - case *List: - for _, child := range v.Children { - InspectBlock(child, f) + seen[block] = true + + // Process the node + if !f(block) { + continue } - case *ListItem: - for _, child := range v.Children { - InspectBlock(child, f) - } - case *BlockQuote: - for _, child := range v.Children { - InspectBlock(child, f) + + switch v := block.(type) { + case *Document: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *List: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *ListItem: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *BlockQuote: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } } } - f(nil) } // InspectInline traverses the blocks in depth-first order, starting with block. If f returns true, // InspectInline invokes f recursively for each child of the block, followed by a call of f(nil). func InspectInline(inline Inline, f func(Inline) bool) { - if !f(inline) { - return - } - switch v := inline.(type) { - case *InlineImage: - for _, child := range v.Children { - InspectInline(child, f) + stack := []Inline{inline} + // Using seen for backtracking + seen := map[Inline]bool{} + + for len(stack) > 0 { + // "peek" the node from the stack + inline := stack[len(stack)-1] + + if seen[inline] { + // "pop" the node only when backtracking(seen) + stack = stack[:len(stack)-1] + f(nil) + continue } - case *InlineLink: - for _, child := range v.Children { - InspectInline(child, f) + seen[inline] = true + + // Process the node + if !f(inline) { + continue } - case *ReferenceImage: - for _, child := range v.Children { - InspectInline(child, f) - } - case *ReferenceLink: - for _, child := range v.Children { - InspectInline(child, f) + + switch v := inline.(type) { + case *InlineImage: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *InlineLink: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *ReferenceImage: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } + case *ReferenceLink: + for i := len(v.Children) - 1; i >= 0; i-- { + stack = append(stack, v.Children[i]) + } } } - f(nil) }