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.

utils.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package lint
  2. import (
  3. "strings"
  4. "unicode"
  5. )
  6. // Name returns a different name if it should be different.
  7. func Name(name string, whitelist, blacklist []string) (should string) {
  8. // Fast path for simple cases: "_" and all lowercase.
  9. if name == "_" {
  10. return name
  11. }
  12. allLower := true
  13. for _, r := range name {
  14. if !unicode.IsLower(r) {
  15. allLower = false
  16. break
  17. }
  18. }
  19. if allLower {
  20. return name
  21. }
  22. // Split camelCase at any lower->upper transition, and split on underscores.
  23. // Check each word for common initialisms.
  24. runes := []rune(name)
  25. w, i := 0, 0 // index of start of word, scan
  26. for i+1 <= len(runes) {
  27. eow := false // whether we hit the end of a word
  28. if i+1 == len(runes) {
  29. eow = true
  30. } else if runes[i+1] == '_' {
  31. // underscore; shift the remainder forward over any run of underscores
  32. eow = true
  33. n := 1
  34. for i+n+1 < len(runes) && runes[i+n+1] == '_' {
  35. n++
  36. }
  37. // Leave at most one underscore if the underscore is between two digits
  38. if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
  39. n--
  40. }
  41. copy(runes[i+1:], runes[i+n+1:])
  42. runes = runes[:len(runes)-n]
  43. } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
  44. // lower->non-lower
  45. eow = true
  46. }
  47. i++
  48. if !eow {
  49. continue
  50. }
  51. // [w,i) is a word.
  52. word := string(runes[w:i])
  53. ignoreInitWarnings := map[string]bool{}
  54. for _, i := range whitelist {
  55. ignoreInitWarnings[i] = true
  56. }
  57. extraInits := map[string]bool{}
  58. for _, i := range blacklist {
  59. extraInits[i] = true
  60. }
  61. if u := strings.ToUpper(word); (commonInitialisms[u] || extraInits[u]) && !ignoreInitWarnings[u] {
  62. // Keep consistent case, which is lowercase only at the start.
  63. if w == 0 && unicode.IsLower(runes[w]) {
  64. u = strings.ToLower(u)
  65. }
  66. // All the common initialisms are ASCII,
  67. // so we can replace the bytes exactly.
  68. copy(runes[w:], []rune(u))
  69. } else if w > 0 && strings.ToLower(word) == word {
  70. // already all lowercase, and not the first word, so uppercase the first character.
  71. runes[w] = unicode.ToUpper(runes[w])
  72. }
  73. w = i
  74. }
  75. return string(runes)
  76. }
  77. // commonInitialisms is a set of common initialisms.
  78. // Only add entries that are highly unlikely to be non-initialisms.
  79. // For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
  80. var commonInitialisms = map[string]bool{
  81. "ACL": true,
  82. "API": true,
  83. "ASCII": true,
  84. "CPU": true,
  85. "CSS": true,
  86. "DNS": true,
  87. "EOF": true,
  88. "GUID": true,
  89. "HTML": true,
  90. "HTTP": true,
  91. "HTTPS": true,
  92. "ID": true,
  93. "IP": true,
  94. "JSON": true,
  95. "LHS": true,
  96. "QPS": true,
  97. "RAM": true,
  98. "RHS": true,
  99. "RPC": true,
  100. "SLA": true,
  101. "SMTP": true,
  102. "SQL": true,
  103. "SSH": true,
  104. "TCP": true,
  105. "TLS": true,
  106. "TTL": true,
  107. "UDP": true,
  108. "UI": true,
  109. "UID": true,
  110. "UUID": true,
  111. "URI": true,
  112. "URL": true,
  113. "UTF8": true,
  114. "VM": true,
  115. "XML": true,
  116. "XMPP": true,
  117. "XSRF": true,
  118. "XSS": true,
  119. }