github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+incompatible/utils/markdown/markdown.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  // This package implements a parser for the subset of the CommonMark spec necessary for us to do
     5  // server-side processing. It is not a full implementation and lacks many features. But it is
     6  // complete enough to efficiently and accurately allow us to do what we need to like rewrite image
     7  // URLs for proxying.
     8  package markdown
     9  
    10  import (
    11  	"strings"
    12  )
    13  
    14  func isEscapable(c rune) bool {
    15  	return c > ' ' && (c < '0' || (c > '9' && (c < 'A' || (c > 'Z' && (c < 'a' || (c > 'z' && c <= '~'))))))
    16  }
    17  
    18  func isEscapableByte(c byte) bool {
    19  	return isEscapable(rune(c))
    20  }
    21  
    22  func isWhitespace(c rune) bool {
    23  	switch c {
    24  	case ' ', '\t', '\n', '\u000b', '\u000c', '\r':
    25  		return true
    26  	default:
    27  		return false
    28  	}
    29  }
    30  
    31  func isWhitespaceByte(c byte) bool {
    32  	return isWhitespace(rune(c))
    33  }
    34  
    35  func isHex(c rune) bool {
    36  	return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
    37  }
    38  
    39  func isHexByte(c byte) bool {
    40  	return isHex(rune(c))
    41  }
    42  
    43  func nextNonWhitespace(markdown string, position int) int {
    44  	for offset, c := range []byte(markdown[position:]) {
    45  		if !isWhitespaceByte(c) {
    46  			return position + offset
    47  		}
    48  	}
    49  	return len(markdown)
    50  }
    51  
    52  func nextLine(markdown string, position int) (linePosition int, skippedNonWhitespace bool) {
    53  	for i := position; i < len(markdown); i++ {
    54  		c := markdown[i]
    55  		if c == '\r' {
    56  			if i+1 < len(markdown) && markdown[i+1] == '\n' {
    57  				return i + 2, skippedNonWhitespace
    58  			}
    59  			return i + 1, skippedNonWhitespace
    60  		} else if c == '\n' {
    61  			return i + 1, skippedNonWhitespace
    62  		} else if !isWhitespaceByte(c) {
    63  			skippedNonWhitespace = true
    64  		}
    65  	}
    66  	return len(markdown), skippedNonWhitespace
    67  }
    68  
    69  func countIndentation(markdown string, r Range) (spaces, bytes int) {
    70  	for i := r.Position; i < r.End; i++ {
    71  		if markdown[i] == ' ' {
    72  			spaces++
    73  			bytes++
    74  		} else if markdown[i] == '\t' {
    75  			spaces += 4
    76  			bytes++
    77  		} else {
    78  			break
    79  		}
    80  	}
    81  	return
    82  }
    83  
    84  func trimLeftSpace(markdown string, r Range) Range {
    85  	s := markdown[r.Position:r.End]
    86  	trimmed := strings.TrimLeftFunc(s, isWhitespace)
    87  	return Range{r.Position, r.End - (len(s) - len(trimmed))}
    88  }
    89  
    90  func trimRightSpace(markdown string, r Range) Range {
    91  	s := markdown[r.Position:r.End]
    92  	trimmed := strings.TrimRightFunc(s, isWhitespace)
    93  	return Range{r.Position, r.End - (len(s) - len(trimmed))}
    94  }
    95  
    96  func relativeToAbsolutePosition(ranges []Range, position int) int {
    97  	rem := position
    98  	for _, r := range ranges {
    99  		l := r.End - r.Position
   100  		if rem < l {
   101  			return r.Position + rem
   102  		}
   103  		rem -= l
   104  	}
   105  	if len(ranges) == 0 {
   106  		return 0
   107  	}
   108  	return ranges[len(ranges)-1].End
   109  }
   110  
   111  func trimBytesFromRanges(ranges []Range, bytes int) (result []Range) {
   112  	rem := bytes
   113  	for _, r := range ranges {
   114  		if rem == 0 {
   115  			result = append(result, r)
   116  			continue
   117  		}
   118  		l := r.End - r.Position
   119  		if rem < l {
   120  			result = append(result, Range{r.Position + rem, r.End})
   121  			rem = 0
   122  			continue
   123  		}
   124  		rem -= l
   125  	}
   126  	return
   127  }
   128  
   129  func Parse(markdown string) (*Document, []*ReferenceDefinition) {
   130  	lines := ParseLines(markdown)
   131  	return ParseBlocks(markdown, lines)
   132  }