diff --git a/chat/room.go b/chat/room.go index 7ce6de1..389ce89 100644 --- a/chat/room.go +++ b/chat/room.go @@ -232,3 +232,8 @@ func (r *Room) NamesPrefix(prefix string) []string { } return names } + +func (r *Room) CompleteName(prefix string) string { + name, _ := r.Members.CompleteName(prefix) + return name +} diff --git a/host.go b/host.go index 39c38cf..f362d60 100644 --- a/host.go +++ b/host.go @@ -192,13 +192,7 @@ func (h *Host) Serve() { } func (h *Host) completeName(partial string) string { - names := h.NamesPrefix(partial) - if len(names) == 0 { - // Didn't find anything - return "" - } - - return names[len(names)-1] + return h.CompleteName(partial) } func (h *Host) completeCommand(partial string) string { diff --git a/set/name_trie.go b/set/name_trie.go new file mode 100644 index 0000000..2b1fe3b --- /dev/null +++ b/set/name_trie.go @@ -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) + } +} \ No newline at end of file diff --git a/set/set.go b/set/set.go index 1b489ad..defe4ac 100644 --- a/set/set.go +++ b/set/set.go @@ -21,6 +21,7 @@ type Set struct { sync.RWMutex lookup map[string]Item normalize func(string) string + names *NameTrie } // New creates a new set with case-insensitive keys @@ -28,6 +29,7 @@ func New() *Set { return &Set{ lookup: map[string]Item{}, normalize: normalize, + names: createNameTrie(), } } @@ -36,6 +38,7 @@ func (s *Set) Clear() int { s.Lock() n := len(s.lookup) s.lookup = map[string]Item{} + s.names = createNameTrie() s.Unlock() return n } @@ -102,6 +105,7 @@ func (s *Set) Add(item Item) error { } s.lookup[key] = item + s.names.Insert(key) return nil } @@ -117,6 +121,7 @@ func (s *Set) Remove(key string) error { return ErrMissing } delete(s.lookup, key) + s.names.Delete(key) return nil } @@ -187,6 +192,10 @@ func (s *Set) ListPrefix(prefix string) []Item { return r } +func (s *Set) CompleteName(name string) (string, bool) { + return s.names.ClosestName(name) +} + func normalize(key string) string { return strings.ToLower(key) }