89 lines
2.2 KiB
Go
89 lines
2.2 KiB
Go
package complete
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/diamondburned/cchat"
|
|
"github.com/diamondburned/cchat-discord/internal/discord/channel/shared"
|
|
"github.com/lithammer/fuzzysearch/fuzzy"
|
|
)
|
|
|
|
type CompleterFunc func(word string) []cchat.CompletionEntry
|
|
|
|
type ChannelCompleter struct {
|
|
shared.Channel
|
|
}
|
|
|
|
type CompleterPrefixes map[byte]CompleterFunc
|
|
|
|
type Completer struct {
|
|
Prefixes CompleterPrefixes
|
|
SlashHandler cchat.Completer
|
|
}
|
|
|
|
const MaxCompletion = 15
|
|
|
|
func New(ch shared.Channel) cchat.Completer {
|
|
completer := ChannelCompleter{ch}
|
|
return Completer{
|
|
Prefixes: map[byte]CompleterFunc{
|
|
'@': completer.CompleteMentions,
|
|
'#': completer.CompleteChannels,
|
|
':': completer.CompleteEmojis,
|
|
},
|
|
}
|
|
}
|
|
|
|
// CompleteMessage implements message input completion capability for Discord.
|
|
// This method supports user mentions, channel mentions and emojis.
|
|
//
|
|
// For the individual implementations, refer to channel_completion.go.
|
|
func (cc Completer) Complete(words []string, i int64) []cchat.CompletionEntry {
|
|
var word = words[i]
|
|
// Word should have at least a character for the char check.
|
|
if len(word) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Always check the first word for slash, not the current word.
|
|
if cc.SlashHandler != nil && words[0][0] == '/' {
|
|
return cc.SlashHandler.Complete(words, i)
|
|
}
|
|
|
|
fn, ok := cc.Prefixes[word[0]]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return fn(word[1:])
|
|
}
|
|
|
|
// rankFunc is the default rank function to use.
|
|
func rankFunc(source, target string) int {
|
|
return fuzzy.RankMatchNormalizedFold(source, target)
|
|
}
|
|
|
|
func ensureEntriesMade(entries *[]cchat.CompletionEntry) {
|
|
if *entries == nil {
|
|
*entries = make([]cchat.CompletionEntry, 0, MaxCompletion)
|
|
}
|
|
}
|
|
|
|
func ensureDistancesMade(distances *map[string]int) {
|
|
if *distances == nil {
|
|
*distances = make(map[string]int, MaxCompletion)
|
|
}
|
|
}
|
|
|
|
// sortDistances sorts according to the given Levenshtein distances from the Raw
|
|
// string of the entries from most accurate to least accurate.
|
|
func sortDistances(entries []cchat.CompletionEntry, distances map[string]int) {
|
|
if len(entries) == 0 {
|
|
return
|
|
}
|
|
// The lower the distance, the more accurate.
|
|
sort.SliceStable(entries, func(i, j int) bool {
|
|
return distances[entries[i].Raw] < distances[entries[j].Raw]
|
|
})
|
|
}
|