package readline import ( "context" "sync/atomic" ) // DelayedTabContext is a custom context interface for async updates to the tab completions type DelayedTabContext struct { rl *Instance Context context.Context cancel context.CancelFunc } func delayedSyntaxTimer(rl *Instance, i int64) { if rl.PasswordMask != 0 || rl.DelayedSyntaxWorker == nil { return } // if len(rl.line)+rl.promptLen > GetTermWidth() { // // line wraps, which is hard to do with random ANSI escape sequences // // so better we don't bother trying. // return // } // We pass either the current line or the one with the current completion. newLine := rl.DelayedSyntaxWorker(rl.GetLine()) var sLine string count := atomic.LoadInt64(&rl.delayedSyntaxCount) if count != i { return } // Highlight the line again if rl.SyntaxHighlighter != nil { sLine = rl.SyntaxHighlighter(newLine) } else { sLine = string(newLine) } // Save the line as current, and refresh rl.updateLine([]rune(sLine)) } // AppendGroupSuggestions - Given a group name, append a list of completion candidates. // Any item in the list already existing in the current group's will be ignored. // If the group is not found with the given name, all suggestions will be dropped, no new group is created. func (dtc DelayedTabContext) AppendGroupSuggestions(groupName string, suggestions []string) { dtc.rl.mutex.Lock() defer dtc.rl.mutex.Unlock() // Get the group, and return if not found var grp *CompletionGroup for _, g := range dtc.rl.tcGroups { if g.Name == groupName { grp = g } } if grp == nil { return } // Add candidate items for i := range suggestions { select { case <-dtc.Context.Done(): return default: // Drop duplicates for _, actual := range grp.Suggestions { if actual == suggestions[i] { continue } } // Descriptions might be used by tabdisplay maps or grids, but not lists. if grp.DisplayType != TabDisplayList { grp.Descriptions[suggestions[i]] = suggestions[i] } // Suggestions are used by all groups no matter their display type grp.Suggestions = append(grp.Suggestions, suggestions[i]) } } dtc.rl.clearHelpers() dtc.rl.renderHelpers() } // AppendGroupAliases - Given a group name, append a list of completion candidates' ALIASES, that is, a second candidate item. // If any candidate (map index) already exists, its corresponding alias will be overwritten with the new one. // If any candidate (map index) does not exists yet, it will be added along with its alias. // If the group is not found with the given name, all suggestions will be dropped, no new group is created. func (dtc DelayedTabContext) AppendGroupAliases(groupName string, aliases map[string]string) { dtc.rl.mutex.Lock() defer dtc.rl.mutex.Unlock() // Get the group, and return if not found var grp *CompletionGroup for _, g := range dtc.rl.tcGroups { if g.Name == groupName { grp = g } } if grp == nil { return } // Add candidate aliases for sugg, alias := range aliases { select { case <-dtc.Context.Done(): return default: // Add to suggestions list if not existing yet var found bool for _, actual := range grp.Suggestions { if actual == sugg { found = true } } if !found { grp.Suggestions = append(grp.Suggestions, sugg) } // Map the new description anyway grp.Aliases[sugg] = alias } } // Reinit all completion groups (recomputes sizes) for _, grp := range dtc.rl.tcGroups { grp.init(dtc.rl) } // dtc.rl.clearHelpers() // dtc.rl.renderHelpers() } // AppendGroupDescriptions - Given a group name, append a list of descriptions to a list of candidates. // If any candidate (map index) already exists, its corresponding description will be overwritten with the new one. // If any candidate (map index) does not exists yet, it will be added along with its description. func (dtc DelayedTabContext) AppendGroupDescriptions(groupName string, descriptions map[string]string) { dtc.rl.mutex.Lock() defer dtc.rl.mutex.Unlock() // Get the group, and return if not found var grp *CompletionGroup for _, g := range dtc.rl.tcGroups { if g.Name == groupName { grp = g } } if grp == nil { return } // Add candidate descriptions for sugg, desc := range descriptions { select { case <-dtc.Context.Done(): return default: // Add to suggestions list if not existing yet var found bool for _, actual := range grp.Suggestions { if actual == sugg { found = true } } if !found { grp.Suggestions = append(grp.Suggestions, sugg) } // Map the new description anyway grp.Descriptions[sugg] = desc } } // Reinit all completion groups (recomputes sizes) for _, grp := range dtc.rl.tcGroups { grp.init(dtc.rl) } dtc.rl.clearHelpers() dtc.rl.renderHelpers() } // AppendGroup - Asynchronously add an entire group of completions to the current list func (dtc DelayedTabContext) AppendGroup(group *CompletionGroup) { dtc.rl.mutex.Lock() defer dtc.rl.mutex.Unlock() // Simply append group to the list dtc.rl.tcGroups = append(dtc.rl.tcGroups, group) dtc.rl.clearHelpers() dtc.rl.renderHelpers() }