feat(terminal): clear the scrollback with Ctrl+Shift+K and keep blank lines out of it on clear

main
reionwong 1 month ago
parent bf6a9f1c2d
commit cab126f191

@ -152,6 +152,26 @@ void Emulation::clearHistory()
// would cancel the update for the rest of that chunk.
bufferedUpdate();
}
bool Emulation::clearScreenAndHistory(bool atPrompt)
{
// Clearing under a full-screen program would break its idea of the screen.
if (_currentScreen != _screen[0])
return false;
_screen[0]->setScroll( _screen[0]->getScroll() , false );
if (atPrompt) {
_screen[0]->eraseScreen();
// The shell's clear-screen redraws the prompt and any typed input.
emit sendData("\x0C", 1);
} else {
_screen[0]->eraseAboveCursor();
}
bufferedUpdate();
return true;
}
void Emulation::setHistory(const HistoryType& t)
{
_screen[0]->setScroll(t);

@ -176,6 +176,13 @@ public:
const HistoryType& history() const;
/** Clears the history scroll. */
void clearHistory();
/**
* Clears the history and the screen for a user's clear command. At a prompt the
* shell gets a form feed to draw the prompt again; with a program running only
* the lines above the cursor go. Returns false on the alternate screen, which
* is left alone.
*/
bool clearScreenAndHistory(bool atPrompt);
/**
* Copies the output history from @p startLine to @p endLine

@ -1106,8 +1106,9 @@ void Screen::clearToBeginOfScreen()
void Screen::clearEntireScreen()
{
// Add entire screen to history
for (int i = 0; i < (lines-1); i++)
// Only rows up to the last written one go into the history; shells send this
// on every Ctrl+L, and pushing the blank rows below would grow the scrollback each time.
for (int i = lastNonBlankLine(); i >= 0; i--)
{
addHistLine(); scrollUp(0,1);
}
@ -1115,6 +1116,36 @@ void Screen::clearEntireScreen()
clearImage(loc(0,0),loc(columns-1,lines-1),' ');
}
void Screen::eraseScreen()
{
clearImage(loc(0,0),loc(columns-1,lines-1),' ');
}
void Screen::eraseAboveCursor()
{
if (cuY == 0)
return;
scrollUp(0,cuY);
cuY = 0;
}
int Screen::lastNonBlankLine() const
{
const Character blank;
for (int y = lines-1; y >= 0; y--)
{
for (const Character &ch : screenLines[y])
{
if (ch.character != blank.character || ch.backgroundColor != blank.backgroundColor)
return y;
}
}
return -1;
}
/*! fill screen with 'E'
This is to aid screen alignment
*/

@ -254,6 +254,10 @@ public:
/** Clear the whole screen, moving the current screen contents into the history first. */
void clearEntireScreen();
/** Clear the whole screen without touching the history. */
void eraseScreen();
/** Drop the lines above the cursor, moving the cursor line to the top. */
void eraseAboveCursor();
/**
* Clear the area of the screen from the current cursor position to the end of
* the screen.
@ -584,6 +588,9 @@ private:
Screen(const Screen &) = delete;
Screen &operator=(const Screen &) = delete;
// -1 when every line is blank.
int lastNonBlankLine() const;
//copies a line of text from the screen or history into a stream using a
//specified character decoder. Returns the number of lines actually copied,
//which may be less than 'count' if (start+count) is more than the number of characters on

@ -303,6 +303,11 @@ void KSession::clearScrollback()
m_session->clearHistory();
}
bool KSession::clearScreenAndScrollback()
{
return m_session->emulation()->clearScreenAndHistory(!hasActiveProcess());
}
void KSession::resetTerminal()
{
// Resetting clears the screen into the history, so the history goes after it.

@ -167,6 +167,9 @@ public slots:
void clearScrollback();
// Clears the scrollback and the screen; false on the alternate screen, which keeps the key.
bool clearScreenAndScrollback();
// Resets the terminal state and clears the screen and scrollback.
void resetTerminal();

@ -144,6 +144,12 @@ Item {
event.accepted = true
}
if ((event.key === Qt.Key_K)
&& (event.modifiers & Qt.ControlModifier)
&& (event.modifiers & Qt.ShiftModifier)) {
event.accepted = _session.clearScreenAndScrollback()
}
// Zoom: Ctrl+= (or Ctrl++), Ctrl+-, Ctrl+0.
if ((event.modifiers & Qt.ControlModifier) && !(event.modifiers & Qt.AltModifier)) {
if (event.key === Qt.Key_Equal || event.key === Qt.Key_Plus) {

@ -3,6 +3,7 @@
#include <QKeyEvent>
#include <QTest>
#include "History.h"
#include "KeyboardTranslator.h"
#include "ScreenWindow.h"
#include "Vt102Emulation.h"
@ -62,6 +63,12 @@ public:
QList<KeyboardTranslator::Command> takeCommands() { return std::exchange(m_commands, {}); }
void keepHistory() { m_emulation.setHistory(HistoryTypeBuffer(1000)); }
int historyLines() const { return m_window->screen()->getHistLines(); }
int cursorLine() const { return m_window->screen()->getCursorY(); }
bool clearAll(bool atPrompt) { return m_emulation.clearScreenAndHistory(atPrompt); }
QByteArray takeSent() { return std::exchange(m_sent, {}); }
private:
Vt102Emulation m_emulation;
ScreenWindow *m_window;
@ -204,6 +211,63 @@ private slots:
QCOMPARE(terminal.line(4), QStringLiteral("abcd"));
}
void clearingTheScreenKeepsOnlyWrittenLinesInHistory()
{
Terminal terminal;
terminal.keepHistory();
terminal.print("a\r\nb\r\n$ ");
terminal.print("\033[H\033[2J$ ");
QCOMPARE(terminal.historyLines(), 3);
// The prompt alone at the top goes up once, the blank lines below it never.
terminal.print("\033[H\033[2J$ ");
terminal.print("\033[H\033[2J$ ");
QCOMPARE(terminal.historyLines(), 5);
terminal.print("\033[H\033[2J\033[H\033[2J");
QCOMPARE(terminal.historyLines(), 6);
}
void clearAllAtAPromptAsksTheShellToRedraw()
{
Terminal terminal;
terminal.keepHistory();
terminal.print("a\r\nb\r\n$ \033[H\033[2J$ ls");
QCOMPARE(terminal.historyLines(), 3);
QVERIFY(terminal.clearAll(true));
QCOMPARE(terminal.takeSent(), QByteArray("\x0C"));
QCOMPARE(terminal.historyLines(), 0);
QCOMPARE(terminal.line(4), QStringLiteral(" "));
// The shell's redraw leaves nothing behind in the history.
terminal.print("\033[H\033[2J$ ls");
QCOMPARE(terminal.historyLines(), 0);
QCOMPARE(terminal.line(4), QStringLiteral("$ ls"));
}
void clearAllUnderAProgramKeepsTheCursorLine()
{
Terminal terminal;
terminal.keepHistory();
terminal.print("a\r\nb\r\nc");
QVERIFY(terminal.clearAll(false));
QCOMPARE(terminal.takeSent(), QByteArray());
QCOMPARE(terminal.historyLines(), 0);
QCOMPARE(terminal.cursorLine(), 0);
QCOMPARE(terminal.line(1), QStringLiteral("c"));
}
void clearAllLeavesTheAlternateScreenAlone()
{
Terminal terminal;
terminal.print("\033[?1049hx");
QVERIFY(!terminal.clearAll(true));
QCOMPARE(terminal.takeSent(), QByteArray());
QCOMPARE(terminal.line(1), QStringLiteral("x"));
}
void subParametersStayWithTheirAttribute()
{
Terminal terminal;

Loading…
Cancel
Save