Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve screen repaint speed #45

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/ReadLine.Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace ConsoleApplication
{
Expand All @@ -10,7 +11,7 @@ public static void Main(string[] args)
Console.WriteLine("---------------------");
Console.WriteLine();

string[] history = new string[] { "ls -a", "dotnet run", "git init" };
var history = new List<string> { "ls -a", "dotnet run", "git init" };
ReadLine.AddHistory(history);

ReadLine.AutoCompletionHandler = new AutoCompletionHandler();
Expand All @@ -31,7 +32,7 @@ public string[] GetSuggestions(string text, int index)
if (text.StartsWith("git "))
return new string[] { "init", "clone", "pull", "push" };
else
return null;
return new string[] { "git", "ls", "cd", "pwd" };
}
}
}
179 changes: 96 additions & 83 deletions src/ReadLine/KeyHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ namespace Internal.ReadLine
{
internal class KeyHandler
{
private int _cursorPos;
private int _cursorLimit;
private int _promptLength;
private StringBuilder _text;
private List<string> _history;
private int _historyIndex;
Expand All @@ -20,9 +19,9 @@ internal class KeyHandler
private int _completionsIndex;
private IConsole Console2;

private bool IsStartOfLine() => _cursorPos == 0;
private bool IsStartOfLine() => Console2.CursorLeft == 0;

private bool IsEndOfLine() => _cursorPos == _cursorLimit;
private bool IsEndOfLine() => Console2.CursorLeft == _text.Length + _promptLength;

private bool IsStartOfBuffer() => Console2.CursorLeft == 0;

Expand All @@ -34,18 +33,18 @@ private void MoveCursorLeft()
if (IsStartOfLine())
return;

if (Console2.CursorLeft == _promptLength)
return;

if (IsStartOfBuffer())
Console2.SetCursorPosition(Console2.BufferWidth - 1, Console2.CursorTop - 1);
else
Console2.SetCursorPosition(Console2.CursorLeft - 1, Console2.CursorTop);

_cursorPos--;
}

private void MoveCursorHome()
{
while (!IsStartOfLine())
MoveCursorLeft();
Console2.SetCursorPosition(_promptLength, Console2.CursorTop);
}

private string BuildKeyInput()
Expand All @@ -57,14 +56,18 @@ private string BuildKeyInput()
private void MoveCursorRight()
{
if (IsEndOfLine())
{
return;
}

if (IsEndOfBuffer())
{
Console2.SetCursorPosition(0, Console2.CursorTop + 1);
}
else
{
Console2.SetCursorPosition(Console2.CursorLeft + 1, Console2.CursorTop);

_cursorPos++;
}
}

private void MoveCursorEnd()
Expand All @@ -75,148 +78,157 @@ private void MoveCursorEnd()

private void ClearLine()
{
MoveCursorEnd();
while (!IsStartOfLine())
Backspace();
ClearLine(_promptLength);
}

private void WriteNewString(string str)
private void ClearLine(int startPos)
{
ClearLine();
foreach (char character in str)
WriteChar(character);
var clear = "";
for (var i = 0; i < Console2.BufferWidth - startPos; i++)
{
clear += " ";
}
Console2.SetCursorPosition(startPos, Console2.CursorTop);
Console2.Write(clear);
Console2.SetCursorPosition(startPos, Console2.CursorTop);
}

private void WriteString(string str)
{
foreach (char character in str)
WriteChar(character);
_text.Clear();
_text.Append(str);
Console2.Write(_text.ToString());
}

private void WriteChar() => WriteChar(_keyInfo.KeyChar);

private void WriteChar(char c)
{
if (IsEndOfLine())
if (Console2.CursorLeft >= _text.Length + _promptLength)
{
_text.Append(c);
Console2.Write(c.ToString());
_cursorPos++;
}
else
{
int left = Console2.CursorLeft;
int top = Console2.CursorTop;
string str = _text.ToString().Substring(_cursorPos);
_text.Insert(_cursorPos, c);
Console2.Write(c.ToString() + str);
Console2.SetCursorPosition(left, top);
MoveCursorRight();
int origPos = Console2.CursorLeft;
_text.Insert(origPos - _promptLength, c);
ClearLine();
Console2.Write(_text.ToString());
Console2.SetCursorPosition(origPos + 1, Console2.CursorTop);
}

_cursorLimit++;
}

private void Backspace()
{
if (IsStartOfLine())
{
ResetAutoComplete();
return;

MoveCursorLeft();
int index = _cursorPos;
_text.Remove(index, 1);
string replacement = _text.ToString().Substring(index);
int left = Console2.CursorLeft;
int top = Console2.CursorTop;
Console2.Write(string.Format("{0} ", replacement));
Console2.SetCursorPosition(left, top);
_cursorLimit--;
}
if (Console2.CursorLeft > _promptLength)
{
int origPos = Console2.CursorLeft;
_text.Remove(origPos - _promptLength - 1, 1);
ClearLine();
WriteString(_text.ToString());
Console2.SetCursorPosition(origPos -1, Console2.CursorTop);
}
}

private void Delete()
{
if (IsEndOfLine())
{
return;
}

int index = _cursorPos;
_text.Remove(index, 1);
string replacement = _text.ToString().Substring(index);
int left = Console2.CursorLeft;
int top = Console2.CursorTop;
Console2.Write(string.Format("{0} ", replacement));
Console2.SetCursorPosition(left, top);
_cursorLimit--;
int origPos = Console2.CursorLeft;
if ((Console2.CursorLeft - _promptLength) < _text.Length)
{
_text.Remove(Console2.CursorLeft - _promptLength, 1);
ClearLine();
WriteString(_text.ToString());
Console2.SetCursorPosition(origPos, Console2.CursorTop);
}
}

private void TransposeChars()
{
// local helper functions
bool almostEndOfLine() => (_cursorLimit - _cursorPos) == 1;
bool almostEndOfLine() => (Console2.BufferWidth - Console2.CursorLeft) == 1;
int incrementIf(Func<bool> expression, int index) => expression() ? index + 1 : index;
int decrementIf(Func<bool> expression, int index) => expression() ? index - 1 : index;

if (IsStartOfLine()) { return; }

var firstIdx = decrementIf(IsEndOfLine, _cursorPos - 1);
var secondIdx = decrementIf(IsEndOfLine, _cursorPos);
var firstIdx = decrementIf(IsEndOfLine, Console2.CursorLeft - 1);
var secondIdx = decrementIf(IsEndOfLine, Console2.CursorLeft);

var secondChar = _text[secondIdx];
_text[secondIdx] = _text[firstIdx];
_text[firstIdx] = secondChar;

var left = incrementIf(almostEndOfLine, Console2.CursorLeft);
var cursorPosition = incrementIf(almostEndOfLine, _cursorPos);
var cursorPosition = incrementIf(almostEndOfLine, Console2.CursorLeft);

WriteNewString(_text.ToString());
ClearLine();
WriteString(_text.ToString());

Console2.SetCursorPosition(left, Console2.CursorTop);
_cursorPos = cursorPosition;

MoveCursorRight();
}

private void StartAutoComplete()
{
while (_cursorPos > _completionStart)
Backspace();

ClearLine(_completionStart + _promptLength);
_completionsIndex = 0;

WriteString(_completions[_completionsIndex]);
WriteAutoComplete();
}

private void NextAutoComplete()
{
while (_cursorPos > _completionStart)
Backspace();

ClearLine(_completionStart + _promptLength);
_completionsIndex++;

if (_completionsIndex == _completions.Length)
_completionsIndex = 0;

WriteString(_completions[_completionsIndex]);
WriteAutoComplete();
}

private void PreviousAutoComplete()
{
while (_cursorPos > _completionStart)
Backspace();

ClearLine(_completionStart + _promptLength);
_completionsIndex--;

if (_completionsIndex == -1)
_completionsIndex = _completions.Length - 1;

WriteString(_completions[_completionsIndex]);
WriteAutoComplete();
}

private void WriteAutoComplete()
{
if (_text.ToString().Contains(" "))
{
var separator = _text.ToString().LastIndexOf(' ');
ClearLine();
WriteString(_text.ToString().Substring(0, separator) + " " + _completions[_completionsIndex]);
}
else
{
WriteString(_completions[_completionsIndex]);
}
}

private void PrevHistory()
{
if (_historyIndex > 0)
{
_historyIndex--;
WriteNewString(_history[_historyIndex]);
ClearLine();
WriteString(_history[_historyIndex]);
}
}

Expand All @@ -225,10 +237,11 @@ private void NextHistory()
if (_historyIndex < _history.Count)
{
_historyIndex++;
if (_historyIndex == _history.Count)
ClearLine();
else
WriteNewString(_history[_historyIndex]);
ClearLine();
if (_historyIndex != _history.Count)
{
WriteString(_history[_historyIndex]);
}
}
}

Expand All @@ -246,8 +259,9 @@ public string Text
}
}

public KeyHandler(IConsole console, List<string> history, IAutoCompleteHandler autoCompleteHandler)
public KeyHandler(IConsole console, List<string> history, IAutoCompleteHandler autoCompleteHandler, int promptLength)
{
_promptLength = promptLength;
Console2 = console;

_history = history ?? new List<string>();
Expand Down Expand Up @@ -280,14 +294,11 @@ public KeyHandler(IConsole console, List<string> history, IAutoCompleteHandler a
};
_keyActions["ControlK"] = () =>
{
int pos = _cursorPos;
MoveCursorEnd();
while (_cursorPos > pos)
Backspace();
ClearLine(Console2.CursorLeft);
};
_keyActions["ControlW"] = () =>
{
while (!IsStartOfLine() && _text[_cursorPos - 1] != ' ')
while (!IsStartOfLine() && _text[Console2.CursorLeft - 1] != ' ')
Backspace();
};
_keyActions["ControlT"] = TransposeChars;
Expand All @@ -300,7 +311,7 @@ public KeyHandler(IConsole console, List<string> history, IAutoCompleteHandler a
}
else
{
if (autoCompleteHandler == null || !IsEndOfLine())
if (autoCompleteHandler == null)
return;

string text = _text.ToString();
Expand Down Expand Up @@ -333,12 +344,14 @@ public void Handle(ConsoleKeyInfo keyInfo)

// If in auto complete mode and Tab wasn't pressed
if (IsInAutoCompleteMode() && _keyInfo.Key != ConsoleKey.Tab)
{
ResetAutoComplete();
}

Action action;
_keyActions.TryGetValue(BuildKeyInput(), out action);
action = action ?? WriteChar;
action.Invoke();
}
}
}
}
Loading