vendor tsgo
This commit is contained in:
186
tools/tsgo/internal/ls/autoimport/index.go
Normal file
186
tools/tsgo/internal/ls/autoimport/index.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package autoimport
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
)
|
||||
|
||||
// Named is a constraint for types that can provide their name.
|
||||
type Named interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// Index stores entries with an index mapping uppercase letters to entries whose name
|
||||
// starts with that letter, and lowercase letters to entries whose name contains a
|
||||
// word starting with that letter.
|
||||
type Index[T Named] struct {
|
||||
entries []T
|
||||
index map[rune][]int
|
||||
}
|
||||
|
||||
func (idx *Index[T]) Find(name string, caseSensitive bool) []T {
|
||||
if len(idx.entries) == 0 || len(name) == 0 {
|
||||
return nil
|
||||
}
|
||||
firstRune := core.FirstResult(utf8.DecodeRuneInString(name))
|
||||
if firstRune == utf8.RuneError {
|
||||
return nil
|
||||
}
|
||||
firstRuneUpper := unicode.ToUpper(firstRune)
|
||||
candidates, ok := idx.index[firstRuneUpper]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var results []T
|
||||
for _, entryIndex := range candidates {
|
||||
entry := idx.entries[entryIndex]
|
||||
entryName := entry.Name()
|
||||
if (caseSensitive && entryName == name) || (!caseSensitive && strings.EqualFold(entryName, name)) {
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// SearchWordPrefix returns each entry whose name contains a word beginning with
|
||||
// the first character of 'prefix', and whose name contains all characters
|
||||
// of 'prefix' in order (case-insensitive). If 'filter' is provided, only entries
|
||||
// for which filter(entry) returns true are included.
|
||||
func (idx *Index[T]) SearchWordPrefix(prefix string) []T {
|
||||
if len(idx.entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(prefix) == 0 {
|
||||
return idx.entries
|
||||
}
|
||||
|
||||
prefix = strings.ToLower(prefix)
|
||||
firstRune, _ := utf8.DecodeRuneInString(prefix)
|
||||
if firstRune == utf8.RuneError {
|
||||
return nil
|
||||
}
|
||||
|
||||
firstRuneUpper := unicode.ToUpper(firstRune)
|
||||
firstRuneLower := unicode.ToLower(firstRune)
|
||||
|
||||
// Look up entries that have words starting with this letter
|
||||
var wordStarts []int
|
||||
nameStarts, _ := idx.index[firstRuneUpper]
|
||||
if firstRuneUpper != firstRuneLower {
|
||||
wordStarts, _ = idx.index[firstRuneLower]
|
||||
}
|
||||
count := len(nameStarts) + len(wordStarts)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter entries by checking if they contain all characters in order
|
||||
results := make([]T, 0, count)
|
||||
for _, starts := range [][]int{nameStarts, wordStarts} {
|
||||
for _, i := range starts {
|
||||
entry := idx.entries[i]
|
||||
if containsCharsInOrder(entry.Name(), prefix) {
|
||||
results = append(results, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// containsCharsInOrder checks if str contains all characters from pattern in order (case-insensitive).
|
||||
func containsCharsInOrder(str, pattern string) bool {
|
||||
str = strings.ToLower(str)
|
||||
pattern = strings.ToLower(pattern)
|
||||
|
||||
patternIdx := 0
|
||||
for _, ch := range str {
|
||||
if patternIdx < len(pattern) {
|
||||
patternRune, size := utf8.DecodeRuneInString(pattern[patternIdx:])
|
||||
if ch == patternRune {
|
||||
patternIdx += size
|
||||
}
|
||||
}
|
||||
}
|
||||
return patternIdx == len(pattern)
|
||||
}
|
||||
|
||||
// insertAsWords adds a value to the index keyed by the first letter of each word in its name.
|
||||
func (idx *Index[T]) insertAsWords(value T) {
|
||||
if idx.index == nil {
|
||||
idx.index = make(map[rune][]int)
|
||||
}
|
||||
|
||||
name := value.Name()
|
||||
if len(name) == 0 {
|
||||
panic("Cannot index entry with empty name")
|
||||
}
|
||||
entryIndex := len(idx.entries)
|
||||
idx.entries = append(idx.entries, value)
|
||||
|
||||
indices := wordIndices(name)
|
||||
seenRunes := make(map[rune]bool)
|
||||
|
||||
for i, start := range indices {
|
||||
substr := name[start:]
|
||||
firstRune, _ := utf8.DecodeRuneInString(substr)
|
||||
if firstRune == utf8.RuneError {
|
||||
continue
|
||||
}
|
||||
if i == 0 {
|
||||
// Name start keyed by uppercase
|
||||
firstRune = unicode.ToUpper(firstRune)
|
||||
idx.index[firstRune] = append(idx.index[firstRune], entryIndex)
|
||||
seenRunes[firstRune] = true // (Still set seenRunes in case first character is non-alphabetic)
|
||||
} else {
|
||||
// Subsequent word starts keyed by lowercase
|
||||
firstRune = unicode.ToLower(firstRune)
|
||||
if !seenRunes[firstRune] {
|
||||
idx.index[firstRune] = append(idx.index[firstRune], entryIndex)
|
||||
seenRunes[firstRune] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a new Index containing only entries for which filter returns true.
|
||||
func (idx *Index[T]) Clone(filter func(T) bool) *Index[T] {
|
||||
if idx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
newIdx := &Index[T]{
|
||||
entries: make([]T, 0, len(idx.entries)),
|
||||
index: make(map[rune][]int, len(idx.index)),
|
||||
}
|
||||
|
||||
// Build mapping from old index to new index for filtered entries
|
||||
oldToNew := make(map[int]int, len(idx.entries))
|
||||
for oldIndex, entry := range idx.entries {
|
||||
if filter(entry) {
|
||||
newIndex := len(newIdx.entries)
|
||||
newIdx.entries = append(newIdx.entries, entry)
|
||||
oldToNew[oldIndex] = newIndex
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the index with remapped indices
|
||||
for r, oldIndices := range idx.index {
|
||||
newIndices := make([]int, 0, len(oldIndices))
|
||||
for _, oldIndex := range oldIndices {
|
||||
if newIndex, ok := oldToNew[oldIndex]; ok {
|
||||
newIndices = append(newIndices, newIndex)
|
||||
}
|
||||
}
|
||||
if len(newIndices) > 0 {
|
||||
newIdx.index[r] = newIndices
|
||||
}
|
||||
}
|
||||
|
||||
return newIdx
|
||||
}
|
||||
Reference in New Issue
Block a user