Add NameTrie for predictable, correct name autocomplete

This commit is contained in:
Jesse Shapiro 2016-11-12 17:40:06 -05:00
parent 55c1def24d
commit 56cd155e4f
4 changed files with 98 additions and 7 deletions

View File

@ -232,3 +232,8 @@ func (r *Room) NamesPrefix(prefix string) []string {
} }
return names return names
} }
func (r *Room) CompleteName(prefix string) string {
name, _ := r.Members.CompleteName(prefix)
return name
}

View File

@ -192,13 +192,7 @@ func (h *Host) Serve() {
} }
func (h *Host) completeName(partial string) string { func (h *Host) completeName(partial string) string {
names := h.NamesPrefix(partial) return h.CompleteName(partial)
if len(names) == 0 {
// Didn't find anything
return ""
}
return names[len(names)-1]
} }
func (h *Host) completeCommand(partial string) string { func (h *Host) completeCommand(partial string) string {

83
set/name_trie.go Normal file
View File

@ -0,0 +1,83 @@
package set
import (
"fmt"
"sort"
)
type NameTrie struct {
children map[rune]*NameTrie
terminates bool
}
func createNameTrie() *NameTrie {
return &NameTrie{make(map[rune]*NameTrie), false}
}
func (tree *NameTrie) Exists(name string) bool {
nameSlice := []rune(name)
node, ok := tree.traverse(nameSlice, false)
return ok && node.terminates
}
func (tree *NameTrie) Insert(name string) {
nameSlice := []rune(name)
node, ok := tree.traverse(nameSlice, true)
if ok {
node.terminates = true
}
}
func (tree *NameTrie) Delete(name string) {
nameSlice := []rune(name)
node, ok := tree.traverse(nameSlice, false)
if ok {
node.terminates = false
}
}
func (tree *NameTrie) ClosestName(prefix string) (name string, ok bool) {
fmt.Println(tree, prefix)
nameslice := []rune(prefix)
node, ok := tree.traverse(nameslice, false)
if !ok {
return "", false
}
return node.closestName(prefix)
}
func (node *NameTrie) closestName(prefix string) (name string, ok bool) {
if node.terminates {
return prefix, true
}
keys := []string{}
for suffix := range(node.children){keys = append(keys, string(suffix))}
sort.Strings(keys)
fmt.Println(keys)
for i := range keys {
child := node.children[[]rune(keys[i])[0]]
name, ok := child.closestName(prefix + keys[i])
if ok{
return name, true
}
}
return "", false
}
func (tree *NameTrie) traverse(remainder []rune, create bool) (nextTree *NameTrie, ok bool) {
nextRune := remainder[0]
nextTree, ok = tree.children[nextRune]
if !ok {
if create {
tree.children[nextRune] = createNameTrie()
nextTree = tree.children[nextRune]
} else {
return createNameTrie(), false
}
}
if len(remainder) < 2 {
return nextTree, true
} else {
return nextTree.traverse(remainder[1:], create)
}
}

View File

@ -21,6 +21,7 @@ type Set struct {
sync.RWMutex sync.RWMutex
lookup map[string]Item lookup map[string]Item
normalize func(string) string normalize func(string) string
names *NameTrie
} }
// New creates a new set with case-insensitive keys // New creates a new set with case-insensitive keys
@ -28,6 +29,7 @@ func New() *Set {
return &Set{ return &Set{
lookup: map[string]Item{}, lookup: map[string]Item{},
normalize: normalize, normalize: normalize,
names: createNameTrie(),
} }
} }
@ -36,6 +38,7 @@ func (s *Set) Clear() int {
s.Lock() s.Lock()
n := len(s.lookup) n := len(s.lookup)
s.lookup = map[string]Item{} s.lookup = map[string]Item{}
s.names = createNameTrie()
s.Unlock() s.Unlock()
return n return n
} }
@ -102,6 +105,7 @@ func (s *Set) Add(item Item) error {
} }
s.lookup[key] = item s.lookup[key] = item
s.names.Insert(key)
return nil return nil
} }
@ -117,6 +121,7 @@ func (s *Set) Remove(key string) error {
return ErrMissing return ErrMissing
} }
delete(s.lookup, key) delete(s.lookup, key)
s.names.Delete(key)
return nil return nil
} }
@ -187,6 +192,10 @@ func (s *Set) ListPrefix(prefix string) []Item {
return r return r
} }
func (s *Set) CompleteName(name string) (string, bool) {
return s.names.ClosestName(name)
}
func normalize(key string) string { func normalize(key string) string {
return strings.ToLower(key) return strings.ToLower(key)
} }