From 599991ea731953f772824ce3ed1e591246aa004f Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 22 Jan 2018 15:32:50 -0600 Subject: PLT-3383: image proxy support (#7991) * image proxy support * go vet fix, remove mistakenly added coverage file * fix test compile error * add validation to config settings and documentation to model functions * add message_source field to post --- utils/markdown/blocks.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 utils/markdown/blocks.go (limited to 'utils/markdown/blocks.go') diff --git a/utils/markdown/blocks.go b/utils/markdown/blocks.go new file mode 100644 index 000000000..14972f943 --- /dev/null +++ b/utils/markdown/blocks.go @@ -0,0 +1,153 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package markdown + +import ( + "strings" +) + +type continuation struct { + Indentation int + Remaining Range +} + +type Block interface { + Continuation(indentation int, r Range) *continuation + AddLine(indentation int, r Range) bool + Close() + AllowsBlockStarts() bool + HasTrailingBlankLine() bool +} + +type blockBase struct{} + +func (*blockBase) AddLine(indentation int, r Range) bool { return false } +func (*blockBase) Close() {} +func (*blockBase) AllowsBlockStarts() bool { return true } +func (*blockBase) HasTrailingBlankLine() bool { return false } + +type ContainerBlock interface { + Block + AddChild(openBlocks []Block) []Block +} + +type Range struct { + Position int + End int +} + +func closeBlocks(blocks []Block, referenceDefinitions *[]*ReferenceDefinition) { + for _, block := range blocks { + block.Close() + if p, ok := block.(*Paragraph); ok && len(p.ReferenceDefinitions) > 0 { + *referenceDefinitions = append(*referenceDefinitions, p.ReferenceDefinitions...) + } + } +} + +func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefinition) { + document := &Document{} + var referenceDefinitions []*ReferenceDefinition + + openBlocks := []Block{document} + + for _, line := range lines { + r := line.Range + lastMatchIndex := 0 + + indentation, indentationBytes := countIndentation(markdown, r) + r = Range{r.Position + indentationBytes, r.End} + + for i, block := range openBlocks { + if continuation := block.Continuation(indentation, r); continuation != nil { + indentation = continuation.Indentation + r = continuation.Remaining + additionalIndentation, additionalIndentationBytes := countIndentation(markdown, r) + r = Range{r.Position + additionalIndentationBytes, r.End} + indentation += additionalIndentation + lastMatchIndex = i + } else { + break + } + } + + if openBlocks[lastMatchIndex].AllowsBlockStarts() { + if newBlocks := blockStart(markdown, indentation, r, openBlocks[:lastMatchIndex+1], openBlocks[lastMatchIndex+1:]); newBlocks != nil { + didAdd := false + for i := lastMatchIndex; i >= 0; i-- { + if container, ok := openBlocks[i].(ContainerBlock); ok { + if newBlocks := container.AddChild(newBlocks); newBlocks != nil { + closeBlocks(openBlocks[i+1:], &referenceDefinitions) + openBlocks = openBlocks[:i+1] + openBlocks = append(openBlocks, newBlocks...) + didAdd = true + break + } + } + } + if didAdd { + continue + } + } + } + + isBlank := strings.TrimSpace(markdown[r.Position:r.End]) == "" + if paragraph, ok := openBlocks[len(openBlocks)-1].(*Paragraph); ok && !isBlank { + paragraph.Text = append(paragraph.Text, r) + continue + } + + closeBlocks(openBlocks[lastMatchIndex+1:], &referenceDefinitions) + openBlocks = openBlocks[:lastMatchIndex+1] + + if openBlocks[lastMatchIndex].AddLine(indentation, r) { + continue + } + + if paragraph := newParagraph(markdown, r); paragraph != nil { + for i := lastMatchIndex; i >= 0; i-- { + if container, ok := openBlocks[i].(ContainerBlock); ok { + if newBlocks := container.AddChild([]Block{paragraph}); newBlocks != nil { + closeBlocks(openBlocks[i+1:], &referenceDefinitions) + openBlocks = openBlocks[:i+1] + openBlocks = append(openBlocks, newBlocks...) + break + } + } + } + } + } + + closeBlocks(openBlocks, &referenceDefinitions) + + return document, referenceDefinitions +} + +func blockStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { + if r.Position >= r.End { + return nil + } + + if start := blockQuoteStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { + return start + } else if start := listStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { + return start + } else if start := indentedCodeStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { + return start + } else if start := fencedCodeStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { + return start + } + + return nil +} + +func blockStartOrParagraph(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { + if start := blockStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { + return start + } + if paragraph := newParagraph(markdown, r); paragraph != nil { + return []Block{paragraph} + } + return nil +} -- cgit v1.2.3-1-g7c22