1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2026-05-22 10:15:43 +02:00

suggest files when picking a path to filter on

async fetching of suggestions

remove limit

cache the trie for future use

more

more
This commit is contained in:
Jesse Duffield
2021-10-18 19:00:03 +11:00
parent a496858c62
commit ca7252ef8e
54 changed files with 4615 additions and 17 deletions
+3
View File
@@ -0,0 +1,3 @@
This is the complete list of go-patricia copyright holders:
Ondřej Kupka <ondra.cap@gmail.com>
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 The AUTHORS
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+182
View File
@@ -0,0 +1,182 @@
// Copyright (c) 2014 The go-patricia AUTHORS
//
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.
package patricia
import (
"io"
"sort"
)
type childList interface {
length() int
head() *Trie
add(child *Trie) childList
remove(b byte)
replace(b byte, child *Trie)
next(b byte) *Trie
combinedMask() uint64
getChildren() []*Trie
walk(prefix *Prefix, visitor VisitorFunc) error
print(w io.Writer, indent int)
clone() childList
total() int
}
type tries []*Trie
func (t tries) Len() int {
return len(t)
}
func (t tries) Less(i, j int) bool {
strings := sort.StringSlice{string(t[i].prefix), string(t[j].prefix)}
return strings.Less(0, 1)
}
func (t tries) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
type childContainer struct {
char byte
node *Trie
}
type superDenseChildList struct {
children []childContainer
}
func newSuperDenseChildList() childList {
return &superDenseChildList{
make([]childContainer, 0),
}
}
func (list *superDenseChildList) length() int {
return len(list.children)
}
func (list *superDenseChildList) head() *Trie {
if len(list.children) > 0 {
return list.children[0].node
}
return nil
}
func (list *superDenseChildList) add(child *Trie) childList {
char := child.prefix[0]
list.children = append(list.children, childContainer{
char,
child,
})
return list
}
func (list *superDenseChildList) remove(b byte) {
children := list.children
for i := 0; i < len(children); i++ {
if children[i].char == b {
// children[i] = children[len(children)-1]
// children = children[:len(children)-1]
// children = append(children[:i], children[i+1:]...)
newChildren := make([]childContainer, len(children)-1)
// copy the elements over to avoid "memory leaks"
copy(newChildren, children[:i])
copy(newChildren[i:], children[i+1:])
list.children = newChildren
// list.children = make([]childContainer, len(children))
// copy(list.children, children)
// children = nil
return
}
}
}
func (list *superDenseChildList) replace(b byte, child *Trie) {
children := list.children
for i := 0; i < len(list.children); i++ {
if children[i].char == b {
children[i].node = child
return
}
}
}
func (list *superDenseChildList) next(b byte) *Trie {
children := list.children
for i := 0; i < len(list.children); i++ {
if children[i].char == b {
return children[i].node
}
}
return nil
}
func (list superDenseChildList) combinedMask() uint64 {
var mask uint64
for _, child := range list.children {
// fmt.Printf("child = %+v\n", child)
mask |= child.node.mask
}
return mask
}
func (list *superDenseChildList) getChildren() []*Trie {
children := make([]*Trie, 0, len(list.children))
for _, child := range list.children {
children = append(children, child.node)
}
return children
}
func (list *superDenseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
for _, child := range list.children {
node := child.node
*prefix = append(*prefix, node.prefix...)
if node.item != nil {
if err := visitor(*prefix, node.item); err != nil {
if err == SkipSubtree {
*prefix = (*prefix)[:len(*prefix)-len(node.prefix)]
continue
}
*prefix = (*prefix)[:len(*prefix)-len(node.prefix)]
return err
}
}
err := node.children.walk(prefix, visitor)
*prefix = (*prefix)[:len(*prefix)-len(node.prefix)]
if err != nil {
return err
}
}
return nil
}
func (list *superDenseChildList) print(w io.Writer, indent int) {
for _, child := range list.children {
child.node.print(w, indent)
}
}
func (list *superDenseChildList) clone() childList {
clones := make([]childContainer, len(list.children))
for i := 0; i < len(list.children); i++ {
child := list.children[i]
clones[i] = childContainer{child.char, child.node.Clone()}
}
return &superDenseChildList{
clones,
}
}
func (list *superDenseChildList) total() int {
return len(list.children)
}
+892
View File
@@ -0,0 +1,892 @@
// Copyright (c) 2014 The go-patricia AUTHORS
//
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.
package patricia
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
)
//------------------------------------------------------------------------------
// Trie
//------------------------------------------------------------------------------
const (
defaultMaxPrefixPerNode = 10
)
var (
maxPrefixPerNode = defaultMaxPrefixPerNode
)
type (
// Prefix is the type of node prefixes
Prefix []byte
// Item is just interface{}
Item interface{}
// VisitorFunc is the type of functions passed to visit function
VisitorFunc func(prefix Prefix, item Item) error
// FuzzyVisitorFunc additionaly returns how many characters were skipped which can be sorted on
FuzzyVisitorFunc func(prefix Prefix, item Item, skipped int) error
)
// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
// and other funky stuff.
//
// Trie is not thread-safe.
type Trie struct {
prefix Prefix
item Item
mask uint64
children childList
}
// Public API ------------------------------------------------------------------
// NewTrie constructs a new trie.
func NewTrie() *Trie {
trie := &Trie{}
trie.children = newSuperDenseChildList()
trie.mask = 0
return trie
}
// SetMaxPrefixPerNode sets the maximum length of a prefix before it is split into two nodes
func SetMaxPrefixPerNode(value int) {
maxPrefixPerNode = value
}
// Clone makes a copy of an existing trie.
// Items stored in both tries become shared, obviously.
func (trie *Trie) Clone() *Trie {
return &Trie{
prefix: append(Prefix(nil), trie.prefix...),
item: trie.item,
children: trie.children.clone(),
}
}
// Item returns the item stored in the root of this trie.
func (trie *Trie) Item() Item {
return trie.item
}
// Insert inserts a new item into the trie using the given prefix. Insert does
// not replace existing items. It returns false if an item was already in place.
func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
return trie.put(key, item, false)
}
// Set works much like Insert, but it always sets the item, possibly replacing
// the item previously inserted.
func (trie *Trie) Set(key Prefix, item Item) {
trie.put(key, item, true)
}
// Get returns the item located at key.
//
// This method is a bit dangerous, because Get can as well end up in an internal
// node that is not really representing any user-defined value. So when nil is
// a valid value being used, it is not possible to tell if the value was inserted
// into the tree by the user or not. A possible workaround for this is not to use
// nil interface as a valid value, even using zero value of any type is enough
// to prevent this bad behaviour.
func (trie *Trie) Get(key Prefix) (item Item) {
_, node, found, leftover := trie.findSubtree(key)
if !found || len(leftover) != 0 {
return nil
}
return node.item
}
// Match returns what Get(prefix) != nil would return. The same warning as for
// Get applies here as well.
func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
return trie.Get(prefix) != nil
}
// MatchSubtree returns true when there is a subtree representing extensions
// to key, that is if there are any keys in the tree which have key as prefix.
func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
_, _, matched, _ = trie.findSubtree(key)
return
}
// Visit calls visitor on every node containing a non-nil item
// in alphabetical order.
//
// If an error is returned from visitor, the function stops visiting the tree
// and returns that error, unless it is a special error - SkipSubtree. In that
// case Visit skips the subtree represented by the current node and continues
// elsewhere.
func (trie *Trie) Visit(visitor VisitorFunc) error {
return trie.walk(nil, visitor)
}
func (trie *Trie) size() int {
n := 0
err := trie.walk(nil, func(prefix Prefix, item Item) error {
n++
return nil
})
if err != nil {
panic(err)
}
return n
}
func (trie *Trie) total() int {
return 1 + trie.children.total()
}
// VisitSubtree works much like Visit, but it only visits nodes matching prefix.
func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
// Nil prefix not allowed.
if prefix == nil {
panic(ErrNilPrefix)
}
// Empty trie must be handled explicitly.
if trie.prefix == nil {
return nil
}
// Locate the relevant subtree.
_, root, found, leftover := trie.findSubtree(prefix)
if !found {
return nil
}
prefix = append(prefix, leftover...)
// Visit it.
return root.walk(prefix, visitor)
}
type potentialSubtree struct {
idx int
skipped int
prefix Prefix
node *Trie
}
// VisitFuzzy visits every node that is succesfully matched via fuzzy matching
func (trie *Trie) VisitFuzzy(partial Prefix, caseInsensitive bool, visitor FuzzyVisitorFunc) error {
if len(partial) == 0 {
return trie.VisitPrefixes(partial, caseInsensitive, func(prefix Prefix, item Item) error {
return visitor(prefix, item, 0)
})
}
var (
m uint64
cmp uint64
i int
p potentialSubtree
)
potential := []potentialSubtree{potentialSubtree{node: trie, prefix: Prefix(""), idx: 0}}
for l := len(potential); l > 0; l = len(potential) {
i = l - 1
p = potential[i]
potential = potential[:i]
m = makePrefixMask(partial[p.idx:])
if caseInsensitive {
cmp = caseInsensitiveMask(p.node.mask)
} else {
cmp = p.node.mask
}
if (cmp & m) != m {
continue
}
matchCount, skipped := fuzzyMatchCount(p.node.prefix,
partial[p.idx:], p.idx, caseInsensitive)
p.idx += matchCount
if p.idx != 0 {
p.skipped += skipped
}
if p.idx == len(partial) {
fullPrefix := append(p.prefix, p.node.prefix...)
err := p.node.walk(Prefix(""), func(prefix Prefix, item Item) error {
key := make([]byte, len(fullPrefix), len(fullPrefix)+len(prefix))
copy(key, fullPrefix)
key = append(key, prefix...)
err := visitor(key, item, p.skipped)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
continue
}
for _, c := range p.node.children.getChildren() {
if c != nil {
newPrefix := make(Prefix, len(p.prefix), len(p.prefix)+len(p.node.prefix))
copy(newPrefix, p.prefix)
newPrefix = append(newPrefix, p.node.prefix...)
potential = append(potential, potentialSubtree{
node: c,
prefix: newPrefix,
idx: p.idx,
skipped: p.skipped,
})
} else {
fmt.Println("warning, child isn il")
}
}
}
return nil
}
func fuzzyMatchCount(prefix, query Prefix, idx int, caseInsensitive bool) (count, skipped int) {
for i := 0; i < len(prefix); i++ {
var match bool
if caseInsensitive {
match = matchCaseInsensitive(prefix[i], query[count])
} else {
match = prefix[i] == query[count]
}
if !match {
if count+idx > 0 {
skipped++
}
continue
}
count++
if count >= len(query) {
return
}
}
return
}
// VisitSubstring takes a substring and visits all the nodes that whos prefix contains this substring
func (trie *Trie) VisitSubstring(substring Prefix, caseInsensitive bool, visitor VisitorFunc) error {
if len(substring) == 0 {
return trie.VisitSubtree(substring, visitor)
}
var (
m uint64
cmp uint64
i int
p potentialSubtree
suffixLen int
maxSuffixLen = len(substring) - 1
)
potential := []potentialSubtree{potentialSubtree{node: trie, prefix: nil}}
for l := len(potential); l > 0; l = len(potential) {
i = l - 1
p = potential[i]
potential = potential[:i]
if len(p.prefix) < maxSuffixLen {
suffixLen = len(p.prefix)
} else {
suffixLen = maxSuffixLen
}
searchBytes := append(p.prefix[len(p.prefix)-suffixLen:], p.node.prefix...)
contains := false
if caseInsensitive {
contains = bytes.Contains(bytes.ToUpper(searchBytes), bytes.ToUpper(substring))
} else {
contains = bytes.Contains(searchBytes, substring)
}
if contains {
fullPrefix := append(p.prefix, p.node.prefix...)
err := p.node.walk(Prefix(""), func(prefix Prefix, item Item) error {
key := make([]byte, len(fullPrefix), len(fullPrefix)+len(prefix))
copy(key, fullPrefix)
key = append(key, prefix...)
copy(key, append(fullPrefix, prefix...))
err := visitor(key, item)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
newPrefix := make(Prefix, len(p.prefix), len(p.prefix)+len(p.node.prefix))
copy(newPrefix, p.prefix)
newPrefix = append(newPrefix, p.node.prefix...)
overLap := overlapLength(newPrefix, substring, caseInsensitive)
m = makePrefixMask(substring[overLap:])
for _, c := range p.node.children.getChildren() {
if caseInsensitive {
cmp = caseInsensitiveMask(c.mask)
} else {
cmp = c.mask
}
if c != nil && (cmp&m == m) {
potential = append(potential, potentialSubtree{
node: c,
prefix: newPrefix,
})
}
}
}
return nil
}
func overlapLength(prefix, query Prefix, caseInsensitive bool) int {
startLength := len(query) - 1
if len(prefix) < startLength {
startLength = len(prefix)
}
for i := startLength; i > 0; i-- {
suffix := prefix[len(prefix)-i:]
queryPrefix := query[:i]
if caseInsensitive {
if bytes.EqualFold(suffix, queryPrefix) {
return i
}
} else if bytes.Equal(suffix, queryPrefix) {
return i
}
}
return 0
}
// VisitPrefixes visits only nodes that represent prefixes of key.
// To say the obvious, returning SkipSubtree from visitor makes no sense here.
func (trie *Trie) VisitPrefixes(key Prefix, caseInsensitive bool, visitor VisitorFunc) error {
// Nil key not allowed.
if key == nil {
panic(ErrNilPrefix)
}
// Empty trie must be handled explicitly.
if trie.prefix == nil {
return nil
}
// Walk the path matching key prefixes.
node := trie
prefix := key
offset := 0
for {
// Compute what part of prefix matches.
common := node.longestCommonPrefixLength(key, caseInsensitive)
key = key[common:]
offset += common
// Partial match means that there is no subtree matching prefix.
if common < len(node.prefix) {
return nil
}
// Call the visitor.
if item := node.item; item != nil {
if err := visitor(prefix[:offset], item); err != nil {
return err
}
}
if len(key) == 0 {
// This node represents key, we are finished.
return nil
}
// There is some key suffix left, move to the children.
child := node.children.next(key[0])
if child == nil {
// There is nowhere to continue, return.
return nil
}
node = child
}
}
// Delete deletes the item represented by the given prefix.
//
// True is returned if the matching node was found and deleted.
func (trie *Trie) Delete(key Prefix) (deleted bool) {
// Nil prefix not allowed.
if key == nil {
panic(ErrNilPrefix)
}
// Empty trie must be handled explicitly.
if trie.prefix == nil {
return false
}
// Find the relevant node.
path, found, _ := trie.findSubtreePath(key)
if !found {
return false
}
node := path[len(path)-1]
var parent *Trie
if len(path) != 1 {
parent = path[len(path)-2]
}
// If the item is already set to nil, there is nothing to do.
if node.item == nil {
return false
}
// Delete the item.
node.item = nil
// Initialise i before goto.
// Will be used later in a loop.
i := len(path) - 1
// In case there are some child nodes, we cannot drop the whole subtree.
// We can try to compact nodes, though.
if node.children.length() != 0 {
goto Compact
}
// In case we are at the root, just reset it and we are done.
if parent == nil {
node.reset()
return true
}
// We can drop a subtree.
// Find the first ancestor that has its value set or it has 2 or more child nodes.
// That will be the node where to drop the subtree at.
for ; i >= 0; i-- {
if current := path[i]; current.item != nil || current.children.length() >= 2 {
break
}
}
// Handle the case when there is no such node.
// In other words, we can reset the whole tree.
if i == -1 {
path[0].reset()
return true
}
// We can just remove the subtree here.
node = path[i]
if i == 0 {
parent = nil
} else {
parent = path[i-1]
}
// i+1 is always a valid index since i is never pointing to the last node.
// The loop above skips at least the last node since we are sure that the item
// is set to nil and it has no children, othewise we would be compacting instead.
node.children.remove(path[i+1].prefix[0])
// lastly, the bitmasks of all of the parent nodes have to be updated again, since
// a child node of all of them has bin removed
for ; i >= 0; i-- {
n := path[i]
n.mask = n.children.combinedMask()
}
Compact:
// The node is set to the first non-empty ancestor,
// so try to compact since that might be possible now.
if compacted := node.compact(); compacted != node {
if parent == nil {
*node = *compacted
} else {
parent.children.replace(node.prefix[0], compacted)
*parent = *parent.compact()
}
}
return true
}
// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
//
// True is returned if the subtree was found and deleted.
func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
// Nil prefix not allowed.
if prefix == nil {
panic(ErrNilPrefix)
}
// Empty trie must be handled explicitly.
if trie.prefix == nil {
return false
}
// Locate the relevant subtree.
parent, root, found, _ := trie.findSubtree(prefix)
path, _, _ := trie.findSubtreePath(prefix)
if !found {
return false
}
// If we are in the root of the trie, reset the trie.
if parent == nil {
root.reset()
return true
}
// Otherwise remove the root node from its parent.
parent.children.remove(root.prefix[0])
// update masks
parent.mask = parent.children.combinedMask()
for i := len(path) - 1; i >= 0; i-- {
n := path[i]
n.mask = n.children.combinedMask()
}
return true
}
// Internal helper methods -----------------------------------------------------
func (trie *Trie) empty() bool {
return trie.item == nil && trie.children.length() == 0
}
func (trie *Trie) reset() {
trie.prefix = nil
trie.children = newSuperDenseChildList()
}
func makePrefixMask(key Prefix) uint64 {
var mask uint64
for _, b := range key {
if b >= '0' && b <= '9' {
// 0-9 bits: 0-9
b -= 48
} else if b >= 'A' && b <= 'Z' {
// A-Z bits: 10-35
b -= 55
} else if b >= 'a' && b <= 'z' {
// a-z bits: 36-61
b -= 61
} else if b == '.' {
b = 62
} else if b == '-' {
b = 63
} else {
continue
}
mask |= uint64(1) << uint64(b)
}
return mask
}
const upperBits = 0xFFFFFFC00
const lowerBits = 0x3FFFFFF000000000
func caseInsensitiveMask(mask uint64) uint64 {
mask |= (mask & upperBits) << uint64(26)
mask |= (mask & lowerBits) >> uint64(26)
return mask
}
var charmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-"
func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
// Nil prefix not allowed.
if key == nil {
panic(ErrNilPrefix)
}
var (
common int
node = trie
child *Trie
mask uint64
)
mask = makePrefixMask(key)
if node.prefix == nil {
node.mask |= mask
if len(key) <= maxPrefixPerNode {
node.prefix = key
goto InsertItem
}
node.prefix = key[:maxPrefixPerNode]
key = key[maxPrefixPerNode:]
mask = makePrefixMask(key)
goto AppendChild
}
for {
// Compute the longest common prefix length.
common = node.longestCommonPrefixLength(key, false)
key = key[common:]
// Only a part matches, split.
if common < len(node.prefix) {
goto SplitPrefix
}
// common == len(node.prefix) since never (common > len(node.prefix))
// common == len(former key) <-> 0 == len(key)
// -> former key == node.prefix
if len(key) == 0 {
goto InsertItem
}
node.mask |= mask
// Check children for matching prefix.
child = node.children.next(key[0])
if child == nil {
goto AppendChild
}
node = child
}
SplitPrefix:
// Split the prefix if necessary.
child = new(Trie)
*child = *node
*node = *NewTrie()
node.prefix = child.prefix[:common]
child.prefix = child.prefix[common:]
child = child.compact()
node.children = node.children.add(child)
node.mask = child.mask
node.mask |= mask
mask = makePrefixMask(key)
AppendChild:
// Keep appending children until whole prefix is inserted.
// This loop starts with empty node.prefix that needs to be filled.
for len(key) != 0 {
child := NewTrie()
child.mask = mask
if len(key) <= maxPrefixPerNode {
child.prefix = key
node.children = node.children.add(child)
node = child
goto InsertItem
} else {
child.prefix = key[:maxPrefixPerNode]
key = key[maxPrefixPerNode:]
mask = makePrefixMask(key)
node.children = node.children.add(child)
node = child
}
}
InsertItem:
// Try to insert the item if possible.
if replace || node.item == nil {
node.item = item
return true
}
return false
}
func (trie *Trie) compact() *Trie {
// Only a node with a single child can be compacted.
if trie.children.length() != 1 {
return trie
}
child := trie.children.head()
// If any item is set, we cannot compact since we want to retain
// the ability to do searching by key. This makes compaction less usable,
// but that simply cannot be avoided.
if trie.item != nil || child.item != nil {
return trie
}
// Make sure the combined prefixes fit into a single node.
if len(trie.prefix)+len(child.prefix) > maxPrefixPerNode {
return trie
}
// Concatenate the prefixes, move the items.
child.prefix = append(trie.prefix, child.prefix...)
child.mask = trie.mask
if trie.item != nil {
child.item = trie.item
}
return child
}
func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
// Find the subtree matching prefix.
root = trie
for {
// Compute what part of prefix matches.
common := root.longestCommonPrefixLength(prefix, false)
prefix = prefix[common:]
// We used up the whole prefix, subtree found.
if len(prefix) == 0 {
found = true
leftover = root.prefix[common:]
return
}
// Partial match means that there is no subtree matching prefix.
if common < len(root.prefix) {
leftover = root.prefix[common:]
return
}
// There is some prefix left, move to the children.
child := root.children.next(prefix[0])
if child == nil {
// There is nowhere to continue, there is no subtree matching prefix.
return
}
parent = root
root = child
}
}
func (trie *Trie) findSubtreePath(prefix Prefix) (path []*Trie, found bool, leftover Prefix) {
// Find the subtree matching prefix.
root := trie
var subtreePath []*Trie
for {
// Append the current root to the path.
subtreePath = append(subtreePath, root)
// Compute what part of prefix matches.
common := root.longestCommonPrefixLength(prefix, false)
prefix = prefix[common:]
// We used up the whole prefix, subtree found.
if len(prefix) == 0 {
path = subtreePath
found = true
leftover = root.prefix[common:]
return
}
// Partial match means that there is no subtree matching prefix.
if common < len(root.prefix) {
leftover = root.prefix[common:]
return
}
// There is some prefix left, move to the children.
child := root.children.next(prefix[0])
if child == nil {
// There is nowhere to continue, there is no subtree matching prefix.
return
}
root = child
}
}
func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
var prefix Prefix
// Allocate a bit more space for prefix at the beginning.
if actualRootPrefix == nil {
prefix = make(Prefix, 32+len(trie.prefix))
copy(prefix, trie.prefix)
prefix = prefix[:len(trie.prefix)]
} else {
prefix = make(Prefix, 32+len(actualRootPrefix))
copy(prefix, actualRootPrefix)
prefix = prefix[:len(actualRootPrefix)]
}
// Visit the root first. Not that this works for empty trie as well since
// in that case item == nil && len(children) == 0.
if trie.item != nil {
if err := visitor(prefix, trie.item); err != nil {
if err == SkipSubtree {
return nil
}
return err
}
}
// Then continue to the children.
return trie.children.walk(&prefix, visitor)
}
func (trie *Trie) longestCommonPrefixLength(prefix Prefix, caseInsensitive bool) (i int) {
for ; i < len(prefix) && i < len(trie.prefix); i++ {
p := prefix[i]
t := trie.prefix[i]
if caseInsensitive {
if !(matchCaseInsensitive(t, p)) {
break
}
} else {
if p != t {
break
}
}
}
return
}
func matchCaseInsensitive(a byte, b byte) bool {
return a == b+32 || b == a+32 || a == b
}
func (trie *Trie) dump() string {
writer := &bytes.Buffer{}
trie.print(writer, 0)
return writer.String()
}
func (trie *Trie) print(writer io.Writer, indent int) {
fmt.Fprintf(writer, "%s%s %v\n", strings.Repeat(" ", indent), string(trie.prefix), trie.item)
trie.children.print(writer, indent+2)
}
// Errors ----------------------------------------------------------------------
var (
SkipSubtree = errors.New("Skip this subtree")
ErrNilPrefix = errors.New("Nil prefix passed into a method call")
)