github.com/levb/mattermost-server@v5.3.1+incompatible/utils/markdown/fenced_code.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package markdown 5 6 import ( 7 "strings" 8 ) 9 10 type FencedCodeLine struct { 11 Indentation int 12 Range Range 13 } 14 15 type FencedCode struct { 16 blockBase 17 markdown string 18 didSeeClosingFence bool 19 20 Indentation int 21 OpeningFence Range 22 RawInfo Range 23 RawCode []FencedCodeLine 24 } 25 26 func (b *FencedCode) Code() (result string) { 27 for _, code := range b.RawCode { 28 result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End] 29 } 30 return 31 } 32 33 func (b *FencedCode) Info() string { 34 return Unescape(b.markdown[b.RawInfo.Position:b.RawInfo.End]) 35 } 36 37 func (b *FencedCode) Continuation(indentation int, r Range) *continuation { 38 if b.didSeeClosingFence { 39 return nil 40 } 41 return &continuation{ 42 Indentation: indentation, 43 Remaining: r, 44 } 45 } 46 47 func (b *FencedCode) AddLine(indentation int, r Range) bool { 48 s := b.markdown[r.Position:r.End] 49 if indentation <= 3 && strings.HasPrefix(s, b.markdown[b.OpeningFence.Position:b.OpeningFence.End]) { 50 suffix := strings.TrimSpace(s[b.OpeningFence.End-b.OpeningFence.Position:]) 51 isClosingFence := true 52 for _, c := range suffix { 53 if c != rune(s[0]) { 54 isClosingFence = false 55 break 56 } 57 } 58 if isClosingFence { 59 b.didSeeClosingFence = true 60 return true 61 } 62 } 63 64 if indentation >= b.Indentation { 65 indentation -= b.Indentation 66 } else { 67 indentation = 0 68 } 69 70 b.RawCode = append(b.RawCode, FencedCodeLine{ 71 Indentation: indentation, 72 Range: r, 73 }) 74 return true 75 } 76 77 func (b *FencedCode) AllowsBlockStarts() bool { 78 return false 79 } 80 81 func fencedCodeStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { 82 s := markdown[r.Position:r.End] 83 84 if !strings.HasPrefix(s, "```") && !strings.HasPrefix(s, "~~~") { 85 return nil 86 } 87 88 fenceCharacter := rune(s[0]) 89 fenceLength := 3 90 for _, c := range s[3:] { 91 if c == fenceCharacter { 92 fenceLength++ 93 } else { 94 break 95 } 96 } 97 98 for i := r.Position + fenceLength; i < r.End; i++ { 99 if markdown[i] == '`' { 100 return nil 101 } 102 } 103 104 return []Block{ 105 &FencedCode{ 106 markdown: markdown, 107 Indentation: indentation, 108 RawInfo: trimRightSpace(markdown, Range{r.Position + fenceLength, r.End}), 109 OpeningFence: Range{r.Position, r.Position + fenceLength}, 110 }, 111 } 112 }