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

[WIP] Reconstruct TAB character when copying from grid #999

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions src/contour/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,7 @@ void loadConfigFromFile(Config& _config, FileSystem::path const& _fileName)
}

tryLoadValue(usedKeys, doc, "reflow_on_resize", _config.reflowOnResize);
tryLoadValue(usedKeys, doc, "expand_tabs", _config.expandTabs);

if (auto profiles = doc["profiles"]; profiles)
{
Expand Down
1 change: 1 addition & 0 deletions src/contour/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ struct Config
size_t ptyBufferObjectSize = 1024lu * 1024lu;

bool reflowOnResize = true;
bool expandTabs = false;

std::unordered_map<std::string, terminal::ColorPalette> colorschemes;
std::unordered_map<std::string, TerminalProfile> profiles;
Expand Down
1 change: 1 addition & 0 deletions src/contour/TerminalSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ namespace
settings.primaryScreen.allowReflowOnResize = config.reflowOnResize;
settings.highlightDoubleClickedWord = profile.highlightDoubleClickedWord;
settings.highlightTimeout = profile.highlightTimeout;
settings.expandTabs = config.expandTabs;

return settings;
}
Expand Down
1 change: 1 addition & 0 deletions src/vtbackend/CellFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class CellFlags : uint32_t
Overline = (1 << 14),
RapidBlinking = (1 << 15),
CharacterProtected = (1 << 16), // Character is protected by selective erase operations.
Tab = (1 << 17), // Cell is part of tab character.
};

constexpr CellFlags& operator|=(CellFlags& a, CellFlags b) noexcept
Expand Down
3 changes: 1 addition & 2 deletions src/vtbackend/Grid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,7 @@ CellLocation Grid<Cell>::resize(PageSize newSize, CellLocation currentCursorPos,
flushLogicalLine();
if (line.isTrivialBuffer())
{
auto& buffer = line.trivialBuffer();
buffer.displayWidth = newColumnCount;
line.trivialBuffer().resize(newColumnCount);
grownLines.emplace_back(line);
}
else
Expand Down
30 changes: 26 additions & 4 deletions src/vtbackend/Line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ typename Line<Cell>::InflatedBuffer Line<Cell>::reflow(ColumnCount newColumnCoun
{
switch (crispy::strongCompare(newColumnCount, ColumnCount::cast_from(trivialBuffer().text.size())))
{
case Comparison::Greater: trivialBuffer().displayWidth = newColumnCount; return {};
case Comparison::Greater: trivialBuffer().resize(newColumnCount); return {};
case Comparison::Equal: return {};
case Comparison::Less:;
}
Expand Down Expand Up @@ -99,8 +99,7 @@ inline void Line<Cell>::resize(ColumnCount count)
{
if (isTrivialBuffer())
{
TrivialBuffer& buffer = trivialBuffer();
buffer.displayWidth = count;
trivialBuffer().resize(count);
return;
}
}
Expand Down Expand Up @@ -179,7 +178,7 @@ InflatedLineBuffer<Cell> inflate(TrivialLineBuffer const& input)
auto lastChar = char32_t { 0 };
auto utf8DecoderState = unicode::utf8_decoder_state {};
auto gapPending = 0;

size_t cellNr = 0;
for (char const ch: input.text.view())
{
unicode::ConvertResult const r = unicode::from_utf8(utf8DecoderState, static_cast<uint8_t>(ch));
Expand All @@ -200,6 +199,7 @@ InflatedLineBuffer<Cell> inflate(TrivialLineBuffer const& input)
columns.emplace_back(Cell {});
columns.back().setHyperlink(input.hyperlink);
columns.back().write(input.textAttributes, nextChar, static_cast<uint8_t>(charWidth));
columns.back().setTab(input.tabstops[cellNr]);
gapPending = charWidth - 1;
}
else
Expand All @@ -218,6 +218,7 @@ InflatedLineBuffer<Cell> inflate(TrivialLineBuffer const& input)
}
}
lastChar = nextChar;
++cellNr;
}

while (gapPending > 0)
Expand All @@ -229,10 +230,31 @@ InflatedLineBuffer<Cell> inflate(TrivialLineBuffer const& input)
assert(columns.size() == unbox<size_t>(input.usedColumns));

while (columns.size() < unbox<size_t>(input.displayWidth))
{
columns.emplace_back(Cell { input.fillAttributes });
columns.back().setTab(input.tabstops[cellNr]);
++cellNr;
}

return columns;
}

template <typename Cell>
void Line<Cell>::setTab(ColumnOffset start, ColumnCount n, bool tab)
{
if (isInflatedBuffer())
{
for (; n > ColumnCount(0); --n)
useCellAt(start++).setTab(tab);
}
else
{
auto& buffer = trivialBuffer();
for (; n > ColumnCount(0); --n)
buffer.tabstops[(start++).as<size_t>()] = true;
// assert(false);
}
}
} // end namespace terminal

#include <vtbackend/cell/CompactCell.h>
Expand Down
19 changes: 18 additions & 1 deletion src/vtbackend/Line.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,20 @@ struct TrivialLineBuffer

ColumnCount usedColumns {};
crispy::BufferFragment<char> text {};

std::vector<bool> tabstops = std::vector<bool>(displayWidth.value, false);
void reset(GraphicsAttributes attributes) noexcept
{
textAttributes = attributes;
fillAttributes = attributes;
hyperlink = {};
usedColumns = {};
text.reset();
tabstops.clear();
}
void resize(ColumnCount count)
{
displayWidth = count;
tabstops.resize(count.as<size_t>());
}
};

Expand Down Expand Up @@ -234,6 +240,15 @@ class Line
return inflatedBuffer().at(unbox<size_t>(column)).empty();
}

[[nodiscard]] bool hasTabstop(ColumnOffset column) const noexcept
{
Require(ColumnOffset(0) <= column);
Require(column <= ColumnOffset::cast_from(size()));
if (isInflatedBuffer())
return cells()[column.as<size_t>()].isTab();
return trivialBuffer().tabstops[column.as<size_t>()];
}

[[nodiscard]] uint8_t cellWidthAt(ColumnOffset column) const noexcept
{
#if 0 // TODO: This optimization - but only when we return actual widths and not always 1.
Expand All @@ -258,6 +273,8 @@ class Line
[[nodiscard]] bool wrappable() const noexcept { return isFlagEnabled(LineFlags::Wrappable); }
void setWrappable(bool enable) { setFlag(LineFlags::Wrappable, enable); }

void setTab(ColumnOffset start, ColumnCount n, bool tab);

[[nodiscard]] LineFlags wrappableFlag() const noexcept
{
return wrappable() ? LineFlags::Wrappable : LineFlags::None;
Expand Down
53 changes: 49 additions & 4 deletions src/vtbackend/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ using std::vector;
namespace terminal
{

auto constexpr inline TabWidth = ColumnCount(8);

auto const inline VTCaptureBufferLog = logstore::Category("vt.ext.capturebuffer",
"Capture Buffer debug logging.",
logstore::Category::State::Disabled,
Expand Down Expand Up @@ -1468,9 +1466,12 @@ void Screen<Cell>::moveCursorToNextTab()
++i;

auto const currentCursorColumn = logicalCursorPosition().column;

if (i < _state.tabs.size())
moveCursorForward(boxed_cast<ColumnCount>(_state.tabs[i] - currentCursorColumn));
{
auto const cursorMoveAmount = boxed_cast<ColumnCount>(_state.tabs[i] - currentCursorColumn);
currentLine().setTab(currentCursorColumn, cursorMoveAmount, true);
moveCursorForward(cursorMoveAmount);
}
else if (realCursorPosition().column < margin().horizontal.to)
moveCursorForward(boxed_cast<ColumnCount>(margin().horizontal.to - currentCursorColumn));
}
Expand All @@ -1482,6 +1483,7 @@ void Screen<Cell>::moveCursorToNextTab()
auto const n =
min((TabWidth - boxed_cast<ColumnCount>(_cursor.position.column) % TabWidth),
_settings.pageSize.columns - boxed_cast<ColumnCount>(logicalCursorPosition().column));
currentLine().setTab(logicalCursorPosition().column, n, true);
moveCursorForward(n);
}
}
Expand Down Expand Up @@ -3912,6 +3914,49 @@ bool Screen<Cell>::isCursorInsideMargins() const noexcept
return insideVerticalMargin && insideHorizontalMargin;
}

template <typename Cell>
CRISPY_REQUIRES(CellConcept<Cell>)
CellLocation Screen<Cell>::getTabstopStart(CellLocation position) const noexcept
{
if (!_state.tabs.empty())
{
auto tab = std::lower_bound(_state.tabs.begin(), _state.tabs.end(), position.column);
position.column =
(tab == _state.tabs.end()) ? ColumnOffset::cast_from(_settings.pageSize.columns - 1) : *tab - 1;
}
else
{
auto const n = min(boxed_cast<ColumnCount>(position.column) % TabWidth,
_settings.pageSize.columns - boxed_cast<ColumnCount>(position.column));
position.column -= n;
}
auto const& line = _grid.lineAt(position.line);
while (!line.hasTabstop(position.column))
++position.column;
return position;
}

template <typename Cell>
CRISPY_REQUIRES(CellConcept<Cell>)
CellLocation Screen<Cell>::getTabstopEnd(CellLocation position) const noexcept
{
if (!_state.tabs.empty())
{
auto tab = std::upper_bound(_state.tabs.begin(), _state.tabs.end(), position.column);
position.column =
(tab == _state.tabs.end()) ? ColumnOffset::cast_from(_settings.pageSize.columns - 1) : *tab - 1;
}
else
{
auto const n = min((TabWidth - boxed_cast<ColumnCount>(position.column) % TabWidth - 1),
_settings.pageSize.columns - boxed_cast<ColumnCount>(position.column));
position.column += n;
}
auto const& line = _grid.lineAt(position.line);
while (!line.hasTabstop(position.column))
--position.column;
return position;
}
} // namespace terminal

#include <vtbackend/cell/CompactCell.h>
Expand Down
9 changes: 9 additions & 0 deletions src/vtbackend/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class ScreenBase: public SequenceHandler
[[nodiscard]] virtual Margin& margin() noexcept = 0;
[[nodiscard]] virtual bool contains(CellLocation coord) const noexcept = 0;
[[nodiscard]] virtual bool isCellEmpty(CellLocation position) const noexcept = 0;
[[nodiscard]] virtual bool hasTabstop(CellLocation position) const noexcept = 0;
[[nodiscard]] virtual CellLocation getTabstopStart(CellLocation position) const noexcept = 0;
[[nodiscard]] virtual CellLocation getTabstopEnd(CellLocation position) const noexcept = 0;
[[nodiscard]] virtual bool compareCellTextAt(CellLocation position, char codepoint) const noexcept = 0;
[[nodiscard]] virtual std::string cellTextAt(CellLocation position) const noexcept = 0;
[[nodiscard]] virtual LineFlags lineFlagsAt(LineOffset line) const noexcept = 0;
Expand Down Expand Up @@ -518,6 +521,12 @@ class Screen final: public ScreenBase, public capabilities::StaticDatabase
return _grid.lineAt(position.line).cellEmptyAt(position.column);
}

[[nodiscard]] bool hasTabstop(CellLocation position) const noexcept override
{
return _grid.lineAt(position.line).hasTabstop(position.column);
}
[[nodiscard]] CellLocation getTabstopStart(CellLocation position) const noexcept override;
[[nodiscard]] CellLocation getTabstopEnd(CellLocation position) const noexcept override;
[[nodiscard]] bool compareCellTextAt(CellLocation position, char codepoint) const noexcept override
{
auto const& cell = _grid.lineAt(position.line).inflatedBuffer().at(position.column.as<size_t>());
Expand Down
11 changes: 10 additions & 1 deletion src/vtbackend/Selector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ namespace terminal

namespace // {{{ helper
{

tuple<vector<Selection::Range>, CellLocation const, CellLocation const> prepare(
Selection const& selection)
{
Expand Down Expand Up @@ -58,6 +57,16 @@ bool Selection::extend(CellLocation to)
return true;
}

bool Selection::extendStart(CellLocation from)
{
assert(_state != State::Complete
&& "In order extend a selection, the selector must be active (started).");
_state = State::InProgress;
_from = from;
_onSelectionUpdated();
return true;
}

void Selection::complete()
{
if (_state == State::InProgress)
Expand Down
3 changes: 3 additions & 0 deletions src/vtbackend/Selector.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ class Selection
/// Extends the selection to the given coordinate.
[[nodiscard]] virtual bool extend(CellLocation to);

/// Include the given pos
[[nodiscard]] virtual bool extendStart(CellLocation from);

/// Constructs a vector of ranges for this selection.
[[nodiscard]] virtual std::vector<Range> ranges() const;

Expand Down
1 change: 1 addition & 0 deletions src/vtbackend/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct Settings
std::chrono::milliseconds highlightTimeout = std::chrono::milliseconds { 150 };
bool highlightDoubleClickedWord = true;
// TODO: ^^^ make also use of it. probably rename to how VScode has named it.
bool expandTabs = false;

struct PrimaryScreen
{
Expand Down
Loading