github.com/fretkak/mattermost-mattermost-server@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 . 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 }