moved tab completion to separate file, updated handleTab() to new structure. Created new global to hold a copy of the tab completion slice thats always up to date instead of generating each time, hooked completion options updates into view updates and channel join activity.
This commit is contained in:
131
main.go
131
main.go
@ -52,6 +52,7 @@ func main() {
|
||||
if err := g.MainLoop(); err != nil && !gocui.IsQuit(err) {
|
||||
fmt.Printf("%+v", err)
|
||||
}
|
||||
go generateChannelTabCompletionSlice()
|
||||
}
|
||||
|
||||
// Gocui basic setup
|
||||
@ -277,6 +278,7 @@ func populateChat() {
|
||||
return
|
||||
} else {
|
||||
go populateChat()
|
||||
go generateChannelTabCompletionSlice()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -338,6 +340,7 @@ func populateList() {
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
printToView("List", fmt.Sprintf("%s%s%s%s", channelsColor, recentPMs, recentChannels, noColor))
|
||||
go generateRecentTabCompletionSlice()
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,126 +386,6 @@ func formatOutput(api keybase.ChatAPI) string {
|
||||
|
||||
// End formatting
|
||||
|
||||
// Tab completion
|
||||
func getCurrentChannelMembership() []string {
|
||||
var rs []string
|
||||
if channel.Name != "" {
|
||||
t := k.NewTeam(channel.Name)
|
||||
if testVar, err := t.MemberList(); err != nil {
|
||||
return rs // then this isn't a team, its a PM or there was an error in the API call
|
||||
} else {
|
||||
for _, m := range testVar.Result.Members.Owners {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Admins {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Writers {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Readers {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
func filterStringSlice(ss []string, fv string) []string {
|
||||
var rs []string
|
||||
for _, s := range ss {
|
||||
if strings.HasPrefix(s, fv) {
|
||||
rs = append(rs, s)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
func longestCommonPrefix(ss []string) string {
|
||||
// cover the case where the slice has no or one members
|
||||
switch len(ss) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return ss[0]
|
||||
}
|
||||
// all strings are compared by bytes here forward (TBD unicode normalization?)
|
||||
// establish min, max lenth members of the slice by iterating over the members
|
||||
min, max := ss[0], ss[0]
|
||||
for _, s := range ss[1:] {
|
||||
switch {
|
||||
case s < min:
|
||||
min = s
|
||||
case s > max:
|
||||
max = s
|
||||
}
|
||||
}
|
||||
// then iterate over the characters from min to max, as soon as chars don't match return
|
||||
for i := 0; i < len(min) && i < len(max); i++ {
|
||||
if min[i] != max[i] {
|
||||
return min[:i]
|
||||
}
|
||||
}
|
||||
// to cover the case where all members are equal, just return one
|
||||
return min
|
||||
}
|
||||
func stringRemainder(aStr, bStr string) string {
|
||||
var long, short string
|
||||
//figure out which string is longer
|
||||
switch {
|
||||
case len(aStr) < len(bStr):
|
||||
short = aStr
|
||||
long = bStr
|
||||
default:
|
||||
short = bStr
|
||||
long = aStr
|
||||
}
|
||||
// iterate over the strings using an external iterator so we don't lose the value
|
||||
i := 0
|
||||
for i < len(short) && i < len(long) {
|
||||
if short[i] != long[i] {
|
||||
// the strings aren't equal so don't return anything
|
||||
return ""
|
||||
}
|
||||
i++
|
||||
}
|
||||
// return whatever's left of the longer string
|
||||
return long[i:]
|
||||
}
|
||||
func appendIfNotInSlice(ss []string, s string) []string {
|
||||
for _, element := range ss {
|
||||
if element == s {
|
||||
return ss
|
||||
}
|
||||
}
|
||||
return append(ss, s)
|
||||
}
|
||||
func generateChannelTabCompletionSlice(inputWord string) []string {
|
||||
// create a slice to hold the values
|
||||
var firstSlice []string
|
||||
// iterate over all the conversation results
|
||||
for _, s := range channels {
|
||||
if s.MembersType == keybase.TEAM {
|
||||
// its a team so add the topic name as a possible tab completion
|
||||
firstSlice = appendIfNotInSlice(firstSlice, s.TopicName)
|
||||
firstSlice = appendIfNotInSlice(firstSlice, s.Name)
|
||||
} else {
|
||||
// its a user, so clean the name and append the users name as a possible tab completion
|
||||
firstSlice = appendIfNotInSlice(firstSlice, cleanChannelName(s.Name))
|
||||
}
|
||||
}
|
||||
// next fetch all members of the current channel and add them to the slice
|
||||
secondSlice := getCurrentChannelMembership()
|
||||
for _, m := range secondSlice {
|
||||
firstSlice = appendIfNotInSlice(firstSlice, m)
|
||||
}
|
||||
// now return the resultSlice which contains all that are prefixed with inputWord
|
||||
resultSlice := filterStringSlice(firstSlice, inputWord)
|
||||
return resultSlice
|
||||
}
|
||||
func generateEmojiTabCompletionSlice(inputWord string) []string {
|
||||
// use the emojiSlice from emojiList.go and filter it for the input word
|
||||
resultSlice := filterStringSlice(emojiSlice, inputWord)
|
||||
return resultSlice
|
||||
}
|
||||
func handleTab() error {
|
||||
inputString, err := getInputString("Input")
|
||||
if err != nil {
|
||||
@ -515,14 +398,14 @@ func handleTab() error {
|
||||
var resultSlice []string
|
||||
// if the word starts with a : its an emoji lookup
|
||||
if strings.HasPrefix(s, ":") {
|
||||
resultSlice = generateEmojiTabCompletionSlice(s)
|
||||
resultSlice = getEmojiTabCompletionSlice(s)
|
||||
} else {
|
||||
// now in case the word (s) is a mention @something, lets remove it to normalize
|
||||
if strings.HasPrefix(s, "@") {
|
||||
// now in case the word (s) is a mention @something, lets remove it to normalize
|
||||
s = strings.Replace(s, "@", "", 1)
|
||||
}
|
||||
// now call get the list of all possible cantidates that have that as a prefix
|
||||
resultSlice = generateChannelTabCompletionSlice(s)
|
||||
resultSlice = getChannelTabCompletionSlice(s)
|
||||
}
|
||||
rLen := len(resultSlice)
|
||||
lcp := longestCommonPrefix(resultSlice)
|
||||
@ -546,8 +429,6 @@ func handleTab() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// End tab completion
|
||||
|
||||
// Input handling
|
||||
func handleMessage(api keybase.ChatAPI) {
|
||||
if _, ok := typeCommands[api.Msg.Content.Type]; ok {
|
||||
|
||||
142
tabComplete.go
Normal file
142
tabComplete.go
Normal file
@ -0,0 +1,142 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"samhofi.us/x/keybase"
|
||||
)
|
||||
|
||||
var (
|
||||
tabSlice []string
|
||||
)
|
||||
|
||||
// Main tab completion functions
|
||||
func getEmojiTabCompletionSlice(inputWord string) []string {
|
||||
// use the emojiSlice from emojiList.go and filter it for the input word
|
||||
resultSlice := filterStringSlice(emojiSlice, inputWord)
|
||||
return resultSlice
|
||||
}
|
||||
func getChannelTabCompletionSlice(inputWord string) []string {
|
||||
// use the tabSlice from above and filter it for the input word
|
||||
resultSlice := filterStringSlice(tabSlice, inputWord)
|
||||
return resultSlice
|
||||
}
|
||||
|
||||
//Generator Functions
|
||||
func generateChannelTabCompletionSlice() {
|
||||
// fetch all members of the current channel and add them to the slice
|
||||
channelSlice := getCurrentChannelMembership()
|
||||
for _, m := range channelSlice {
|
||||
tabSlice = appendIfNotInSlice(tabSlice, m)
|
||||
}
|
||||
}
|
||||
func generateRecentTabCompletionSlice() {
|
||||
var recentSlice []string
|
||||
for _, s := range channels {
|
||||
if s.MembersType == keybase.TEAM {
|
||||
// its a team so add the topic name and channel name
|
||||
recentSlice = appendIfNotInSlice(recentSlice, s.TopicName)
|
||||
recentSlice = appendIfNotInSlice(recentSlice, s.Name)
|
||||
} else {
|
||||
//its a user, so clean the name and append
|
||||
recentSlice = appendIfNotInSlice(recentSlice, cleanChannelName(s.Name))
|
||||
}
|
||||
}
|
||||
for _, s := range recentSlice {
|
||||
tabSlice = appendIfNotInSlice(tabSlice, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func getCurrentChannelMembership() []string {
|
||||
var rs []string
|
||||
if channel.Name != "" {
|
||||
t := k.NewTeam(channel.Name)
|
||||
if testVar, err := t.MemberList(); err != nil {
|
||||
return rs // then this isn't a team, its a PM or there was an error in the API call
|
||||
} else {
|
||||
for _, m := range testVar.Result.Members.Owners {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Admins {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Writers {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
for _, m := range testVar.Result.Members.Readers {
|
||||
rs = append(rs, fmt.Sprintf("%+v", m.Username))
|
||||
}
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
func filterStringSlice(ss []string, fv string) []string {
|
||||
var rs []string
|
||||
for _, s := range ss {
|
||||
if strings.HasPrefix(s, fv) {
|
||||
rs = append(rs, s)
|
||||
}
|
||||
}
|
||||
return rs
|
||||
}
|
||||
func longestCommonPrefix(ss []string) string {
|
||||
// cover the case where the slice has no or one members
|
||||
switch len(ss) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return ss[0]
|
||||
}
|
||||
// all strings are compared by bytes here forward (TBD unicode normalization?)
|
||||
// establish min, max lenth members of the slice by iterating over the members
|
||||
min, max := ss[0], ss[0]
|
||||
for _, s := range ss[1:] {
|
||||
switch {
|
||||
case s < min:
|
||||
min = s
|
||||
case s > max:
|
||||
max = s
|
||||
}
|
||||
}
|
||||
// then iterate over the characters from min to max, as soon as chars don't match return
|
||||
for i := 0; i < len(min) && i < len(max); i++ {
|
||||
if min[i] != max[i] {
|
||||
return min[:i]
|
||||
}
|
||||
}
|
||||
// to cover the case where all members are equal, just return one
|
||||
return min
|
||||
}
|
||||
func stringRemainder(aStr, bStr string) string {
|
||||
var long, short string
|
||||
//figure out which string is longer
|
||||
switch {
|
||||
case len(aStr) < len(bStr):
|
||||
short = aStr
|
||||
long = bStr
|
||||
default:
|
||||
short = bStr
|
||||
long = aStr
|
||||
}
|
||||
// iterate over the strings using an external iterator so we don't lose the value
|
||||
i := 0
|
||||
for i < len(short) && i < len(long) {
|
||||
if short[i] != long[i] {
|
||||
// the strings aren't equal so don't return anything
|
||||
return ""
|
||||
}
|
||||
i++
|
||||
}
|
||||
// return whatever's left of the longer string
|
||||
return long[i:]
|
||||
}
|
||||
func appendIfNotInSlice(ss []string, s string) []string {
|
||||
for _, element := range ss {
|
||||
if element == s {
|
||||
return ss
|
||||
}
|
||||
}
|
||||
return append(ss, s)
|
||||
}
|
||||
Reference in New Issue
Block a user