You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

string.go 2.0KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package util
  5. import "github.com/yuin/goldmark/util"
  6. func isSnakeCaseUpper(c byte) bool {
  7. return 'A' <= c && c <= 'Z'
  8. }
  9. func isSnakeCaseLowerOrNumber(c byte) bool {
  10. return 'a' <= c && c <= 'z' || '0' <= c && c <= '9'
  11. }
  12. // ToSnakeCase convert the input string to snake_case format.
  13. //
  14. // Some samples.
  15. // "FirstName" => "first_name"
  16. // "HTTPServer" => "http_server"
  17. // "NoHTTPS" => "no_https"
  18. // "GO_PATH" => "go_path"
  19. // "GO PATH" => "go_path" // space is converted to underscore.
  20. // "GO-PATH" => "go_path" // hyphen is converted to underscore.
  21. //
  22. func ToSnakeCase(input string) string {
  23. if len(input) == 0 {
  24. return ""
  25. }
  26. var res []byte
  27. if len(input) == 1 {
  28. c := input[0]
  29. if isSnakeCaseUpper(c) {
  30. res = []byte{c + 'a' - 'A'}
  31. } else if isSnakeCaseLowerOrNumber(c) {
  32. res = []byte{c}
  33. } else {
  34. res = []byte{'_'}
  35. }
  36. } else {
  37. res = make([]byte, 0, len(input)*4/3)
  38. pos := 0
  39. needSep := false
  40. for pos < len(input) {
  41. c := input[pos]
  42. if c >= 0x80 {
  43. res = append(res, c)
  44. pos++
  45. continue
  46. }
  47. isUpper := isSnakeCaseUpper(c)
  48. if isUpper || isSnakeCaseLowerOrNumber(c) {
  49. end := pos + 1
  50. if isUpper {
  51. // skip the following upper letters
  52. for end < len(input) && isSnakeCaseUpper(input[end]) {
  53. end++
  54. }
  55. if end-pos > 1 && end < len(input) && isSnakeCaseLowerOrNumber(input[end]) {
  56. end--
  57. }
  58. }
  59. // skip the following lower or number letters
  60. for end < len(input) && (isSnakeCaseLowerOrNumber(input[end]) || input[end] >= 0x80) {
  61. end++
  62. }
  63. if needSep {
  64. res = append(res, '_')
  65. }
  66. res = append(res, input[pos:end]...)
  67. pos = end
  68. needSep = true
  69. } else {
  70. res = append(res, '_')
  71. pos++
  72. needSep = false
  73. }
  74. }
  75. for i := 0; i < len(res); i++ {
  76. if isSnakeCaseUpper(res[i]) {
  77. res[i] += 'a' - 'A'
  78. }
  79. }
  80. }
  81. return util.BytesToReadOnlyString(res)
  82. }