github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/strings.go (about) 1 package main 2 3 import ( 4 "strings" 5 "unicode" 6 "unicode/utf8" 7 ) 8 9 // hasAnyPrefixWord checks if the given line is prefixed with any one of the given words 10 func hasAnyPrefixWord(line string, wordList []string) bool { 11 for _, word := range wordList { 12 if strings.HasPrefix(line, word+" ") { 13 return true 14 } 15 } 16 return false 17 } 18 19 // hasAnyPrefix checks if the given line is prefixed with any one of the given strings 20 func hasAnyPrefix(line string, stringList []string) bool { 21 for _, s := range stringList { 22 if strings.HasPrefix(line, s) { 23 return true 24 } 25 } 26 return false 27 } 28 29 // hasS checks if the given string slice contains the given string 30 func hasS(sl []string, s string) bool { 31 for _, e := range sl { 32 if e == s { 33 return true 34 } 35 } 36 return false 37 } 38 39 // firstWordContainsOneOf checks if the first word of the given string contains 40 // any one of the given strings 41 func firstWordContainsOneOf(s string, sl []string) bool { 42 if s == "" { 43 return false 44 } 45 fields := strings.Fields(s) 46 if len(fields) == 0 { 47 return false 48 } 49 firstWord := fields[0] 50 for _, e := range sl { 51 if strings.Contains(firstWord, e) { 52 return true 53 } 54 } 55 return false 56 } 57 58 // hasSuffix checks if the given string end with one of the given suffixes 59 func hasSuffix(s string, suffixes []string) bool { 60 for _, suffix := range suffixes { 61 if strings.HasSuffix(s, suffix) { 62 return true 63 } 64 } 65 return false 66 } 67 68 // hasKey checks if the given string map contains the given key 69 func hasKey(m map[string]string, key string) bool { 70 _, found := m[key] 71 return found 72 } 73 74 // filterS returns all strings that makes the function f return true 75 func filterS(sl []string, f func(string) bool) []string { 76 var results []string 77 for _, e := range sl { 78 if f(e) { 79 results = append(results, e) 80 } 81 } 82 return results 83 } 84 85 // equalStringSlices checks if two given string slices are equal or not 86 // returns true if they are equal 87 func equalStringSlices(a, b []string) bool { 88 lena := len(a) 89 if lena != len(b) { 90 return false 91 } 92 for i := 0; i < lena; i++ { 93 if a[i] != b[i] { 94 return false 95 } 96 } 97 return true 98 } 99 100 // hasWords checks if a range of more than one letter is found 101 func hasWords(s string) bool { 102 letterCount := 0 103 for _, r := range s { 104 if unicode.IsLetter(r) { 105 letterCount++ 106 } else { 107 letterCount = 0 108 } 109 if letterCount > 1 { 110 return true 111 } 112 } 113 return false 114 } 115 116 // allUpper checks if all letters in a string are uppercase 117 func allUpper(s string) bool { 118 for _, r := range s { 119 if !unicode.IsUpper(r) && unicode.IsLetter(r) { 120 return false 121 } 122 } 123 return true 124 } 125 126 // allLower checks if all letters in a string are lowercase 127 func allLower(s string) bool { 128 for _, r := range s { 129 if unicode.IsLetter(r) && !unicode.IsLower(r) { 130 return false 131 } 132 } 133 return true 134 } 135 136 // capitalizeWords can change "john bob" to "John Bob" 137 func capitalizeWords(s string) string { 138 words := strings.Fields(s) 139 var newWords []string 140 for _, word := range words { 141 if len(word) > 1 { 142 capitalizedWord := strings.ToUpper(string(word[0])) + word[1:] 143 newWords = append(newWords, capitalizedWord) 144 } else { 145 newWords = append(newWords, word) 146 } 147 } 148 return strings.Join(newWords, " ") 149 } 150 151 // onlyAZaz checks if the given string only contains letters a-z and A-Z 152 func onlyAZaz(s string) bool { 153 for _, r := range s { 154 if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') { 155 return false 156 } 157 } 158 return true 159 } 160 161 // smartSplit will split a string on spaces, but only spaces that are not within [], () or {} 162 func smartSplit(s string) []string { 163 // Define constants for states. 164 const ( 165 Outside = iota 166 InParentheses 167 InBrackets 168 InBraces 169 ) 170 171 state := Outside 172 var result []string 173 var word strings.Builder 174 175 for _, ch := range s { 176 switch ch { 177 case '(': 178 if state == Outside { 179 state = InParentheses 180 } 181 word.WriteRune(ch) 182 case ')': 183 if state == InParentheses { 184 state = Outside 185 } 186 word.WriteRune(ch) 187 case '[': 188 if state == Outside { 189 state = InBrackets 190 } 191 word.WriteRune(ch) 192 case ']': 193 if state == InBrackets { 194 state = Outside 195 } 196 word.WriteRune(ch) 197 case '{': 198 if state == Outside { 199 state = InBraces 200 } 201 word.WriteRune(ch) 202 case '}': 203 if state == InBraces { 204 state = Outside 205 } 206 word.WriteRune(ch) 207 case ' ': 208 if state == Outside { 209 // Only split on space if outside of any brackets, braces, or parentheses. 210 result = append(result, word.String()) 211 word.Reset() 212 } else { 213 word.WriteRune(ch) 214 } 215 default: 216 word.WriteRune(ch) 217 } 218 } 219 220 // Append the last word. 221 if word.Len() > 0 { 222 result = append(result, word.String()) 223 } 224 225 return result 226 } 227 228 // isAllowedFilenameChar checks if the given rune is allowed in a typical cross-platform filename 229 func isAllowedFilenameChar(r rune) bool { 230 if unicode.IsLetter(r) || unicode.IsDigit(r) || isEmoji(r) { 231 return true 232 } 233 switch r { 234 case '.', ',', ':', '-', '+', '_', '/', '\\': 235 return true 236 default: 237 return false 238 } 239 } 240 241 // sanitizeFilename removes any character from the input string that is not part of a typical cross-platform filename 242 func sanitizeFilename(input string) string { 243 var result []rune 244 for _, r := range input { 245 if isAllowedFilenameChar(r) { 246 result = append(result, r) 247 } 248 } 249 return string(result) 250 } 251 252 // getLeadingWhitespace returns the leading whitespace of the given string 253 func getLeadingWhitespace(line string) string { 254 var whitespace []rune 255 for _, char := range line { 256 if unicode.IsSpace(char) { 257 whitespace = append(whitespace, char) 258 } else { 259 break 260 } 261 } 262 return string(whitespace) 263 } 264 265 func withinBackticks(line, what string) bool { 266 first := []rune(what)[0] 267 within := false 268 lineRunes := []rune(line) 269 whatRunes := []rune(what) 270 271 for i, r := range lineRunes { 272 if r == '`' { // ` 273 within = !within 274 continue 275 } 276 if within && r == first { 277 // check if the following runes also matches "what" 278 // if they do, return true 279 match := true 280 for whatIndex, whatRune := range whatRunes { 281 lineIndex := i + whatIndex 282 if lineIndex >= len(lineRunes) { 283 match = false 284 break 285 } 286 if lineRunes[lineIndex] != whatRune { 287 match = false 288 break 289 } 290 } 291 if match { 292 return true 293 } 294 } 295 } 296 return false 297 } 298 299 // isEmoji checks if a rune is likely to be an emoji. 300 func isEmoji(r rune) bool { 301 // Check if the rune falls within ranges that are likely to be used by emojis. 302 return unicode.Is(unicode.S, r) || // Symbols 303 unicode.Is(unicode.P, r) || // Punctuation 304 r >= utf8.RuneSelf // Emojis are typically multi-byte characters in UTF-8 305 } 306 307 // trimRightSpace trims space but only at the right side of a string 308 func trimRightSpace(str string) string { 309 return strings.TrimRightFunc(str, unicode.IsSpace) 310 }