github.com/mad-app/mattermost-server@v5.11.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  }