namespace Terminal.Gui; /// /// which suggests from a collection of words those that match the /// . You can update at any time to change candidates /// considered for autocomplete. /// public class SingleWordSuggestionGenerator : ISuggestionGenerator { /// The full set of all strings that can be suggested. /// public virtual List AllSuggestions { get; set; } = new (); /// public IEnumerable GenerateSuggestions (AutocompleteContext context) { // if there is nothing to pick from if (AllSuggestions.Count == 0) { return Enumerable.Empty (); } List line = context.CurrentLine.Select (c => c.Rune).ToList (); string currentWord = IdxToWord (line, context.CursorPosition, out int startIdx); context.CursorPosition = startIdx < 1 ? startIdx : Math.Min (startIdx + 1, line.Count); if (string.IsNullOrWhiteSpace (currentWord)) { return Enumerable.Empty (); } return AllSuggestions.Where ( o => o.StartsWith (currentWord, StringComparison.CurrentCultureIgnoreCase) && !o.Equals (currentWord, StringComparison.CurrentCultureIgnoreCase) ) .Select (o => new Suggestion (currentWord.Length, o)) .ToList () .AsReadOnly (); } /// /// Return true if the given symbol should be considered part of a word and can be contained in matches. Base /// behavior is to use /// /// The rune. /// public virtual bool IsWordChar (Rune rune) { return char.IsLetterOrDigit ((char)rune.Value); } /// /// /// Given a of characters, returns the word which ends at or null. /// Also returns null if the is positioned in the middle of a word. /// /// /// Use this method to determine whether autocomplete should be shown when the cursor is at a given point in a /// line and to get the word from which suggestions should be generated. Use the to /// indicate if search the word at left (negative), at right (positive) or at the current column (zero) which is /// the default. /// /// /// /// /// The start index of the word. /// /// protected virtual string IdxToWord (List line, int idx, out int startIdx, int columnOffset = 0) { var sb = new StringBuilder (); startIdx = idx; // get the ending word index while (startIdx < line.Count) { if (IsWordChar (line [startIdx])) { startIdx++; } else { break; } } // It isn't a word char then there is no way to autocomplete that word if (startIdx == idx && columnOffset != 0) { return null; } // we are at the end of a word. Work out what has been typed so far while (startIdx-- > 0) { if (IsWordChar (line [startIdx])) { sb.Insert (0, (char)line [startIdx].Value); } else { break; } } startIdx = Math.Max (startIdx, 0); return sb.ToString (); } }