Commit cfaf997e authored by Matan Ziv-Av's avatar Matan Ziv-Av Committed by Tomaz Canabrava
Browse files

Add semantic shell integration

- Add `OSC 133 ; *` escape sequences (where * stands for one of
  A,B,C,D,L,P. For the shell to report text mode
  (one of prompt/input/output).
- Store the mode of each character written to the screen.
- Replace isRealCharacter field in Character with flags.
- Store line length in _lineProperties array.
parent e936c6cb
......@@ -50,7 +50,7 @@ using namespace Konsole;
#endif
const Character Screen::DefaultChar =
Character(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), DEFAULT_RENDITION, false);
Character(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), DEFAULT_RENDITION, 0);
Screen::Screen(int lines, int columns)
: _currentTerminalDisplay(nullptr)
......@@ -73,6 +73,8 @@ Screen::Screen(int lines, int columns)
, _currentRendition(DEFAULT_RENDITION)
, _topMargin(0)
, _bottomMargin(0)
, _replMode(REPL_None)
, _hasRepl(false)
, _tabStops(QBitArray())
, _selBegin(0)
, _selTopLeft(0)
......@@ -222,6 +224,7 @@ void Screen::reverseIndex()
void Screen::nextLine()
//=NEL
{
_lineProperties[_cuY] = SetLineLength(_lineProperties[_cuY], _cuX);
toStartOfLine();
index();
}
......@@ -243,7 +246,7 @@ void Screen::eraseBlock(int y, int x, int height, int width)
width = qBound(0, width, _columns - x - 1);
int endCol = x + width;
height = qBound(0, height, _lines - y - 1);
Character chr(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), RE_TRANSPARENT, false);
Character chr(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), RE_TRANSPARENT, 0);
for (int row = y; row < y + height; row++) {
QVector<Character> &line = _screenLines[row];
if (line.size() < endCol + 1) {
......@@ -282,7 +285,7 @@ void Screen::deleteChars(int n)
_screenLines[_cuY].remove(_cuX, n);
// Append space(s) with current attributes
Character spaceWithCurrentAttrs(' ', _effectiveForeground, _effectiveBackground, _effectiveRendition, false);
Character spaceWithCurrentAttrs(' ', _effectiveForeground, _effectiveBackground, _effectiveRendition, 0);
for (int i = 0; i < n; ++i) {
_screenLines[_cuY].append(spaceWithCurrentAttrs);
......@@ -917,6 +920,7 @@ void Screen::initTabStops()
void Screen::newLine()
{
if (getMode(MODE_NewLine)) {
_lineProperties[_cuY] = SetLineLength(_lineProperties[_cuY], _cuX);
toStartOfLine();
}
......@@ -1047,7 +1051,7 @@ notcombine:
currentChar.foregroundColor = _effectiveForeground;
currentChar.backgroundColor = _effectiveBackground;
currentChar.rendition = _effectiveRendition;
currentChar.isRealCharacter = true;
currentChar.flags = setRepl(EF_REAL, _replMode);
_lastDrawnChar = c;
......@@ -1065,11 +1069,17 @@ notcombine:
ch.foregroundColor = _effectiveForeground;
ch.backgroundColor = _effectiveBackground;
ch.rendition = _effectiveRendition;
ch.isRealCharacter = false;
ch.flags = setRepl(EF_UNREAL, _replMode);
--w;
}
_cuX = newCursorX;
if (_replMode != REPL_None && std::make_pair(_cuY, _cuX) >= _replModeEnd) {
_replModeEnd = std::make_pair(_cuY, _cuX);
}
if (LineLength(_lineProperties[_cuY]) < _cuX) {
_lineProperties[_cuY] = SetLineLength(_lineProperties[_cuY], _cuX);
}
if (_escapeSequenceUrlExtractor) {
_escapeSequenceUrlExtractor->appendUrlText(QChar(c));
......@@ -1215,18 +1225,27 @@ void Screen::clearImage(int loca, int loce, char c, bool resetLineRendition)
const int topLine = loca / _columns;
const int bottomLine = loce / _columns;
Character clearCh(uint(c), _currentForeground, _currentBackground, DEFAULT_RENDITION, false);
Character clearCh(uint(c), _currentForeground, _currentBackground, DEFAULT_RENDITION, 0);
// if the character being used to clear the area is the same as the
// default character, the affected _lines can simply be shrunk.
const bool isDefaultCh = (clearCh == Screen::DefaultChar);
for (int y = topLine; y <= bottomLine; ++y) {
_lineProperties[y] &= ~LINE_WRAPPED;
_lineProperties[y] = 0;
const int endCol = (y == bottomLine) ? loce % _columns : _columns - 1;
const int startCol = (y == topLine) ? loca % _columns : 0;
if (endCol < _columns - 1 || startCol > 0) {
_lineProperties[y] &= ~LINE_WRAPPED;
if (LineLength(_lineProperties[y]) < endCol && LineLength(_lineProperties[y]) > startCol) {
_lineProperties[y] = SetLineLength(_lineProperties[y], startCol);
}
} else {
_lineProperties[y] = LINE_DEFAULT;
}
QVector<Character> &line = _screenLines[y];
if (isDefaultCh && endCol == _columns - 1) {
......@@ -1512,7 +1531,7 @@ void Screen::setSelectionEnd(const int x, const int y, const bool trimTrailingWh
_history->getCells(bottomRow, 0, histLineLen, histLine.data());
for (int j = bottomColumn; j < histLineLen; j++) {
if (histLine.at(j).isRealCharacter && (!trimTrailingWhitespace || !QChar(histLine.at(j).character).isSpace())) {
if ((histLine.at(j).flags & EF_REAL) != 0 && (!trimTrailingWhitespace || !QChar(histLine.at(j).character).isSpace())) {
beyondLastColumn = false;
}
}
......@@ -1523,7 +1542,7 @@ void Screen::setSelectionEnd(const int x, const int y, const bool trimTrailingWh
const int length = _screenLines.at(line).count();
for (int k = bottomColumn; k < lastColumn && k < length; k++) {
if (data[k].isRealCharacter && (!trimTrailingWhitespace || !QChar(data[k].character).isSpace())) {
if ((data[k].flags & EF_REAL) != 0 && (!trimTrailingWhitespace || !QChar(data[k].character).isSpace())) {
beyondLastColumn = false;
}
}
......@@ -1681,7 +1700,7 @@ int Screen::copyLineToStream(int line,
// Exclude trailing empty cells from count and don't bother processing them further.
// See the comment on the similar case for screen lines for an explanation.
while (count > 0 && !characterBuffer[start + count - 1].isRealCharacter) {
while (count > 0 && (characterBuffer[start + count - 1].flags & EF_REAL) == 0) {
count--;
}
......@@ -1717,7 +1736,7 @@ int Screen::copyLineToStream(int line,
// character when TrimTrailingWhitespace is true), so the returned
// count from this function must not include empty cells beyond that
// last character.
while (length > 0 && !data[length - 1].isRealCharacter) {
while (length > 0 && (data[length - 1].flags & EF_REAL) == 0) {
length--;
}
......@@ -1908,6 +1927,20 @@ void Screen::setLineProperty(LineProperty property, bool enable)
_lineProperties[_cuY] = static_cast<LineProperty>(_lineProperties.at(_cuY) & ~property);
}
}
void Screen::setReplMode(int mode)
{
if (_replMode != mode) {
_replMode = mode;
_replModeStart = std::make_pair(_cuY, _cuX);
_replModeEnd = std::make_pair(_cuY, _cuX);
}
if (mode != REPL_None) {
_hasRepl = true;
setLineProperty(LINE_PROMPT_START << (mode - REPL_PROMPT), true);
}
}
void Screen::fillWithDefaultChar(Character *dest, int count)
{
std::fill_n(dest, count, Screen::DefaultChar);
......
......@@ -31,6 +31,11 @@
#define MODE_AppScreen 6
#define MODES_SCREEN 7
#define REPL_None 0
#define REPL_PROMPT 1
#define REPL_INPUT 2
#define REPL_OUTPUT 3
struct TerminalGraphicsPlacement_t {
QPixmap pixmap;
qint64 id;
......@@ -89,6 +94,9 @@ public:
PreserveLineBreaks = 0x2,
TrimLeadingWhitespace = 0x4,
TrimTrailingWhitespace = 0x8,
ExcludePrompt = 0x10,
ExcludeInput = 0x20,
ExcludeOutput = 0x40,
};
Q_DECLARE_FLAGS(DecodingOptions, DecodingOption)
......@@ -536,6 +544,37 @@ public:
*/
void setLineProperty(LineProperty property, bool enable);
/**
* Set REPL mode (shell integration)
*
* @param mode is the REPL mode
* Possible modes are:
* REPL_None
* REPL_PROMPT
* REPL_INPUT
* REPL_OUTPUT
*/
void setReplMode(int mode);
/** Return true if semantic shell integration is in use. */
bool hasRepl() const
{
return _hasRepl;
}
/** Return current REPL mode */
int replMode() const
{
return _replMode;
}
/** Return location of current REPL mode start. */
std::pair<int, int> replModeStart() const
{
return _replModeStart;
}
std::pair<int, int> replModeEnd() const
{
return _replModeEnd;
}
/**
* Returns the number of lines that the image has been scrolled up or down by,
* since the last call to resetScrolledLines().
......@@ -763,6 +802,10 @@ private:
// states ----------------
int _currentModes[MODES_SCREEN];
int _savedModes[MODES_SCREEN];
int _replMode;
bool _hasRepl;
std::pair<int, int> _replModeStart;
std::pair<int, int> _replModeEnd;
// ----------------------------
......
......@@ -1048,8 +1048,20 @@ void Vt102Emulation::processSessionAttributeRequest(const int tokenSize, const u
}
if (attribute == 133) {
if (value == QLatin1String("A")) {
_currentScreen->setLineProperty(LINE_PROMPT_START, true);
if (value == QLatin1String("A") || value == QLatin1String("N") || value == QLatin1String("P")) {
_currentScreen->setReplMode(REPL_PROMPT);
}
if (value == QLatin1String("L") && _currentScreen->getCursorX() > 0) {
_currentScreen->nextLine();
}
if (value == QLatin1String("B")) {
_currentScreen->setReplMode(REPL_INPUT);
}
if (value == QLatin1String("C")) {
_currentScreen->setReplMode(REPL_OUTPUT);
}
if (value == QLatin1String("D")) {
_currentScreen->setReplMode(REPL_None);
}
}
if (attribute == 4) {
......
......@@ -20,10 +20,12 @@
namespace Konsole
{
typedef unsigned char LineProperty;
typedef quint32 LineProperty;
typedef quint16 RenditionFlags;
typedef quint16 ExtraFlags;
/* clang-format off */
const int LINE_DEFAULT = 0;
const int LINE_WRAPPED = (1 << 0);
......@@ -31,6 +33,10 @@ const int LINE_DOUBLEWIDTH = (1 << 1);
const int LINE_DOUBLEHEIGHT_TOP = (1 << 2);
const int LINE_DOUBLEHEIGHT_BOTTOM = (1 << 3);
const int LINE_PROMPT_START = (1 << 4);
const int LINE_INPUT_START = (1 << 5);
const int LINE_OUTPUT_START = (1 << 6);
const int LINE_LEN_POS = 8;
const int LINE_LEN_MASK = (0xfff << LINE_LEN_POS);
const RenditionFlags DEFAULT_RENDITION = 0;
const RenditionFlags RE_BOLD = (1 << 0);
......@@ -47,6 +53,16 @@ const RenditionFlags RE_OVERLINE = (1 << 10);
const RenditionFlags RE_SELECTED = (1 << 11);
const RenditionFlags RE_TRANSPARENT = (1 << 12);
const ExtraFlags EF_UNREAL = 0;
const ExtraFlags EF_REAL = (1 << 0);
const ExtraFlags EF_REPL = (3 << 1);
const ExtraFlags EF_REPL_PROMPT = (1 << 1);
const ExtraFlags EF_REPL_INPUT = (2 << 1);
const ExtraFlags EF_REPL_OUTPUT = (3 << 1);
#define setRepl(f, m) (((f) & ~EF_REPL) | ((m) * EF_REPL_PROMPT))
#define LineLength(f) static_cast<int>(((f) & LINE_LEN_MASK) >> LINE_LEN_POS)
#define SetLineLength(f, l) (((f) & ~LINE_LEN_MASK) | ((l) << LINE_LEN_POS))
/* clang-format on */
/**
......@@ -65,19 +81,18 @@ public:
* @param _b The color used to draw the character's background.
* @param _r A set of rendition flags which specify how this character
* is to be drawn.
* @param _real Indicate whether this character really exists, or exists
* simply as place holder.
* @param _flags Extra flags describing the character. not directly related to its rendition
*/
explicit constexpr Character(uint _c = ' ',
CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR),
CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR),
RenditionFlags _r = DEFAULT_RENDITION,
bool _real = true)
ExtraFlags _flags = EF_REAL)
: character(_c)
, rendition(_r)
, foregroundColor(_f)
, backgroundColor(_b)
, isRealCharacter(_real)
, flags(_flags)
{
}
......@@ -107,7 +122,7 @@ public:
* PlaceHolderCharacter: a character which exists as place holder
* TabStopCharacter: a special place holder for HT("\t")
*/
bool isRealCharacter;
ExtraFlags flags;
/**
* returns true if the format (color, rendition flag) of the compared characters is equal
......
......@@ -97,7 +97,7 @@ void PlainTextDecoder::decodeLine(const Character *const characters, int count,
// FIXME: the special case of '\n' here is really ugly
// Maybe the '\n' should be added after calling this method in
// Screen::copyLineToStream()
if (characters[i].isRealCharacter && characters[i].character != '\n') {
if ((characters[i].flags & EF_REAL) != 0 && characters[i].character != '\n') {
realCharacterGuard = i;
break;
}
......@@ -129,7 +129,7 @@ void PlainTextDecoder::decodeLine(const Character *const characters, int count,
// This feels tricky, but otherwise leading "whitespaces" may be
// lost in some situation. One typical example is copying the result
// of `dialog --infobox "qwe" 10 10` .
if (characters[i].isRealCharacter || i <= realCharacterGuard) {
if ((characters[i].flags & EF_REAL) != 0 || i <= realCharacterGuard) {
characterBuffer.append(characters[i].character);
i += qMax(1, Character::stringWidth(&characters[i].character, 1));
} else {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment