line getter initial impl

This commit is contained in:
Adam D. Ruppe 2021-11-24 17:37:48 -05:00
parent e017511fc2
commit 9797370ad6
1 changed files with 302 additions and 70 deletions

View File

@ -1272,9 +1272,7 @@ struct Terminal {
readTermcap(); readTermcap();
if(type == ConsoleOutputType.cellular) { if(type == ConsoleOutputType.cellular) {
doTermcap("ti"); goCellular();
clear();
moveTo(0, 0, ForceOption.alwaysSend); // we need to know where the cursor is for some features to work, and moving it is easier than querying it
} }
if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) { if(terminalInFamily("xterm", "rxvt", "screen", "tmux")) {
@ -1283,29 +1281,8 @@ struct Terminal {
} }
// EXPERIMENTAL do not use yet private void goCellular() {
Terminal alternateScreen() { version(Win32Console) {
assert(this.type != ConsoleOutputType.cellular);
this.flush();
return Terminal(ConsoleOutputType.cellular);
}
version(Windows) {
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
}
version(Win32Console)
/// ditto
this(ConsoleOutputType type) {
_initialized = true;
createLock();
if(UseVtSequences) {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
initializeVt();
} else {
if(type == ConsoleOutputType.cellular) {
hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null); hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, null, CONSOLE_TEXTMODE_BUFFER, null);
if(hConsole == INVALID_HANDLE_VALUE) { if(hConsole == INVALID_HANDLE_VALUE) {
import std.conv; import std.conv;
@ -1333,6 +1310,69 @@ struct Terminal {
GetConsoleCursorInfo(hConsole, &originalCursorInfo); GetConsoleCursorInfo(hConsole, &originalCursorInfo);
clear(); clear();
} else {
doTermcap("ti");
clear();
moveTo(0, 0, ForceOption.alwaysSend); // we need to know where the cursor is for some features to work, and moving it is easier than querying it
}
}
private void goLinear() {
version(Win32Console) {
auto stdo = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleActiveScreenBuffer(stdo);
if(hConsole !is stdo)
CloseHandle(hConsole);
hConsole = stdo;
} else {
doTermcap("te");
}
}
private ConsoleOutputType originalType;
private bool typeChanged;
// EXPERIMENTAL do not use yet
/++
It is not valid to call this if you constructed with minimalProcessing.
+/
void enableAlternateScreen(bool active) {
assert(type != ConsoleOutputType.minimalProcessing);
if(active) {
if(type == ConsoleOutputType.cellular)
return; // already set
flush();
goCellular();
type = ConsoleOutputType.cellular;
} else {
if(type == ConsoleOutputType.linear)
return; // already set
flush();
goLinear();
type = ConsoleOutputType.linear;
}
}
version(Windows) {
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
}
version(Win32Console)
/// ditto
this(ConsoleOutputType type) {
_initialized = true;
createLock();
if(UseVtSequences) {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
initializeVt();
} else {
if(type == ConsoleOutputType.cellular) {
goCellular();
} else { } else {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE); hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
} }
@ -1384,7 +1424,7 @@ struct Terminal {
if(UseVtSequences) { if(UseVtSequences) {
if(type == ConsoleOutputType.cellular) { if(type == ConsoleOutputType.cellular) {
doTermcap("te"); goLinear();
} }
version(TerminalDirectToEmulator) { version(TerminalDirectToEmulator) {
if(usingDirectEmulator) { if(usingDirectEmulator) {
@ -1425,10 +1465,7 @@ struct Terminal {
SetConsoleOutputCP(oldCp); SetConsoleOutputCP(oldCp);
SetConsoleCP(oldCpIn); SetConsoleCP(oldCpIn);
auto stdo = GetStdHandle(STD_OUTPUT_HANDLE); goLinear();
SetConsoleActiveScreenBuffer(stdo);
if(hConsole !is stdo)
CloseHandle(hConsole);
} }
version(TerminalDirectToEmulator) version(TerminalDirectToEmulator)
@ -5285,7 +5322,40 @@ class LineGetter {
} }
bool insertMode = true; bool insertMode = true;
bool multiLineMode = false;
private ConsoleOutputType original = cast(ConsoleOutputType) -1;
private bool multiLineModeOn = false;
private int startOfLineXOriginal;
private int startOfLineYOriginal;
void multiLineMode(bool on) {
if(original == -1) {
original = terminal.type;
startOfLineXOriginal = startOfLineX;
startOfLineYOriginal = startOfLineY;
}
if(on) {
terminal.enableAlternateScreen = true;
startOfLineX = 0;
startOfLineY = 0;
}
else if(original == ConsoleOutputType.linear) {
terminal.enableAlternateScreen = false;
}
if(!on) {
startOfLineX = startOfLineXOriginal;
startOfLineY = startOfLineYOriginal;
}
multiLineModeOn = on;
}
bool multiLineMode() { return multiLineModeOn; }
void toggleMultiLineMode() {
multiLineMode = !multiLineModeOn;
redraw();
}
private dchar[] line; private dchar[] line;
private int cursorPosition = 0; private int cursorPosition = 0;
@ -5558,6 +5628,12 @@ class LineGetter {
written++; written++;
lineLength--; lineLength--;
if(lg.multiLineMode) {
if(ch == '\n') {
lineLength = lg.terminal.width;
}
}
} }
void drawContent(T)(T towrite, int highlightBegin = 0, int highlightEnd = 0, bool inverted = false, int lineidx = -1) { void drawContent(T)(T towrite, int highlightBegin = 0, int highlightEnd = 0, bool inverted = false, int lineidx = -1) {
@ -5571,8 +5647,14 @@ class LineGetter {
} }
foreach(idx, dchar ch; towrite) { foreach(idx, dchar ch; towrite) {
if(lineLength <= 0) if(lineLength <= 0) {
if(lg.multiLineMode) {
if(ch == '\n')
lineLength = lg.terminal.width;
continue;
} else
break; break;
}
static if(is(T == dchar[])) { static if(is(T == dchar[])) {
if(lineidx != -1 && colorChars == 0) { if(lineidx != -1 && colorChars == 0) {
@ -5587,7 +5669,7 @@ class LineGetter {
} }
switch(ch) { switch(ch) {
case '\n': specialChar('n'); break; case '\n': lg.multiLineMode ? regularChar('\n') : specialChar('n'); break;
case '\r': specialChar('r'); break; case '\r': specialChar('r'); break;
case '\a': specialChar('a'); break; case '\a': specialChar('a'); break;
case '\t': specialChar('t'); break; case '\t': specialChar('t'); break;
@ -5666,6 +5748,7 @@ class LineGetter {
if(!cdi.populated) if(!cdi.populated)
return; return;
if(!multiLineMode) {
if(UseVtSequences && !_drawWidthMax) { if(UseVtSequences && !_drawWidthMax) {
terminal.writeStringRaw("\033[K"); terminal.writeStringRaw("\033[K");
} else { } else {
@ -5675,9 +5758,12 @@ class LineGetter {
terminal.write(" "); terminal.write(" ");
lastDrawLength = cdi.written; lastDrawLength = cdi.written;
} }
// if echoChar is null then we don't want to reflect the position at all // if echoChar is null then we don't want to reflect the position at all
terminal.moveTo(startOfLineX + ((echoChar == 0) ? 0 : cdi.cursorPositionToDrawX) + promptLength, startOfLineY + cdi.cursorPositionToDrawY); terminal.moveTo(startOfLineX + ((echoChar == 0) ? 0 : cdi.cursorPositionToDrawX) + promptLength, startOfLineY + cdi.cursorPositionToDrawY);
} else {
if(echoChar != 0)
terminal.moveTo(cdi.cursorPositionToDrawX, cdi.cursorPositionToDrawY);
}
endRedraw(); // make sure the cursor is turned back on endRedraw(); // make sure the cursor is turned back on
} }
@ -5714,17 +5800,26 @@ class LineGetter {
} }
terminal.moveTo(startOfLineX, startOfLineY); terminal.moveTo(startOfLineX, startOfLineY);
if(multiLineMode)
terminal.clear();
Drawer drawer = Drawer(this); Drawer drawer = Drawer(this);
drawer.lineLength = availableLineLength(); drawer.lineLength = availableLineLength();
if(drawer.lineLength < 0) if(drawer.lineLength < 0)
throw new Exception("too narrow terminal to draw"); throw new Exception("too narrow terminal to draw");
if(!multiLineMode) {
terminal.color(promptColor, background); terminal.color(promptColor, background);
terminal.write(prompt); terminal.write(prompt);
terminal.color(regularForeground, background); terminal.color(regularForeground, background);
}
auto towrite = line[horizontalScrollPosition .. $]; auto towrite = line[horizontalScrollPosition .. $];
if(multiLineMode) {
towrite = line[];
horizontalScrollPosition = 0; // FIXME
}
auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition; auto cursorPositionToDrawX = cursorPosition - horizontalScrollPosition;
auto cursorPositionToDrawY = 0; auto cursorPositionToDrawY = 0;
@ -5762,8 +5857,27 @@ class LineGetter {
CoreRedrawInfo cri; CoreRedrawInfo cri;
cri.populated = true; cri.populated = true;
cri.written = drawer.written; cri.written = drawer.written;
if(multiLineMode) {
cursorPositionToDrawX = 0;
cursorPositionToDrawY = 0;
// would be better if it did this in the same drawing pass...
foreach(idx, dchar ch; line) {
if(idx == cursorPosition)
break;
if(ch == '\n') {
cursorPositionToDrawX = 0;
cursorPositionToDrawY++;
} else {
cursorPositionToDrawX++;
}
}
cri.cursorPositionToDrawX = cursorPositionToDrawX; cri.cursorPositionToDrawX = cursorPositionToDrawX;
cri.cursorPositionToDrawY = cursorPositionToDrawY; cri.cursorPositionToDrawY = cursorPositionToDrawY;
} else {
cri.cursorPositionToDrawX = cursorPositionToDrawX;
cri.cursorPositionToDrawY = cursorPositionToDrawY;
}
return cri; return cri;
} }
@ -6064,6 +6178,88 @@ class LineGetter {
int selectionEnd = -1; int selectionEnd = -1;
} }
void backwardToNewline() {
while(cursorPosition && line[cursorPosition - 1] != '\n')
cursorPosition--;
phantomCursorX = 0;
}
void forwardToNewLine() {
while(cursorPosition < line.length && line[cursorPosition] != '\n')
cursorPosition++;
}
private int phantomCursorX;
void lineBackward() {
int count;
while(cursorPosition && line[cursorPosition - 1] != '\n') {
cursorPosition--;
count++;
}
if(count > phantomCursorX)
phantomCursorX = count;
if(cursorPosition == 0)
return;
cursorPosition--;
while(cursorPosition && line[cursorPosition - 1] != '\n') {
cursorPosition--;
}
count = phantomCursorX;
while(count) {
if(cursorPosition == line.length)
break;
if(line[cursorPosition] == '\n')
break;
cursorPosition++;
count--;
}
}
void lineForward() {
int count;
// see where we are in the current line
auto beginPos = cursorPosition;
while(beginPos && line[beginPos - 1] != '\n') {
beginPos--;
count++;
}
if(count > phantomCursorX)
phantomCursorX = count;
// get to the next line
while(cursorPosition < line.length && line[cursorPosition] != '\n') {
cursorPosition++;
}
if(cursorPosition == line.length)
return;
cursorPosition++;
// get to the same spot in this same line
count = phantomCursorX;
while(count) {
if(cursorPosition == line.length)
break;
if(line[cursorPosition] == '\n')
break;
cursorPosition++;
count--;
}
}
void pageBackward() {
}
void pageForward() {
}
/++ /++
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
@ -6219,13 +6415,18 @@ class LineGetter {
redraw(); redraw();
} }
phantomCursorX = 0;
break; break;
case KeyboardEvent.Key.escape: case KeyboardEvent.Key.escape:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode)
multiLineMode = false;
else {
cursorPosition = 0; cursorPosition = 0;
horizontalScrollPosition = 0; horizontalScrollPosition = 0;
line = line[0 .. 0]; line = line[0 .. 0];
line.assumeSafeAppend(); line.assumeSafeAppend();
}
redraw(); redraw();
break; break;
case KeyboardEvent.Key.F1: case KeyboardEvent.Key.F1:
@ -6234,6 +6435,12 @@ class LineGetter {
break; break;
case KeyboardEvent.Key.F2: case KeyboardEvent.Key.F2:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(ev.modifierState & ModifierState.control) {
toggleMultiLineMode();
break;
}
line[] &= cast(dchar) ~PRIVATE_BITS_MASK; line[] &= cast(dchar) ~PRIVATE_BITS_MASK;
auto got = editLineInEditor(line, cursorPosition); auto got = editLineInEditor(line, cursorPosition);
if(got !is null) { if(got !is null) {
@ -6363,6 +6570,7 @@ class LineGetter {
break; break;
case KeyboardEvent.Key.LeftArrow: case KeyboardEvent.Key.LeftArrow:
justHitTab = justKilled = false; justHitTab = justKilled = false;
phantomCursorX = 0;
/* /*
if(ev.modifierState & ModifierState.shift) if(ev.modifierState & ModifierState.shift)
@ -6395,6 +6603,9 @@ class LineGetter {
goto default; goto default;
case KeyboardEvent.Key.UpArrow: case KeyboardEvent.Key.UpArrow:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode)
lineBackward();
else
loadFromHistory(currentHistoryViewPosition + 1); loadFromHistory(currentHistoryViewPosition + 1);
redraw(); redraw();
break; break;
@ -6404,16 +6615,25 @@ class LineGetter {
goto default; goto default;
case KeyboardEvent.Key.DownArrow: case KeyboardEvent.Key.DownArrow:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode)
lineForward();
else
loadFromHistory(currentHistoryViewPosition - 1); loadFromHistory(currentHistoryViewPosition - 1);
redraw(); redraw();
break; break;
case KeyboardEvent.Key.PageUp: case KeyboardEvent.Key.PageUp:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode)
pageBackward();
else
loadFromHistory(cast(int) history.length); loadFromHistory(cast(int) history.length);
redraw(); redraw();
break; break;
case KeyboardEvent.Key.PageDown: case KeyboardEvent.Key.PageDown:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode)
pageForward();
else
loadFromHistory(0); loadFromHistory(0);
redraw(); redraw();
break; break;
@ -6429,7 +6649,11 @@ class LineGetter {
goto case; goto case;
case KeyboardEvent.Key.Home: case KeyboardEvent.Key.Home:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode) {
backwardToNewline();
} else {
cursorPosition = 0; cursorPosition = 0;
}
horizontalScrollPosition = 0; horizontalScrollPosition = 0;
redraw(); redraw();
break; break;
@ -6439,8 +6663,12 @@ class LineGetter {
goto case; goto case;
case KeyboardEvent.Key.End: case KeyboardEvent.Key.End:
justHitTab = justKilled = false; justHitTab = justKilled = false;
if(multiLineMode) {
forwardToNewLine();
} else {
cursorPosition = cast(int) line.length; cursorPosition = cast(int) line.length;
scrollToEnd(); scrollToEnd();
}
redraw(); redraw();
break; break;
case 'v', 22: case 'v', 22:
@ -6679,6 +6907,10 @@ class LineGetter {
string finishGettingLine() { string finishGettingLine() {
import std.conv; import std.conv;
if(multiLineMode)
multiLineMode = false;
line[] &= cast(dchar) ~PRIVATE_BITS_MASK; line[] &= cast(dchar) ~PRIVATE_BITS_MASK;
auto f = to!string(line); auto f = to!string(line);