Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

82 lines
2.0 KiB
Go

package search
import (
"strings"
"unicode"
)
// Tokenize splits a string into searchable tokens by:
// - camelCase boundaries (getUserById → [get, user, by, id])
// - snake_case boundaries (get_user_by_id → [get, user, by, id])
// - path separators (internal/auth/token.go → [internal, auth, token, go])
// - dot separators (UserService.FindUser → [user, service, find, user])
//
// All tokens are lowercased. Tokens shorter than 2 characters are dropped.
func Tokenize(s string) []string {
var tokens []string
var current []rune
flush := func() {
if len(current) >= 2 {
tokens = append(tokens, strings.ToLower(string(current)))
}
current = current[:0]
}
runes := []rune(s)
for i, r := range runes {
switch {
case r == '_' || r == '/' || r == '.' || r == ':' || r == '-' || r == ' ':
flush()
case unicode.IsUpper(r):
if len(current) > 0 {
prev := current[len(current)-1]
// camelCase: lowercase followed by uppercase → split
if unicode.IsLower(prev) {
flush()
}
// ALLCAPS followed by uppercase+lowercase → split before the last uppercase
// e.g. HTMLParser: H-T-M-L-P → flush "HTML" before "P"
if unicode.IsUpper(prev) && i+1 < len(runes) && unicode.IsLower(runes[i+1]) {
flush()
}
}
current = append(current, r)
case unicode.IsLetter(r) || unicode.IsDigit(r):
current = append(current, r)
default:
flush()
}
}
flush()
if tokens == nil {
tokens = []string{}
}
return tokens
}
// TokenizeQuery tokenizes a search query, keeping all tokens (including short ones
// that might be meaningful like "go" or "js").
func TokenizeQuery(s string) []string {
var tokens []string
var current []rune
flush := func() {
if len(current) > 0 {
tokens = append(tokens, strings.ToLower(string(current)))
}
current = current[:0]
}
for _, r := range s {
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
current = append(current, r)
} else {
flush()
}
}
flush()
return tokens
}