mirror of https://github.com/adamdruppe/arsd.git
big improvements to getline
This commit is contained in:
parent
2123bf0db4
commit
8142c56e6d
260
terminal.d
260
terminal.d
|
@ -185,7 +185,10 @@ version(Posix) {
|
||||||
// capabilities.
|
// capabilities.
|
||||||
//version = Demo
|
//version = Demo
|
||||||
|
|
||||||
version(Windows) {
|
version(Windows)
|
||||||
|
version=Win32Console;
|
||||||
|
|
||||||
|
version(Win32Console) {
|
||||||
import core.sys.windows.windows;
|
import core.sys.windows.windows;
|
||||||
import std.string : toStringz;
|
import std.string : toStringz;
|
||||||
private {
|
private {
|
||||||
|
@ -407,6 +410,8 @@ enum ConsoleInputFlags {
|
||||||
|
|
||||||
allInputEvents = 8|4|2, /// subscribe to all input events. Note: in previous versions, this also returned release events. It no longer does, use allInputEventsWithRelease if you want them.
|
allInputEvents = 8|4|2, /// subscribe to all input events. Note: in previous versions, this also returned release events. It no longer does, use allInputEventsWithRelease if you want them.
|
||||||
allInputEventsWithRelease = allInputEvents|releasedKeys, /// subscribe to all input events, including (unreliable on Posix) key release events.
|
allInputEventsWithRelease = allInputEvents|releasedKeys, /// subscribe to all input events, including (unreliable on Posix) key release events.
|
||||||
|
|
||||||
|
noEolWrap = 128,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines how terminal output should be handled.
|
/// Defines how terminal output should be handled.
|
||||||
|
@ -440,19 +445,34 @@ struct Terminal {
|
||||||
/++
|
/++
|
||||||
Terminal is only valid to use on an actual console device or terminal
|
Terminal is only valid to use on an actual console device or terminal
|
||||||
handle. You should not attempt to construct a Terminal instance if this
|
handle. You should not attempt to construct a Terminal instance if this
|
||||||
returns false;
|
returns false. Real time input is similarly impossible if `!stdinIsTerminal`.
|
||||||
+/
|
+/
|
||||||
static bool stdoutIsTerminal() {
|
static bool stdoutIsTerminal() {
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
import core.sys.posix.unistd;
|
import core.sys.posix.unistd;
|
||||||
return cast(bool) isatty(1);
|
return cast(bool) isatty(1);
|
||||||
} else version(Windows) {
|
} else version(Win32Console) {
|
||||||
|
auto hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
return GetFileType(hConsole) == FILE_TYPE_CHAR;
|
||||||
|
/+
|
||||||
auto hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
auto hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
||||||
if(GetConsoleScreenBufferInfo(hConsole, &originalSbi) == 0)
|
if(GetConsoleScreenBufferInfo(hConsole, &originalSbi) == 0)
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
|
+/
|
||||||
|
} else static assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
static bool stdinIsTerminal() {
|
||||||
|
version(Posix) {
|
||||||
|
import core.sys.posix.unistd;
|
||||||
|
return cast(bool) isatty(0);
|
||||||
|
} else version(Win32Console) {
|
||||||
|
auto hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
return GetFileType(hConsole) == FILE_TYPE_CHAR;
|
||||||
} else static assert(0);
|
} else static assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,12 +803,12 @@ struct Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
HANDLE hConsole;
|
HANDLE hConsole;
|
||||||
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
/// ditto
|
/// ditto
|
||||||
this(ConsoleOutputType type) {
|
this(ConsoleOutputType type) {
|
||||||
if(type == ConsoleOutputType.cellular) {
|
if(type == ConsoleOutputType.cellular) {
|
||||||
|
@ -839,7 +859,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
private Color defaultBackgroundColor = Color.black;
|
private Color defaultBackgroundColor = Color.black;
|
||||||
private Color defaultForegroundColor = Color.white;
|
private Color defaultForegroundColor = Color.white;
|
||||||
UINT oldCp;
|
UINT oldCp;
|
||||||
|
@ -1062,7 +1082,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// Returns the terminal to normal output colors
|
/// Returns the terminal to normal output colors
|
||||||
void reset() {
|
void reset() {
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
SetConsoleTextAttribute(
|
SetConsoleTextAttribute(
|
||||||
hConsole,
|
hConsole,
|
||||||
originalSbi.wAttributes);
|
originalSbi.wAttributes);
|
||||||
|
@ -1096,7 +1116,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
executeAutoHideCursor();
|
executeAutoHideCursor();
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
doTermcap("cm", y, x);
|
doTermcap("cm", y, x);
|
||||||
} else version(Windows) {
|
} else version(Win32Console) {
|
||||||
|
|
||||||
flush(); // if we don't do this now, the buffering can screw up the position
|
flush(); // if we don't do this now, the buffering can screw up the position
|
||||||
COORD coord = {cast(short) x, cast(short) y};
|
COORD coord = {cast(short) x, cast(short) y};
|
||||||
|
@ -1144,7 +1164,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
private void executeAutoHideCursor() {
|
private void executeAutoHideCursor() {
|
||||||
if(autoHidingCursor) {
|
if(autoHidingCursor) {
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
hideCursor();
|
hideCursor();
|
||||||
else version(Posix) {
|
else version(Posix) {
|
||||||
// prepend the hide cursor command so it is the first thing flushed
|
// prepend the hide cursor command so it is the first thing flushed
|
||||||
|
@ -1178,7 +1198,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
|
|
||||||
/// Changes the terminal's title
|
/// Changes the terminal's title
|
||||||
void setTitle(string t) {
|
void setTitle(string t) {
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
|
// FIXME: use the W version
|
||||||
SetConsoleTitleA(toStringz(t));
|
SetConsoleTitleA(toStringz(t));
|
||||||
} else {
|
} else {
|
||||||
import std.string;
|
import std.string;
|
||||||
|
@ -1206,7 +1227,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
writeBuffer = writeBuffer[written .. $];
|
writeBuffer = writeBuffer[written .. $];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else version(Windows) {
|
} else version(Win32Console) {
|
||||||
import std.conv;
|
import std.conv;
|
||||||
// FIXME: I'm not sure I'm actually happy with this allocation but
|
// FIXME: I'm not sure I'm actually happy with this allocation but
|
||||||
// it probably isn't a big deal. At least it has unicode support now.
|
// it probably isn't a big deal. At least it has unicode support now.
|
||||||
|
@ -1222,7 +1243,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] getSize() {
|
int[] getSize() {
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
GetConsoleScreenBufferInfo( hConsole, &info );
|
GetConsoleScreenBufferInfo( hConsole, &info );
|
||||||
|
|
||||||
|
@ -1374,7 +1395,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
// FIXME: make sure all the data is sent, check for errors
|
// FIXME: make sure all the data is sent, check for errors
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
writeBuffer ~= s; // buffer it to do everything at once in flush() calls
|
writeBuffer ~= s; // buffer it to do everything at once in flush() calls
|
||||||
} else version(Windows) {
|
} else version(Win32Console) {
|
||||||
writeBuffer ~= s;
|
writeBuffer ~= s;
|
||||||
} else static assert(0);
|
} else static assert(0);
|
||||||
}
|
}
|
||||||
|
@ -1383,7 +1404,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
||||||
void clear() {
|
void clear() {
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
doTermcap("cl");
|
doTermcap("cl");
|
||||||
} else version(Windows) {
|
} else version(Win32Console) {
|
||||||
// http://support.microsoft.com/kb/99261
|
// http://support.microsoft.com/kb/99261
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
|
@ -1474,7 +1495,7 @@ struct RealTimeConsoleInput {
|
||||||
// so this hack is just to give some room for that to happen without destroying the rest of the world
|
// so this hack is just to give some room for that to happen without destroying the rest of the world
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
private DWORD oldInput;
|
private DWORD oldInput;
|
||||||
private DWORD oldOutput;
|
private DWORD oldOutput;
|
||||||
HANDLE inputHandle;
|
HANDLE inputHandle;
|
||||||
|
@ -1489,7 +1510,7 @@ struct RealTimeConsoleInput {
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
this.terminal = terminal;
|
this.terminal = terminal;
|
||||||
|
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
inputHandle = GetStdHandle(STD_INPUT_HANDLE);
|
inputHandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
|
||||||
GetConsoleMode(inputHandle, &oldInput);
|
GetConsoleMode(inputHandle, &oldInput);
|
||||||
|
@ -1512,11 +1533,10 @@ struct RealTimeConsoleInput {
|
||||||
mode = 0;
|
mode = 0;
|
||||||
// we want this to match linux too
|
// we want this to match linux too
|
||||||
mode |= ENABLE_PROCESSED_OUTPUT; /* 0x01 */
|
mode |= ENABLE_PROCESSED_OUTPUT; /* 0x01 */
|
||||||
mode |= ENABLE_WRAP_AT_EOL_OUTPUT; /* 0x02 */
|
if(!(flags & ConsoleInputFlags.noEolWrap))
|
||||||
|
mode |= ENABLE_WRAP_AT_EOL_OUTPUT; /* 0x02 */
|
||||||
SetConsoleMode(terminal.hConsole, mode);
|
SetConsoleMode(terminal.hConsole, mode);
|
||||||
destructor ~= { SetConsoleMode(terminal.hConsole, oldOutput); };
|
destructor ~= { SetConsoleMode(terminal.hConsole, oldOutput); };
|
||||||
|
|
||||||
// FIXME: change to UTF8 as well
|
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Posix) {
|
version(Posix) {
|
||||||
|
@ -1607,7 +1627,7 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
version(with_eventloop) {
|
version(with_eventloop) {
|
||||||
import arsd.eventloop;
|
import arsd.eventloop;
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
auto listenTo = inputHandle;
|
auto listenTo = inputHandle;
|
||||||
else version(Posix)
|
else version(Posix)
|
||||||
auto listenTo = this.fdIn;
|
auto listenTo = this.fdIn;
|
||||||
|
@ -1646,7 +1666,7 @@ struct RealTimeConsoleInput {
|
||||||
void integrateWithSimpleDisplayEventLoop()(void delegate(InputEvent) userEventHandler) {
|
void integrateWithSimpleDisplayEventLoop()(void delegate(InputEvent) userEventHandler) {
|
||||||
this.userEventHandler = userEventHandler;
|
this.userEventHandler = userEventHandler;
|
||||||
import arsd.simpledisplay;
|
import arsd.simpledisplay;
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
auto listener = new WindowsHandleReader(&fdReadyReader, terminal.hConsole);
|
auto listener = new WindowsHandleReader(&fdReadyReader, terminal.hConsole);
|
||||||
else version(linux)
|
else version(linux)
|
||||||
auto listener = new PosixFdReader(&fdReadyReader, fdIn);
|
auto listener = new PosixFdReader(&fdReadyReader, fdIn);
|
||||||
|
@ -1729,7 +1749,7 @@ struct RealTimeConsoleInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool timedCheckForInput_bypassingBuffer(int milliseconds) {
|
bool timedCheckForInput_bypassingBuffer(int milliseconds) {
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
auto response = WaitForSingleObject(terminal.hConsole, milliseconds);
|
auto response = WaitForSingleObject(terminal.hConsole, milliseconds);
|
||||||
if(response == 0)
|
if(response == 0)
|
||||||
return true; // the object is ready
|
return true; // the object is ready
|
||||||
|
@ -1926,7 +1946,7 @@ struct RealTimeConsoleInput {
|
||||||
|
|
||||||
InputEvent[] inputQueue;
|
InputEvent[] inputQueue;
|
||||||
|
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
InputEvent[] readNextEvents() {
|
InputEvent[] readNextEvents() {
|
||||||
terminal.flush(); // make sure all output is sent out before waiting for anything
|
terminal.flush(); // make sure all output is sent out before waiting for anything
|
||||||
|
|
||||||
|
@ -2591,7 +2611,7 @@ struct EndOfFileEvent {}
|
||||||
|
|
||||||
interface CustomEvent {}
|
interface CustomEvent {}
|
||||||
|
|
||||||
version(Windows)
|
version(Win32Console)
|
||||||
enum ModifierState : uint {
|
enum ModifierState : uint {
|
||||||
shift = 0x10,
|
shift = 0x10,
|
||||||
control = 0x8 | 0x4, // 8 == left ctrl, 4 == right ctrl
|
control = 0x8 | 0x4, // 8 == left ctrl, 4 == right ctrl
|
||||||
|
@ -2967,9 +2987,11 @@ class LineGetter {
|
||||||
|
|
||||||
/// You can customize the colors here. You should set these after construction, but before
|
/// You can customize the colors here. You should set these after construction, but before
|
||||||
/// calling startGettingLine or getline.
|
/// calling startGettingLine or getline.
|
||||||
Color suggestionForeground;
|
Color suggestionForeground = Color.blue;
|
||||||
Color regularForeground; /// .
|
Color regularForeground = Color.DEFAULT; /// ditto
|
||||||
Color background; /// .
|
Color background = Color.DEFAULT; /// ditto
|
||||||
|
Color promptColor = Color.DEFAULT; /// ditto
|
||||||
|
Color specialCharBackground = Color.green; /// ditto
|
||||||
//bool reverseVideo;
|
//bool reverseVideo;
|
||||||
|
|
||||||
/// Set this if you want a prompt to be drawn with the line. It does NOT support color in string.
|
/// Set this if you want a prompt to be drawn with the line. It does NOT support color in string.
|
||||||
|
@ -3071,6 +3093,8 @@ class LineGetter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private RealTimeConsoleInput* rtci;
|
||||||
|
|
||||||
/// One-call shop for the main workhorse
|
/// One-call shop for the main workhorse
|
||||||
/// If you already have a RealTimeConsoleInput ready to go, you
|
/// If you already have a RealTimeConsoleInput ready to go, you
|
||||||
/// should pass a pointer to yours here. Otherwise, LineGetter will
|
/// should pass a pointer to yours here. Otherwise, LineGetter will
|
||||||
|
@ -3078,10 +3102,15 @@ class LineGetter {
|
||||||
public string getline(RealTimeConsoleInput* input = null) {
|
public string getline(RealTimeConsoleInput* input = null) {
|
||||||
startGettingLine();
|
startGettingLine();
|
||||||
if(input is null) {
|
if(input is null) {
|
||||||
auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents);
|
auto i = RealTimeConsoleInput(terminal, ConsoleInputFlags.raw | ConsoleInputFlags.allInputEvents | ConsoleInputFlags.noEolWrap);
|
||||||
|
//rtci = &i;
|
||||||
|
//scope(exit) rtci = null;
|
||||||
while(workOnLine(i.nextEvent())) {}
|
while(workOnLine(i.nextEvent())) {}
|
||||||
} else
|
} else {
|
||||||
|
//rtci = input;
|
||||||
|
//scope(exit) rtci = null;
|
||||||
while(workOnLine(input.nextEvent())) {}
|
while(workOnLine(input.nextEvent())) {}
|
||||||
|
}
|
||||||
return finishGettingLine();
|
return finishGettingLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3231,19 +3260,53 @@ class LineGetter {
|
||||||
if(lineLength < 0)
|
if(lineLength < 0)
|
||||||
throw new Exception("too narrow terminal to draw");
|
throw new Exception("too narrow terminal to draw");
|
||||||
|
|
||||||
|
terminal.color(promptColor, background);
|
||||||
terminal.write(prompt);
|
terminal.write(prompt);
|
||||||
|
terminal.color(regularForeground, background);
|
||||||
|
|
||||||
auto towrite = line[horizontalScrollPosition .. $];
|
auto towrite = line[horizontalScrollPosition .. $];
|
||||||
auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition;
|
auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition;
|
||||||
auto cursorPositionToDrawY = 0;
|
auto cursorPositionToDrawY = 0;
|
||||||
|
|
||||||
if(towrite.length > lineLength) {
|
int written = cast(int) prompt.length;
|
||||||
towrite = towrite[0 .. lineLength];
|
|
||||||
|
void specialChar(char c) {
|
||||||
|
terminal.color(regularForeground, specialCharBackground);
|
||||||
|
terminal.write(c);
|
||||||
|
terminal.color(regularForeground, background);
|
||||||
|
|
||||||
|
written++;
|
||||||
|
lineLength--;
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.write(towrite);
|
void regularChar(dchar ch) {
|
||||||
|
import std.utf;
|
||||||
|
char[4] buffer;
|
||||||
|
auto l = encode(buffer, ch);
|
||||||
|
// note the Terminal buffers it so meh
|
||||||
|
terminal.write(buffer[0 .. l]);
|
||||||
|
|
||||||
lineLength -= towrite.length;
|
written++;
|
||||||
|
lineLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: if there is a color at the end of the line it messes up as you scroll
|
||||||
|
// FIXME: need a way to go to multi-line editing
|
||||||
|
|
||||||
|
foreach(dchar ch; towrite) {
|
||||||
|
if(lineLength == 0)
|
||||||
|
break;
|
||||||
|
switch(ch) {
|
||||||
|
case '\n': specialChar('n'); break;
|
||||||
|
case '\r': specialChar('r'); break;
|
||||||
|
case '\a': specialChar('a'); break;
|
||||||
|
case '\t': specialChar('t'); break;
|
||||||
|
case '\b': specialChar('b'); break;
|
||||||
|
case '\033': specialChar('e'); break;
|
||||||
|
default:
|
||||||
|
regularChar(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string suggestion;
|
string suggestion;
|
||||||
|
|
||||||
|
@ -3251,13 +3314,16 @@ class LineGetter {
|
||||||
suggestion = ((cursorPosition == towrite.length) && autoSuggest) ? this.suggestion() : null;
|
suggestion = ((cursorPosition == towrite.length) && autoSuggest) ? this.suggestion() : null;
|
||||||
if(suggestion.length) {
|
if(suggestion.length) {
|
||||||
terminal.color(suggestionForeground, background);
|
terminal.color(suggestionForeground, background);
|
||||||
terminal.write(suggestion);
|
foreach(dchar ch; suggestion) {
|
||||||
|
if(lineLength == 0)
|
||||||
|
break;
|
||||||
|
regularChar(ch);
|
||||||
|
}
|
||||||
terminal.color(regularForeground, background);
|
terminal.color(regularForeground, background);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: graphemes and utf-8 on suggestion/prompt
|
// FIXME: graphemes and utf-8 on suggestion/prompt
|
||||||
auto written = cast(int) (towrite.length + suggestion.length + prompt.length);
|
|
||||||
|
|
||||||
if(written < lastDrawLength)
|
if(written < lastDrawLength)
|
||||||
foreach(i; written .. lastDrawLength)
|
foreach(i; written .. lastDrawLength)
|
||||||
|
@ -3282,10 +3348,40 @@ class LineGetter {
|
||||||
line.assumeSafeAppend();
|
line.assumeSafeAppend();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCursorPosition();
|
initializeWithSize(true);
|
||||||
terminal.showCursor();
|
|
||||||
|
terminal.showCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void positionCursor() {
|
||||||
|
if(cursorPosition == 0)
|
||||||
|
horizontalScrollPosition = 0;
|
||||||
|
else if(cursorPosition == line.length)
|
||||||
|
scrollToEnd();
|
||||||
|
else {
|
||||||
|
// otherwise just try to center it in the screen
|
||||||
|
horizontalScrollPosition = cursorPosition;
|
||||||
|
horizontalScrollPosition -= terminal.width / 2;
|
||||||
|
// align on a code point boundary
|
||||||
|
while(horizontalScrollPosition > 0 && (line[horizontalScrollPosition] & 0x80))
|
||||||
|
horizontalScrollPosition--;
|
||||||
|
if(horizontalScrollPosition < 0)
|
||||||
|
horizontalScrollPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeWithSize(bool firstEver = false) {
|
||||||
|
auto x = startOfLineX;
|
||||||
|
|
||||||
|
updateCursorPosition();
|
||||||
|
|
||||||
|
if(!firstEver) {
|
||||||
|
startOfLineX = x;
|
||||||
|
positionCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDrawLength = terminal.width;
|
||||||
|
|
||||||
lastDrawLength = availableLineLength();
|
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3293,7 +3389,7 @@ class LineGetter {
|
||||||
terminal.flush();
|
terminal.flush();
|
||||||
|
|
||||||
// then get the current cursor position to start fresh
|
// then get the current cursor position to start fresh
|
||||||
version(Windows) {
|
version(Win32Console) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||||
GetConsoleScreenBufferInfo(terminal.hConsole, &info);
|
GetConsoleScreenBufferInfo(terminal.hConsole, &info);
|
||||||
startOfLineX = info.dwCursorPosition.X;
|
startOfLineX = info.dwCursorPosition.X;
|
||||||
|
@ -3307,6 +3403,13 @@ class LineGetter {
|
||||||
// We also can't use RealTimeConsoleInput here because it also does event loop stuff
|
// We also can't use RealTimeConsoleInput here because it also does event loop stuff
|
||||||
// which would be broken by the child destructor :( (maybe that should be a FIXME)
|
// which would be broken by the child destructor :( (maybe that should be a FIXME)
|
||||||
|
|
||||||
|
/+
|
||||||
|
if(rtci !is null) {
|
||||||
|
while(rtci.timedCheckForInput_bypassingBuffer(1000))
|
||||||
|
rtci.inputQueue ~= rtci.readNextEvents();
|
||||||
|
}
|
||||||
|
+/
|
||||||
|
|
||||||
ubyte[128] hack2;
|
ubyte[128] hack2;
|
||||||
termios old;
|
termios old;
|
||||||
ubyte[128] hack;
|
ubyte[128] hack;
|
||||||
|
@ -3321,20 +3424,38 @@ class LineGetter {
|
||||||
terminal.writeStringRaw("\033[6n");
|
terminal.writeStringRaw("\033[6n");
|
||||||
terminal.flush();
|
terminal.flush();
|
||||||
|
|
||||||
|
import std.conv;
|
||||||
|
import core.stdc.errno;
|
||||||
|
|
||||||
import core.sys.posix.unistd;
|
import core.sys.posix.unistd;
|
||||||
// reading directly to bypass any buffering
|
// reading directly to bypass any buffering
|
||||||
|
int retries = 16;
|
||||||
ubyte[16] buffer;
|
ubyte[16] buffer;
|
||||||
|
try_again:
|
||||||
auto len = read(terminal.fdIn, buffer.ptr, buffer.length);
|
auto len = read(terminal.fdIn, buffer.ptr, buffer.length);
|
||||||
if(len <= 0)
|
if(len <= 0) {
|
||||||
throw new Exception("Couldn't get cursor position to initialize get line");
|
if(len == -1) {
|
||||||
|
if(errno == EINTR)
|
||||||
|
goto try_again;
|
||||||
|
if(errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
import core.thread;
|
||||||
|
Thread.sleep(10.msecs);
|
||||||
|
goto try_again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("Couldn't get cursor position to initialize get line " ~ to!string(len) ~ " " ~ to!string(errno));
|
||||||
|
}
|
||||||
auto got = buffer[0 .. len];
|
auto got = buffer[0 .. len];
|
||||||
if(got.length < 6)
|
if(got.length < 6)
|
||||||
throw new Exception("not enough cursor reply answer");
|
throw new Exception("not enough cursor reply answer");
|
||||||
if(got[0] != '\033' || got[1] != '[' || got[$-1] != 'R')
|
if(got[0] != '\033' || got[1] != '[' || got[$-1] != 'R') {
|
||||||
throw new Exception("wrong answer for cursor position");
|
retries--;
|
||||||
|
if(retries > 0)
|
||||||
|
goto try_again;
|
||||||
|
throw new Exception("wrong answer for cursor position " ~ cast(string) got[1 .. $]);
|
||||||
|
}
|
||||||
auto gots = cast(char[]) got[2 .. $-1];
|
auto gots = cast(char[]) got[2 .. $-1];
|
||||||
|
|
||||||
import std.conv;
|
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
||||||
auto pieces = split(gots, ";");
|
auto pieces = split(gots, ";");
|
||||||
|
@ -3350,6 +3471,14 @@ class LineGetter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool justHitTab;
|
private bool justHitTab;
|
||||||
|
private bool eof;
|
||||||
|
|
||||||
|
///
|
||||||
|
string delegate(string s) pastePreprocessor;
|
||||||
|
|
||||||
|
string defaultPastePreprocessor(string s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/// for integrating into another event loop
|
/// for integrating into another event loop
|
||||||
/// you can pass individual events to this and
|
/// you can pass individual events to this and
|
||||||
|
@ -3360,6 +3489,7 @@ class LineGetter {
|
||||||
switch(e.type) {
|
switch(e.type) {
|
||||||
case InputEvent.Type.EndOfFileEvent:
|
case InputEvent.Type.EndOfFileEvent:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
|
eof = true;
|
||||||
// FIXME: this should be distinct from an empty line when hit at the beginning
|
// FIXME: this should be distinct from an empty line when hit at the beginning
|
||||||
return false;
|
return false;
|
||||||
//break;
|
//break;
|
||||||
|
@ -3371,6 +3501,9 @@ class LineGetter {
|
||||||
auto ch = ev.which;
|
auto ch = ev.which;
|
||||||
switch(ch) {
|
switch(ch) {
|
||||||
case 4: // ctrl+d will also send a newline-equivalent
|
case 4: // ctrl+d will also send a newline-equivalent
|
||||||
|
if(line.length == 0)
|
||||||
|
eof = true;
|
||||||
|
goto case;
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
|
@ -3425,6 +3558,10 @@ class LineGetter {
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
if(cursorPosition)
|
if(cursorPosition)
|
||||||
cursorPosition--;
|
cursorPosition--;
|
||||||
|
if(ev.modifierState & ModifierState.control) {
|
||||||
|
while(cursorPosition && line[cursorPosition - 1] != ' ')
|
||||||
|
cursorPosition--;
|
||||||
|
}
|
||||||
if(!multiLineMode) {
|
if(!multiLineMode) {
|
||||||
if(cursorPosition < horizontalScrollPosition)
|
if(cursorPosition < horizontalScrollPosition)
|
||||||
horizontalScrollPosition--;
|
horizontalScrollPosition--;
|
||||||
|
@ -3436,6 +3573,14 @@ class LineGetter {
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
if(cursorPosition < line.length)
|
if(cursorPosition < line.length)
|
||||||
cursorPosition++;
|
cursorPosition++;
|
||||||
|
|
||||||
|
if(ev.modifierState & ModifierState.control) {
|
||||||
|
while(cursorPosition + 1 < line.length && line[cursorPosition + 1] != ' ')
|
||||||
|
cursorPosition++;
|
||||||
|
cursorPosition += 2;
|
||||||
|
if(cursorPosition > line.length)
|
||||||
|
cursorPosition = cast(int) line.length;
|
||||||
|
}
|
||||||
if(!multiLineMode) {
|
if(!multiLineMode) {
|
||||||
if(cursorPosition >= horizontalScrollPosition + availableLineLength())
|
if(cursorPosition >= horizontalScrollPosition + availableLineLength())
|
||||||
horizontalScrollPosition++;
|
horizontalScrollPosition++;
|
||||||
|
@ -3479,9 +3624,22 @@ class LineGetter {
|
||||||
break;
|
break;
|
||||||
case KeyboardEvent.Key.Insert:
|
case KeyboardEvent.Key.Insert:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
insertMode = !insertMode;
|
if(ev.modifierState & ModifierState.shift) {
|
||||||
// FIXME: indicate this on the UI somehow
|
// shift+insert = request paste
|
||||||
// like change the cursor or something
|
// ctrl+insert = request copy. but that needs a selection
|
||||||
|
|
||||||
|
// those work on Windows!!!!o
|
||||||
|
|
||||||
|
/*
|
||||||
|
change cursor capabilitiy
|
||||||
|
figure out windows alt+codes
|
||||||
|
*/
|
||||||
|
|
||||||
|
} else {
|
||||||
|
insertMode = !insertMode;
|
||||||
|
// FIXME: indicate this on the UI somehow
|
||||||
|
// like change the cursor or something
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyboardEvent.Key.Delete:
|
case KeyboardEvent.Key.Delete:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
|
@ -3505,7 +3663,10 @@ class LineGetter {
|
||||||
break;
|
break;
|
||||||
case InputEvent.Type.PasteEvent:
|
case InputEvent.Type.PasteEvent:
|
||||||
justHitTab = false;
|
justHitTab = false;
|
||||||
addString(e.pasteEvent.pastedText);
|
if(pastePreprocessor)
|
||||||
|
addString(pastePreprocessor(e.pasteEvent.pastedText));
|
||||||
|
else
|
||||||
|
addString(defaultPastePreprocessor(e.pasteEvent.pastedText));
|
||||||
redraw();
|
redraw();
|
||||||
break;
|
break;
|
||||||
case InputEvent.Type.MouseEvent:
|
case InputEvent.Type.MouseEvent:
|
||||||
|
@ -3531,6 +3692,7 @@ class LineGetter {
|
||||||
/* We'll adjust the bounding box. If you don't like this, handle SizeChangedEvent
|
/* We'll adjust the bounding box. If you don't like this, handle SizeChangedEvent
|
||||||
yourself and then don't pass it to this function. */
|
yourself and then don't pass it to this function. */
|
||||||
// FIXME
|
// FIXME
|
||||||
|
initializeWithSize();
|
||||||
break;
|
break;
|
||||||
case InputEvent.Type.UserInterruptionEvent:
|
case InputEvent.Type.UserInterruptionEvent:
|
||||||
/* I'll take this as canceling the line. */
|
/* I'll take this as canceling the line. */
|
||||||
|
@ -3555,7 +3717,7 @@ class LineGetter {
|
||||||
this.history ~= history;
|
this.history ~= history;
|
||||||
|
|
||||||
// FIXME: we should hide the cursor if it was hidden in the call to startGettingLine
|
// FIXME: we should hide the cursor if it was hidden in the call to startGettingLine
|
||||||
return f;
|
return eof ? null : f.length ? f : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue