Username and nicknames are now both displayed.
This matches the current functionality of the client.
MODIFY
ui/window.go
ui/window.go
@@ -23,6 +23,7 @@ import (
"github.com/Bios-Marcel/cordless/shortcuts"
"github.com/Bios-Marcel/cordless/times"
"github.com/Bios-Marcel/cordless/ui/tviewutil"
+ "github.com/Bios-Marcel/cordless/util/fuzzy"
"github.com/Bios-Marcel/discordgo"
"github.com/Bios-Marcel/tview"
"github.com/gdamore/tcell"
@@ -1003,43 +1004,86 @@ func (window *Window) ShowMentionWindowChildren(mentionWindow *tview.TreeView, m
}
func (window *Window) PopulateMentionWindow(mentionWindow *tview.TreeView, namePart string) {
- if window.selectedChannel != nil {
+ if window.selectedChannel == nil {
+ return
+ }
+
if window.selectedChannel.GuildID != "" {
+ window.PopulateMentionWindowFromCurrentGuild(mentionWindow, namePart)
+ } else {
+ window.PopulateMentionWindowFromCurrentChannel(mentionWindow, namePart)
+ }
+}
+
+func (window *Window) PopulateMentionWindowFromCurrentGuild(mentionWindow *tview.TreeView, namePart string) {
guild, discordError := window.session.State.Guild(window.selectedChannel.GuildID)
- if discordError == nil {
+ if discordError != nil {
+ return
+ }
+
+ nameMap := make(map[string]string)
+ var memberNames []string
for _, user := range guild.Members {
- if strings.Contains(strings.ToUpper(user.Nick), strings.ToUpper(namePart)) || strings.Contains(strings.ToUpper(user.User.Username)+"#"+user.User.Discriminator, strings.ToUpper(namePart)) {
userName := user.User.Username + "#" + user.User.Discriminator
- userNodeText := "\t" + userName
if len(user.Nick) > 0 {
- userNodeText += " | " + user.Nick
- }
- userNode := tview.NewTreeNode(userNodeText)
- userNode.SetReference(userName)
- mentionWindow.GetRoot().AddChild(userNode)
+ combined := "\t" + userName + " | " + user.Nick
+ nameMap[userName] = combined
+ nameMap[user.Nick] = combined
+ memberNames = append(memberNames, userName, user.Nick)
+ } else {
+ memberNames = append(memberNames, userName)
}
}
for _, role := range guild.Roles {
- if strings.Contains(strings.ToUpper(role.Name), strings.ToUpper(namePart)) {
- roleNode := tview.NewTreeNode(role.Name)
- roleNode.SetReference(role)
- mentionWindow.GetRoot().AddChild(roleNode)
+ role := role.Name
+ memberNames = append(memberNames, role)
+ }
+
+ searchResults := fuzzy.ScoreSearch(namePart, memberNames)
+ sortedResults := fuzzy.SortSearchResults(searchResults)
+
+ userWithNickSet := make(map[string]struct{})
+ for _, result := range sortedResults {
+ userName := result.Key
+ combinedStr := nameMap[userName]
+ var userNodeText string
+ if len(combinedStr) > 0 {
+ _, containsStr := userWithNickSet[combinedStr]
+ // If the combined string has been added, skip this entry.
+ if containsStr {
+ continue
}
+ userWithNickSet[combinedStr] = struct{}{}
+ userNodeText = combinedStr
+ } else {
+ userNodeText = userName
}
+ userNode := tview.NewTreeNode(userNodeText)
+ userNode.SetReference(userName)
+ mentionWindow.GetRoot().AddChild(userNode)
}
- } else {
+
+}
+
+func (window *Window) PopulateMentionWindowFromCurrentChannel(mentionWindow *tview.TreeView, namePart string) {
+ var memberNames []string
for _, user := range window.selectedChannel.Recipients {
- if strings.Contains(strings.ToUpper(user.Username)+"#"+user.Discriminator, strings.ToUpper(namePart)) {
userName := user.Username + "#" + user.Discriminator
+ memberNames = append(memberNames, userName)
+ }
+
+ searchResults := fuzzy.ScoreSearch(namePart, memberNames)
+ sortedResults := fuzzy.SortSearchResults(searchResults)
+
+ for _, result := range sortedResults {
+ userName := result.Key
userNodeText := "\t" + userName
userNode := tview.NewTreeNode(userNodeText)
userNode.SetReference(userName)
mentionWindow.GetRoot().AddChild(userNode)
}
- }
- }
- }
+
}
func (window *Window) updateServerReadStatus(guildID string, guildNode *tview.TreeNode, isSelected bool) {

ADD
util/fuzzy/fuzzy.go
+package fuzzy
+
+import (
+ "sort"
+ "strings"
+)
+
+type SearchResult struct {
+ Key string
+ Value float64
+}
+
+func ScoreSearch(searchTerm string, searchItems []string) map[string]float64 {
+ mymap := make(map[string]float64)
+ searchTerm = strings.ToLower(searchTerm)
+ for _, str := range searchItems {
+ mymap[str] = Score(searchTerm, strings.ToLower(str))
+ }
+ return mymap
+}
+
+func SortSearchResults(results map[string]float64) []SearchResult {
+ var arr []SearchResult
+ for key, value := range results {
+ if value >= 0.0 {
+ arr = append(arr, SearchResult{key, value})
+ }
+ }
+
+ sort.Slice(arr, func(i, j int) bool {
+ if arr[i].Value == arr[j].Value {
+ return strings.Compare(arr[i].Key, arr[j].Key) < 0
+ }
+ return arr[i].Value > arr[j].Value
+ })
+
+ return arr
+}
+
+// Returns:
+// -1 if the needle contains letters the haystack does not contain,
+// or if the needle length exceeds the haystack length.
+//
+// 0 if no similarities were found
+//
+// > 0 based on similarities between the needle and haystack (increasing)
+func Score(needle, haystack string) float64 {
+ needleLength := len(needle)
+ haystackLength := len(haystack)
+ if needleLength > haystackLength {
+ return -1
+ }
+
+ if needleLength == 0 {
+ return 0
+ }
+
+ score := 0.0
+ needleIndex := 0
+ for haystackIndex := 0; haystackIndex < haystackLength && needleIndex < needleLength; haystackIndex++ {
+
+ letterScore, foundAtIndex := scoreLetter(needle[needleIndex], haystack, haystackIndex)
+ if letterScore < 0 {
+ return letterScore
+ }
+
+ if foundAtIndex == haystackIndex {
+ // Letter was consecutive
+ score += letterScore * 2
+ } else {
+ score += letterScore
+ // Move haystackIndex up to the next found letter.
+ haystackIndex = foundAtIndex
+ }
+
+ needleIndex++
+ }
+ return score
+}
+
+// Scores a letter from inside a string based on its distance from the start of the string.
+// The index at which the letter was found will be returned.
+// The score and index will be -1 if the character is not found.
+func scoreLetter(c byte, haystack string, startIndex int) (float64, int) {
+ haystackLength := len(haystack)
+ for i := startIndex; i < len(haystack); i++ {
+ if c == haystack[i] {
+ var displacement float64 = float64(haystackLength - i)
+ score := displacement / float64(haystackLength) * (1.0 / float64(haystackLength))
+ return score, i
+ }
+ }
+ // Letter not found.
+ return -1, -1
+}

Username and nicknames are now both displayed.

This matches the current functionality of the client.