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 ();
}
}