github.com/vnforks/kid@v5.11.1+incompatible/utils/markdown/links.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  	"unicode/utf8"
     8  )
     9  
    10  func parseLinkDestination(markdown string, position int) (raw Range, next int, ok bool) {
    11  	if position >= len(markdown) {
    12  		return
    13  	}
    14  
    15  	if markdown[position] == '<' {
    16  		isEscaped := false
    17  
    18  		for offset, c := range []byte(markdown[position+1:]) {
    19  			if isEscaped {
    20  				isEscaped = false
    21  				if isEscapableByte(c) {
    22  					continue
    23  				}
    24  			}
    25  
    26  			if c == '\\' {
    27  				isEscaped = true
    28  			} else if c == '<' {
    29  				break
    30  			} else if c == '>' {
    31  				return Range{position + 1, position + 1 + offset}, position + 1 + offset + 1, true
    32  			} else if isWhitespaceByte(c) {
    33  				break
    34  			}
    35  		}
    36  	}
    37  
    38  	openCount := 0
    39  	isEscaped := false
    40  	for offset, c := range []byte(markdown[position:]) {
    41  		if isEscaped {
    42  			isEscaped = false
    43  			if isEscapableByte(c) {
    44  				continue
    45  			}
    46  		}
    47  
    48  		switch c {
    49  		case '\\':
    50  			isEscaped = true
    51  		case '(':
    52  			openCount++
    53  		case ')':
    54  			if openCount < 1 {
    55  				return Range{position, position + offset}, position + offset, true
    56  			}
    57  			openCount--
    58  		default:
    59  			if isWhitespaceByte(c) {
    60  				return Range{position, position + offset}, position + offset, true
    61  			}
    62  		}
    63  	}
    64  	return Range{position, len(markdown)}, len(markdown), true
    65  }
    66  
    67  func parseLinkTitle(markdown string, position int) (raw Range, next int, ok bool) {
    68  	if position >= len(markdown) {
    69  		return
    70  	}
    71  
    72  	originalPosition := position
    73  
    74  	var closer byte
    75  	switch markdown[position] {
    76  	case '"', '\'':
    77  		closer = markdown[position]
    78  	case '(':
    79  		closer = ')'
    80  	default:
    81  		return
    82  	}
    83  	position++
    84  
    85  	for position < len(markdown) {
    86  		switch markdown[position] {
    87  		case '\\':
    88  			position++
    89  			if position < len(markdown) && isEscapableByte(markdown[position]) {
    90  				position++
    91  			}
    92  		case closer:
    93  			return Range{originalPosition + 1, position}, position + 1, true
    94  		default:
    95  			position++
    96  		}
    97  	}
    98  
    99  	return
   100  }
   101  
   102  func parseLinkLabel(markdown string, position int) (raw Range, next int, ok bool) {
   103  	if position >= len(markdown) || markdown[position] != '[' {
   104  		return
   105  	}
   106  
   107  	originalPosition := position
   108  	position++
   109  
   110  	for position < len(markdown) {
   111  		switch markdown[position] {
   112  		case '\\':
   113  			position++
   114  			if position < len(markdown) && isEscapableByte(markdown[position]) {
   115  				position++
   116  			}
   117  		case '[':
   118  			return
   119  		case ']':
   120  			if position-originalPosition >= 1000 && utf8.RuneCountInString(markdown[originalPosition:position]) >= 1000 {
   121  				return
   122  			}
   123  			return Range{originalPosition + 1, position}, position + 1, true
   124  		default:
   125  			position++
   126  		}
   127  	}
   128  
   129  	return
   130  }
   131  
   132  // As a non-standard feature, we allow image links to specify dimensions of the image by adding "=WIDTHxHEIGHT"
   133  // after the image destination but before the image title like ![alt](http://example.com/image.png =100x200 "title").
   134  // Both width and height are optional, but at least one of them must be specified.
   135  func parseImageDimensions(markdown string, position int) (raw Range, next int, ok bool) {
   136  	if position >= len(markdown) {
   137  		return
   138  	}
   139  
   140  	originalPosition := position
   141  
   142  	// Read =
   143  	position += 1
   144  	if position >= len(markdown) {
   145  		return
   146  	}
   147  
   148  	// Read width
   149  	hasWidth := false
   150  	for isNumericByte(markdown[position]) {
   151  		hasWidth = true
   152  		position += 1
   153  	}
   154  
   155  	// Look for early end of dimensions
   156  	if isWhitespaceByte(markdown[position]) || markdown[position] == ')' {
   157  		return Range{originalPosition, position - 1}, position, true
   158  	}
   159  
   160  	// Read the x
   161  	if markdown[position] != 'x' && markdown[position] != 'X' {
   162  		return
   163  	}
   164  	position += 1
   165  
   166  	// Read height
   167  	hasHeight := false
   168  	for isNumericByte(markdown[position]) {
   169  		hasHeight = true
   170  		position += 1
   171  	}
   172  
   173  	// Make sure the there's no trailing characters
   174  	if !isWhitespaceByte(markdown[position]) && markdown[position] != ')' {
   175  		return
   176  	}
   177  
   178  	if !hasWidth && !hasHeight {
   179  		// At least one of width or height is required
   180  		return
   181  	}
   182  
   183  	return Range{originalPosition, position - 1}, position, true
   184  }