using InteractiveReadLine.Tokenizing; #nullable disable namespace InteractiveReadLine.KeyBehaviors { /// /// These are common "behaviors" which are invoked by keys being pressed, and can be added to the handler /// either as the default behavior or to be invoked when a special key or character is pressed. They are given /// to the handler in the form of Action delegates which receive an IKeyBehaviorTarget, which they can operate /// on in order to affect some change on the line of text being written. /// /// The behaviors provided in this static class are all simple ones, they can be used directly or they can be /// composed together or wrapped in other methods which allow access to external state, or any other use /// case that can be imagined. /// public static class CommonKeyBehaviors { /// /// Deletes the character after the cursor /// /// public static void Delete(IKeyBehaviorTarget target) { if (target.CursorPosition < target.TextBuffer.Length) target.TextBuffer.Remove(target.CursorPosition, 1); } /// /// Deletes the character before the cursor /// public static void Backspace(IKeyBehaviorTarget target) { if (target.CursorPosition > 0) { target.TextBuffer.Remove(target.CursorPosition - 1, 1); target.CursorPosition--; } } /// /// Immediately moves the cursor to the end of the line /// public static void MoveCursorToEnd(IKeyBehaviorTarget target) { target.CursorPosition = target.TextBuffer.Length; } /// /// Immediately moves the cursor to the beginning of the line /// public static void MoveCursorToStart(IKeyBehaviorTarget target) { target.CursorPosition = 0; } /// /// Moves the cursor left (towards the start) of the line, but will not move it past the first character /// public static void MoveCursorLeft(IKeyBehaviorTarget target) { if (target.CursorPosition > 0) target.CursorPosition--; } /// /// Moves the cursor right (towards the end) of the line, but will not move it beyond the end of the line /// public static void MoveCursorRight(IKeyBehaviorTarget target) { if (target.CursorPosition < target.TextBuffer.Length) target.CursorPosition++; } /// /// Clears the entire contents of the text buffer /// public static void ClearAll(IKeyBehaviorTarget target) { target.TextBuffer.Clear(); target.CursorPosition = 0; } /// /// Uses a function to create a key behavior that uses the provider's "InsertText" method to insert /// a message to the user. The handler will provide your function with the tokenized version of the /// current text, which will be null if the configuration doesn't have a tokenizer. /// /// A function which receives a Tokens object and uses it to create a string /// message, this is typically useful for providing help or hints to the user /// A key behavior action which can be registered with the read line configuration public static Action WriteMessageFromTokens(Func message) { return new Action(t => t.InsertText(message(t.GetTextTokens()))); } /// /// Invokes the target's auto-complete next behavior, which substitutes in the next suggestion /// at the token where the cursor is currently residing /// public static void AutoCompleteNext(IKeyBehaviorTarget target) => target.AutoCompleteNext(); /// /// Invokes the target's auto-complete previous behavior, which substitutes in the previous suggestion /// at the token where the cursor is currently residing /// public static void AutoCompletePrevious(IKeyBehaviorTarget target) => target.AutoCompletePrevious(); /// /// Invokes the target's next history behavior, which replaces the line with the next element /// in the history collection. Will do nothing if no history has been provided, or replace the line /// with the last entered line state if the end of the history has been reached. /// /// public static void HistoryNext(IKeyBehaviorTarget target) => target.HistoryNext(); /// /// Invokes the target's previous history behavior, which replaces the entire line with the previous /// element in the history collection. Will do nothing if no history has been provided, or if the oldest /// element in the collection has been reached. /// /// public static void HistoryPrevious(IKeyBehaviorTarget target) => target.HistoryPrevious(); /// /// Finishes the ReadLine input, instructing the handler to return the text as it is /// public static void Finish(IKeyBehaviorTarget target) => target.Finish(); /// /// Removes all of the text between the cursor and the end of the line /// /// public static void CutToEnd(IKeyBehaviorTarget target) { var cursor = target.CursorPosition; var captured = target.TextBuffer.ToString().Substring(0, cursor); target.TextBuffer.Clear(); target.TextBuffer.Append(captured); target.CursorPosition = cursor; } /// /// Removes all of the text between the cursor and the start of the line /// /// public static void CutToStart(IKeyBehaviorTarget target) { var cursor = target.CursorPosition; var captured = target.TextBuffer.ToString() .Substring(cursor, target.TextBuffer.Length - cursor); target.TextBuffer.Clear(); target.TextBuffer.Append(captured); target.CursorPosition = 0; } /// /// Using a basic split on whitespace strategy, this removes all of the text between the cursor /// and the end of the previous word /// /// public static void CutPreviousWord(IKeyBehaviorTarget target) { var tokens = CommonLexers.SplitOnWhitespace(new LineState(target.TextBuffer.ToString(), target.CursorPosition)); int cursor = (int) tokens.CursorToken.Cursor; var token = tokens.CursorToken; var previous = tokens.CursorToken.Previous; if (cursor == 0 && previous?.IsHidden == true) { // Example for "test data here", which should delete back to the beginning // ↑ previous.Text = ""; if (previous.Previous != null) { previous.Previous.Text = ""; } } else { var captured = token.Text.Substring(cursor, token.Text.Length - cursor); token.Cursor = 0; token.Text = captured; if (token.IsHidden && previous != null) { previous.Text = ""; } } target.TextBuffer.Clear(); target.TextBuffer.Append(tokens.Text); target.CursorPosition = tokens.Cursor; } /// /// Inserts the received character at the cursor position if the character is a letter, digit, whitespace, /// punctuation, or symbol. Advances the cursor by one. /// public static void InsertCharacter(IKeyBehaviorTarget target) { if (char.IsLetterOrDigit(target.ReceivedKey.KeyChar) || char.IsWhiteSpace(target.ReceivedKey.KeyChar) || char.IsPunctuation(target.ReceivedKey.KeyChar) || char.IsSymbol(target.ReceivedKey.KeyChar)) { target.TextBuffer.Insert(target.CursorPosition, target.ReceivedKey.KeyChar); target.CursorPosition++; } } } }