[vte] emulation: Refactor tabstop handling
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] emulation: Refactor tabstop handling
- Date: Tue, 27 Mar 2018 17:45:14 +0000 (UTC)
commit ef0a81b2cf08c5514772ee818316c094dda7d64e
Author: Christian Persch <chpe src gnome org>
Date: Tue Mar 27 19:40:12 2018 +0200
emulation: Refactor tabstop handling
Use a bitmask to store the tabstops instead of a hash table,
and add tests.
src/Makefile.am | 18 +++-
src/parser-cmd.hh | 5 +
src/parser.cc | 10 ++
src/tabstops-test.cc | 234 ++++++++++++++++++++++++++++++++++++++
src/tabstops.hh | 222 ++++++++++++++++++++++++++++++++++++
src/vte.cc | 84 +++-----------
src/vteinternal.hh | 16 +--
src/vteseq.cc | 306 +++++++++++++++++++++++++++-----------------------
8 files changed, 675 insertions(+), 220 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index f5f1434..2b2f6c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -180,7 +180,7 @@ vteresources.cc: vte.gresource.xml Makefile $(shell $(GLIB_COMPILE_RESOURCES) --
# Misc unit tests and utilities
-noinst_PROGRAMS += parser-cat slowcat test-modes test-parser
+noinst_PROGRAMS += parser-cat slowcat test-modes test-tabstops test-parser
noinst_SCRIPTS = decset osc window
EXTRA_DIST += $(noinst_SCRIPTS)
@@ -203,6 +203,7 @@ dist_check_SCRIPTS = \
TESTS = \
test-modes \
test-parser \
+ test-tabstops \
reaper \
test-vtetypes \
vteconv \
@@ -318,6 +319,21 @@ test_modes_LDADD = \
$(GLIB_LIBS) \
$(NULL)
+test_tabstops_SOURCES = \
+ tabstops-test.cc \
+ tabstops.hh \
+ $(NULL)
+test_tabstops_CPPFLAGS = \
+ -I$(builddir) \
+ -I$(srcdir) \
+ $(AM_CPPFLAGS)
+test_tabstops_CXXFLAGS = \
+ $(GLIB_CFLAGS) \
+ $(AM_CXXFLAGS)
+test_tabstops_LDADD = \
+ $(GLIB_LIBS) \
+ $(NULL)
+
test_vtetypes_SOURCES = \
vtetypes.cc \
vtetypes.hh \
diff --git a/src/parser-cmd.hh b/src/parser-cmd.hh
index 60c301b..e57e2c7 100644
--- a/src/parser-cmd.hh
+++ b/src/parser-cmd.hh
@@ -191,7 +191,12 @@ _VTE_CMD(SS3) /* single-shift-3 */
_VTE_CMD(ST) /* string-terminator */
_VTE_CMD(SU) /* scroll-up */
_VTE_CMD(SUB) /* substitute */
+_VTE_CMD(TAC) /* tabulation-aligned-centre */
+_VTE_CMD(TALE) /* tabulation-aligned-leading-edge */
+_VTE_CMD(TATE) /* tabulation-aligned-trailing-edge */
_VTE_CMD(TBC) /* tab-clear */
+_VTE_CMD(TCC) /* tabulation-centred-on-character */
+_VTE_CMD(TSR) /* tabulation-stop-remove */
_VTE_CMD(VPA) /* vertical-line-position-absolute */
_VTE_CMD(VPR) /* vertical-line-position-relative */
_VTE_CMD(VT) /* vertical-tab */
diff --git a/src/parser.cc b/src/parser.cc
index c9fd0c4..877947a 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -425,6 +425,8 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
case 'a':
if (flags == 0) /* HPR */
return VTE_CMD_HPR;
+ else if (flags == VTE_SEQ_FLAG_SPACE) /* TALE */
+ return VTE_CMD_TALE;
break;
case 'B':
if (flags == 0) /* CUD */
@@ -433,6 +435,8 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
case 'b':
if (flags == 0) /* REP */
return VTE_CMD_REP;
+ else if (flags == VTE_SEQ_FLAG_SPACE) /* TAC */
+ return VTE_CMD_TAC;
break;
case 'C':
if (flags == 0) /* CUF */
@@ -441,6 +445,8 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
case 'c':
if (flags == 0) /* DA1 */
return VTE_CMD_DA1;
+ else if (flags == VTE_SEQ_FLAG_SPACE) /* TCC */
+ return VTE_CMD_TCC;
else if (flags == VTE_SEQ_FLAG_GT) /* DA2 */
return VTE_CMD_DA2;
else if (flags == VTE_SEQ_FLAG_EQUAL) /* DA3 */
@@ -453,6 +459,8 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
case 'd':
if (flags == 0) /* VPA */
return VTE_CMD_VPA;
+ else if (flags == VTE_SEQ_FLAG_SPACE) /* TSR */
+ return VTE_CMD_TSR;
break;
case 'E':
if (flags == 0) /* CNL */
@@ -796,6 +804,8 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
case '`':
if (flags == 0) /* HPA */
return VTE_CMD_HPA;
+ else if (flags == VTE_SEQ_FLAG_SPACE) /* TATE */
+ return VTE_CMD_TATE;
break;
case '{':
if (flags == VTE_SEQ_FLAG_CASH) /* DECSERA */
diff --git a/src/tabstops-test.cc b/src/tabstops-test.cc
new file mode 100644
index 0000000..a574814
--- /dev/null
+++ b/src/tabstops-test.cc
@@ -0,0 +1,234 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <algorithm>
+
+#include <glib.h>
+
+#include "tabstops.hh"
+#include "vtedefines.hh"
+
+using namespace vte::terminal;
+
+static void
+tabstops_set(Tabstops& t,
+ std::initializer_list<Tabstops::position_t> l)
+{
+ for (auto i : l)
+ t.set(i);
+}
+
+static void
+assert_tabstops_default(Tabstops const& t,
+ Tabstops::position_t start = 0,
+ Tabstops::position_t end = Tabstops::position_t(-1),
+ Tabstops::position_t tab_width = VTE_TAB_WIDTH)
+{
+ if (end == t.npos)
+ end = t.size();
+
+ for (auto i = start; i < end; i++) {
+ if (i % tab_width)
+ g_assert_false(t.get(i));
+ else
+ g_assert_true(t.get(i));
+ }
+}
+
+static void
+assert_tabstops_clear(Tabstops const& t,
+ Tabstops::position_t start = 0,
+ Tabstops::position_t end = Tabstops::position_t(-1))
+{
+ if (end == t.npos)
+ end = t.size();
+
+ for (auto i = start; i < end; i++)
+ g_assert_false(t.get(i));
+}
+
+static void
+assert_tabstops(Tabstops const& t,
+ std::initializer_list<Tabstops::position_t> l,
+ Tabstops::position_t start = 0,
+ Tabstops::position_t end = Tabstops::position_t(-1))
+{
+ if (end == t.npos)
+ end = t.size();
+
+ auto it = l.begin();
+ for (auto i = start; i < end; i++) {
+ if (it != l.end() && i == *it) {
+ g_assert_true(t.get(i));
+ ++it;
+ } else
+ g_assert_false(t.get(i));
+
+ }
+ g_assert_true(it == l.end());
+}
+
+static void
+assert_tabstops_previous(Tabstops const& t,
+ std::initializer_list<std::pair<Tabstops::position_t, Tabstops::position_t>> l,
+ int count = 1)
+{
+ for (auto p : l) {
+ g_assert_cmpuint(t.get_previous(p.first, count), ==, p.second);
+ }
+}
+
+static void
+assert_tabstops_next(Tabstops const& t,
+ std::initializer_list<std::pair<Tabstops::position_t, Tabstops::position_t>> l,
+ int count = 1)
+{
+ for (auto p : l) {
+ g_assert_cmpuint(t.get_next(p.first, count), ==, p.second);
+ }
+}
+
+static void
+test_tabstops_default(void)
+{
+ Tabstops t{};
+ g_assert_cmpuint(t.size(), ==, VTE_COLUMNS);
+
+ assert_tabstops_default(t);
+}
+
+static void
+test_tabstops_get_set(void)
+{
+ Tabstops t{256, false};
+
+ tabstops_set(t, {42, 200});
+ assert_tabstops(t, {42, 200});
+}
+
+static void
+test_tabstops_clear(void)
+{
+ Tabstops t{128, true};
+ t.clear();
+ assert_tabstops_clear(t);
+}
+
+static void
+test_tabstops_reset(void)
+{
+ unsigned int const tab_width = 7;
+
+ Tabstops t{80, true, tab_width};
+ assert_tabstops_default(t, 0, t.npos, tab_width);
+
+ t.resize(80);
+ t.resize(160, false, tab_width);
+ assert_tabstops_default(t, 0, 80, tab_width);
+ assert_tabstops_clear(t, 80, t.npos);
+
+ t.resize(80);
+ t.clear();
+ t.resize(160, true, tab_width);
+ assert_tabstops_clear(t, 0, 80);
+ assert_tabstops_default(t, 80, t.npos, tab_width);
+
+ t.resize(256);
+ t.reset(tab_width);
+ assert_tabstops_default(t, 0, t.npos, tab_width);
+ t.resize(1024, true, tab_width);
+ assert_tabstops_default(t, 0, t.npos, tab_width);
+ t.resize(4096, true, tab_width);
+ assert_tabstops_default(t, 0, t.npos, tab_width);
+}
+
+static void
+test_tabstops_resize(void)
+{
+ Tabstops t;
+ t.resize(80);
+
+ t.reset();
+ assert_tabstops_default(t);
+ t.resize(161, false);
+ assert_tabstops_default(t, 0, 80);
+ assert_tabstops_clear(t, 80, t.npos);
+}
+
+static void
+test_tabstops_previous(void)
+{
+ Tabstops t{512, false};
+ tabstops_set(t, {0, 31, 32, 63, 64, 255, 256});
+ assert_tabstops_previous(t, {{511, 256}, {256, 255}, {255, 64}, {64, 63}, {63, 32}, {32, 31}, {31,
0}});
+
+ assert_tabstops_previous(t, {{511, 255}, {257, 255}, {254, 63}, {64, 32}, {33, 31}, {32, 0}, {31,
t.npos}, {0, t.npos}}, 2);
+
+ t.clear();
+ tabstops_set(t, {127, 256});
+ assert_tabstops_previous(t, {{511, 256}, {256, 127}, {127, t.npos}});
+ assert_tabstops_previous(t, {{384, 256}, {192, 127}, {92, t.npos}});
+
+ unsigned int const tab_width = 3;
+ t.reset(tab_width);
+
+ for (unsigned int p = 1 ; p < t.size(); ++p) {
+ g_assert_cmpuint(t.get_previous(p), ==, (p - 1) / tab_width * tab_width);
+ }
+ g_assert_cmpuint(t.get_previous(0), ==, t.npos);
+}
+
+static void
+test_tabstops_next(void)
+{
+ Tabstops t{512, false};
+ tabstops_set(t, {0, 31, 32, 63, 64, 255, 256});
+ assert_tabstops_next(t, {{0, 31}, {31, 32}, {32, 63}, {63, 64}, {64, 255}, {255, 256}, {256,
t.npos}});
+ assert_tabstops_next(t, {{0, 32}, {2, 32}, {31, 63}, {48, 64}, {128, 256}, {255, t.npos}}, 2);
+
+ t.clear();
+ tabstops_set(t, {127, 256});
+ assert_tabstops_next(t, {{0, 127}, {127, 256}, {256, t.npos}});
+ assert_tabstops_next(t, {{1, 127}, {192, 256}, {384, t.npos}});
+
+ unsigned int const tab_width = 3;
+ t.reset(tab_width);
+
+ for (unsigned int p = 0; p < t.size() - tab_width; ++p) {
+ g_assert_cmpuint(t.get_next(p), ==, (p / tab_width + 1) * tab_width);
+ }
+ g_assert_cmpuint(t.get_next(t.size() - 1), ==, t.npos);
+}
+
+int
+main(int argc,
+ char* argv[])
+{
+ g_test_init(&argc, &argv, nullptr);
+
+ g_test_add_func("/vte/tabstops/default", test_tabstops_default);
+ g_test_add_func("/vte/tabstops/get-set", test_tabstops_get_set);
+ g_test_add_func("/vte/tabstops/clear", test_tabstops_clear);
+ g_test_add_func("/vte/tabstops/reset", test_tabstops_reset);
+ g_test_add_func("/vte/tabstops/resize", test_tabstops_resize);
+ g_test_add_func("/vte/tabstops/previous", test_tabstops_previous);
+ g_test_add_func("/vte/tabstops/next", test_tabstops_next);
+
+ return g_test_run();
+}
diff --git a/src/tabstops.hh b/src/tabstops.hh
new file mode 100644
index 0000000..0722c36
--- /dev/null
+++ b/src/tabstops.hh
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <string.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "vtedefines.hh"
+
+namespace vte {
+
+namespace terminal {
+
+class Tabstops {
+public:
+ using position_t = unsigned int;
+ using signed_position_t = int;
+
+ static position_t const npos = position_t(signed_position_t(-1));
+
+private:
+ typedef unsigned long storage_t;
+
+ /* Number of bits used in storage */
+ position_t m_size{0};
+ /* Number of blocks in m_storage */
+ position_t m_capacity{0};
+ /* Bit storage */
+ storage_t* m_storage{nullptr};
+
+ inline position_t bits() const noexcept
+ {
+ return 8 * sizeof(storage_t);
+ }
+
+ inline position_t block(position_t position) const noexcept
+ {
+ return position / bits();
+ }
+
+ inline position_t block_first(position_t position) const noexcept
+ {
+ return position & ~(bits() - 1);
+ }
+
+ /* Mask with exactly the position's bit set */
+ inline storage_t mask(position_t position) const noexcept
+ {
+ return storage_t(1) << (position & (bits() - 1));
+ }
+
+ /* Mask with all bits set from position (excl.) up to the MSB */
+ inline storage_t mask_lower(position_t position) const noexcept
+ {
+ return ~(mask(position) | (mask(position) - 1));
+ }
+
+ /* Mask with all bits set from 0 to position (excl.) */
+ inline storage_t mask_upper(position_t position) const noexcept
+ {
+ return mask(position) - 1;
+ }
+
+ inline position_t next_position(position_t position) const noexcept
+ {
+ auto b = block(position);
+ auto v = m_storage[b] & mask_lower(position);
+ if (v != 0)
+ return b * bits() + __builtin_ctzl(v);
+
+ while (++b < m_capacity) {
+ v = m_storage[b];
+ if (v != 0)
+ return b * bits() + __builtin_ctzl(v);
+ }
+
+ return npos;
+ }
+
+ inline position_t previous_position(position_t position) const noexcept
+ {
+ auto b = block(position);
+ auto v = m_storage[b] & mask_upper(position);
+ if (v != 0)
+ return (b + 1) * bits() - __builtin_clzl(v) - 1;
+
+ while (b > 0) {
+ v = m_storage[--b];
+ if (v != 0)
+ return (b + 1) * bits() - __builtin_clzl(v) - 1;
+ }
+
+ return npos;
+ }
+
+public:
+ Tabstops(position_t size = VTE_COLUMNS,
+ bool set_default = true,
+ position_t tab_width = VTE_TAB_WIDTH) noexcept
+ {
+ resize(size, set_default, tab_width);
+ }
+
+ Tabstops(Tabstops const&) = delete;
+ Tabstops(Tabstops&&) = delete;
+
+ ~Tabstops()
+ {
+ free(m_storage);
+ };
+
+ Tabstops& operator=(Tabstops const&) = delete;
+ Tabstops& operator=(Tabstops&&) = delete;
+
+ inline position_t size() const noexcept
+ {
+ return m_size;
+ }
+
+ void resize(position_t size,
+ bool set_default = true,
+ position_t tab_width = VTE_TAB_WIDTH) noexcept
+ {
+ /* We want an even number of blocks */
+ auto const new_capacity = ((size + 8 * sizeof(storage_t) - 1) / (8 * sizeof(storage_t)) + 1)
& ~1;
+ g_assert_cmpuint(new_capacity % 2, ==, 0);
+ g_assert_cmpuint(new_capacity * 8 * sizeof(storage_t), >=, size);
+
+ if (new_capacity > m_capacity) {
+ auto const new_capacity_bytes = new_capacity * sizeof(storage_t);
+ m_storage = reinterpret_cast<storage_t*>(realloc(m_storage, new_capacity_bytes));
+ }
+
+ if (size > m_size) {
+ /* Clear storage */
+ auto b = block(m_size);
+ m_storage[b] &= mask_upper(m_size);
+ while (++b < new_capacity)
+ m_storage[b] = 0;
+ }
+
+ auto const old_size = m_size;
+ m_size = size;
+ m_capacity = new_capacity;
+
+ if (set_default) {
+ auto const r = old_size % tab_width;
+ position_t start = r ? old_size + tab_width - r : old_size;
+ for (position_t i = start; i < m_size; i += tab_width)
+ set(i);
+ }
+ }
+
+ inline void clear() noexcept
+ {
+ memset(m_storage, 0, m_capacity * sizeof(m_storage[0]));
+ }
+
+ inline void reset(position_t tab_width = VTE_TAB_WIDTH) noexcept
+ {
+ clear();
+ for (position_t i = 0; i < m_size; i += tab_width)
+ set(i);
+ }
+
+ inline void set(position_t position) noexcept
+ {
+ assert(position < m_size);
+ m_storage[block(position)] |= mask(position);
+ }
+
+ inline void unset(position_t position) noexcept
+ {
+ assert(position < m_size);
+ m_storage[block(position)] &= ~mask(position);
+ }
+
+ inline bool get(position_t position) const noexcept
+ {
+ return (m_storage[block(position)] & mask(position)) != 0;
+ }
+
+ inline position_t get_next(position_t position,
+ int count = 1,
+ position_t endpos = npos) const noexcept
+ {
+ while (count-- && position < m_size)
+ position = next_position(position);
+ return position < m_size ? position : endpos;
+ }
+
+ inline position_t get_previous(position_t position,
+ int count = 1,
+ position_t endpos = npos) const noexcept
+ {
+ while (count-- && position != npos)
+ position = previous_position(position);
+ return position != npos ? position : endpos;
+ }
+};
+
+} // namespace terminal
+
+} // namespace vte
diff --git a/src/vte.cc b/src/vte.cc
index 3e7ab4a..ea05d61 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -951,57 +951,6 @@ VteTerminalPrivate::deselect_all()
}
}
-// FIXMEchpe make m_tabstops a hashset
-
-/* Remove a tabstop. */
-void
-VteTerminalPrivate::clear_tabstop(int column)
-{
- if (m_tabstops) {
- /* Remove a tab stop from the hash table. */
- g_hash_table_remove(m_tabstops,
- GINT_TO_POINTER(2 * column + 1));
- }
-}
-
-/* Check if we have a tabstop at a given position. */
-bool
-VteTerminalPrivate::get_tabstop(int column)
-{
- if (m_tabstops != NULL) {
- auto hash = g_hash_table_lookup(m_tabstops,
- GINT_TO_POINTER(2 * column + 1));
- return hash != nullptr;
- }
-
- return false;
-}
-
-/* Reset the set of tab stops to the default. */
-void
-VteTerminalPrivate::set_tabstop(int column)
-{
- if (m_tabstops != NULL) {
- /* Just set a non-NULL pointer for this column number. */
- g_hash_table_insert(m_tabstops,
- GINT_TO_POINTER(2 * column + 1),
- m_terminal);
- }
-}
-
-/* Reset the set of tab stops to the default. */
-void
-VteTerminalPrivate::set_default_tabstops()
-{
- if (m_tabstops) {
- g_hash_table_destroy(m_tabstops);
- }
- m_tabstops = g_hash_table_new(nullptr, nullptr);
- for (int i = 0; i <= VTE_TAB_MAX; i += VTE_TAB_WIDTH) {
- set_tabstop(i);
- }
-}
-
/* Clear the cache of the screen contents we keep. */
void
VteTerminalPrivate::match_contents_clear()
@@ -7715,14 +7664,19 @@ VteTerminalPrivate::refresh_size()
return;
int rows, columns;
- if (vte_pty_get_size(m_pty, &rows, &columns, NULL)) {
- m_row_count = rows;
- m_column_count = columns;
- } else {
+ if (!vte_pty_get_size(m_pty, &rows, &columns, nullptr)) {
/* Error reading PTY size, use defaults */
- m_row_count = VTE_ROWS;
- m_column_count = VTE_COLUMNS;
+ rows = VTE_ROWS;
+ columns = VTE_COLUMNS;
}
+
+ if (m_row_count == rows &&
+ m_column_count == columns)
+ return;
+
+ m_row_count = rows;
+ m_column_count = columns;
+ m_tabstops.resize(columns);
}
/* Resize the given screen (normal or alternate) of the terminal. */
@@ -7889,6 +7843,7 @@ VteTerminalPrivate::set_size(long columns,
} else {
m_row_count = rows;
m_column_count = columns;
+ m_tabstops.resize(columns);
}
if (old_rows != m_row_count || old_columns != m_column_count) {
m_scrolling_restricted = FALSE;
@@ -8121,7 +8076,6 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
m_allow_bold = TRUE;
m_bold_is_bright = TRUE;
m_rewrap_on_resize = TRUE;
- set_default_tabstops();
m_input_enabled = TRUE;
@@ -8526,11 +8480,6 @@ VteTerminalPrivate::~VteTerminalPrivate()
/* Cancel pending adjustment change notifications. */
m_adjustment_changed_pending = FALSE;
- /* Tabstop information. */
- if (m_tabstops) {
- g_hash_table_destroy(m_tabstops);
- }
-
/* Free any selected text, but if we currently own the selection,
* throw the text onto the clipboard without an owner so that it
* doesn't just disappear. */
@@ -10541,6 +10490,11 @@ VteTerminalPrivate::reset(bool clear_tabstops,
m_modes_private.clear_saved();
m_modes_private.reset();
+ /* Reset tabstops */
+ if (clear_tabstops) {
+ m_tabstops.reset();
+ }
+
/* Window title stack */
if (clear_history) {
m_window_title_stack.clear();
@@ -10578,10 +10532,6 @@ VteTerminalPrivate::reset(bool clear_tabstops,
}
/* DECSCUSR cursor style */
set_cursor_style(VTE_CURSOR_STYLE_TERMINAL_DEFAULT);
- /* Do more stuff we refer to as a "full" reset. */
- if (clear_tabstops) {
- set_default_tabstops();
- }
/* Reset restricted scrolling regions, leave insert mode, make
* the cursor visible again. */
m_scrolling_restricted = FALSE;
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 361187c..781cc19 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -29,6 +29,7 @@
#include "parser.hh"
#include "parser-glue.hh"
#include "modes.hh"
+#include "tabstops.hh"
#include "vtepcre2.h"
#include "vteregexinternal.hh"
@@ -289,8 +290,10 @@ public:
GdkWindow *m_event_window;
/* Metric and sizing data: dimensions of the window */
- vte::grid::row_t m_row_count;
- vte::grid::column_t m_column_count;
+ vte::grid::row_t m_row_count{VTE_ROWS};
+ vte::grid::column_t m_column_count{VTE_COLUMNS};
+
+ vte::terminal::Tabstops m_tabstops{};
/* Emulation setup data. */
struct vte_parser* m_parser; /* control sequence state machine */
@@ -383,7 +386,6 @@ public:
gboolean m_audible_bell;
gboolean m_allow_bold;
gboolean m_bold_is_bright;
- GHashTable *m_tabstops;
gboolean m_text_modified_flag;
gboolean m_text_inserted_flag;
gboolean m_text_deleted_flag;
@@ -991,11 +993,6 @@ public:
void emit_paste_clipboard();
void emit_hyperlink_hover_uri_changed(const GdkRectangle *bbox);
- void clear_tabstop(int column); // FIXMEchpe vte::grid::column_t ?
- bool get_tabstop(int column);
- void set_tabstop(int column);
- void set_default_tabstops();
-
void hyperlink_invalidate_and_get_bbox(hyperlink_idx_t idx, GdkRectangle *bbox);
void hyperlink_hilite_update();
@@ -1210,7 +1207,8 @@ public:
inline void move_cursor_backward(vte::grid::column_t columns);
inline void move_cursor_forward(vte::grid::column_t columns);
- inline void move_cursor_tab();
+ inline void move_cursor_tab_backward(int count = 1);
+ inline void move_cursor_tab_forward(int count = 1);
inline void line_feed();
inline void erase_in_display(vte::parser::Sequence const& seq);
inline void erase_in_line(vte::parser::Sequence const& seq);
diff --git a/src/vteseq.cc b/src/vteseq.cc
index e834f8a..371bbb1 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -780,6 +780,8 @@ VteTerminalPrivate::clear_to_eol()
void
VteTerminalPrivate::set_cursor_column(vte::grid::column_t col)
{
+ _vte_debug_print(VTE_DEBUG_PARSER,
+ "Moving cursor to column %ld.\n", col);
m_screen->cursor.col = CLAMP(col, 0, m_column_count - 1);
}
@@ -1009,75 +1011,73 @@ VteTerminalPrivate::line_feed()
}
void
-VteTerminalPrivate::move_cursor_tab()
+VteTerminalPrivate::move_cursor_tab_backward(int count)
{
- long old_len;
- vte::grid::column_t newcol, col;
+ if (count == 0)
+ return;
+
+ auto const newcol = m_tabstops.get_previous(m_screen->cursor.col, count, 0);
+ set_cursor_column(newcol);
+}
- /* Calculate which column is the next tab stop. */
- newcol = col = m_screen->cursor.col;
+void
+VteTerminalPrivate::move_cursor_tab_forward(int count)
+{
+ if (count == 0)
+ return;
+ auto const col = m_screen->cursor.col;
g_assert (col >= 0);
- if (m_tabstops != NULL) {
- /* Find the next tabstop. */
- for (newcol++; newcol < VTE_TAB_MAX; newcol++) {
- if (get_tabstop(newcol)) {
- break;
- }
- }
- }
+ /* Find the next tabstop, but don't go beyond the end of the line */
+ auto const newcol = m_tabstops.get_next(col, count, m_column_count - 1);
- /* If we have no tab stops or went past the end of the line, stop
- * at the right-most column. */
- if (newcol >= m_column_count) {
- newcol = m_column_count - 1;
- }
+ /* Make sure we don't move cursor back (see bug #340631) */
+ // FIXMEchpe how could this happen!?
+ if (col >= newcol)
+ return;
- /* but make sure we don't move cursor back (bug #340631) */
- if (col < newcol) {
- VteRowData *rowdata = ensure_row();
-
- /* Smart tab handling: bug 353610
- *
- * If we currently don't have any cells in the space this
- * tab creates, we try to make the tab character copyable,
- * by appending a single tab char with lots of fragment
- * cells following it.
- *
- * Otherwise, just append empty cells that will show up
- * as a space each.
- */
-
- old_len = _vte_row_data_length (rowdata);
- _vte_row_data_fill (rowdata, &basic_cell, newcol);
-
- /* Insert smart tab if there's nothing in the line after
- * us, not even empty cells (with non-default background
- * color for example).
- *
- * Notable bugs here: 545924, 597242, 764330 */
- if (col >= old_len && newcol - col <= VTE_TAB_WIDTH_MAX) {
- glong i;
- VteCell *cell = _vte_row_data_get_writable (rowdata, col);
- VteCell tab = *cell;
- tab.attr.set_columns(newcol - col);
- tab.c = '\t';
- /* Save tab char */
- *cell = tab;
- /* And adjust the fragments */
- for (i = col + 1; i < newcol; i++) {
- cell = _vte_row_data_get_writable (rowdata, i);
- cell->c = '\t';
- cell->attr.set_columns(1);
- cell->attr.set_fragment(true);
- }
- }
+ /* Smart tab handling: bug 353610
+ *
+ * If we currently don't have any cells in the space this
+ * tab creates, we try to make the tab character copyable,
+ * by appending a single tab char with lots of fragment
+ * cells following it.
+ *
+ * Otherwise, just append empty cells that will show up
+ * as a space each.
+ */
+
+ VteRowData *rowdata = ensure_row();
+ auto const old_len = _vte_row_data_length (rowdata);
+ _vte_row_data_fill (rowdata, &basic_cell, newcol);
+
+ /* Insert smart tab if there's nothing in the line after
+ * us, not even empty cells (with non-default background
+ * color for example).
+ *
+ * Notable bugs here: 545924, 597242, 764330
+ */
+ if (col >= old_len && (newcol - col) <= VTE_TAB_WIDTH_MAX) {
+ glong i;
+ VteCell *cell = _vte_row_data_get_writable (rowdata, col);
+ VteCell tab = *cell;
+ tab.attr.set_columns(newcol - col);
+ tab.c = '\t';
+ /* Save tab char */
+ *cell = tab;
+ /* And adjust the fragments */
+ for (i = col + 1; i < newcol; i++) {
+ cell = _vte_row_data_get_writable (rowdata, i);
+ cell->c = '\t';
+ cell->attr.set_columns(1);
+ cell->attr.set_fragment(true);
+ }
+ }
- invalidate_cells(m_screen->cursor.col, newcol - m_screen->cursor.col,
- m_screen->cursor.row, 1);
- m_screen->cursor.col = newcol;
- }
+ invalidate_cells(m_screen->cursor.col, newcol - m_screen->cursor.col,
+ m_screen->cursor.row, 1);
+ m_screen->cursor.col = newcol;
}
void
@@ -1733,35 +1733,10 @@ VteTerminalPrivate::CBT(vte::parser::Sequence const& seq)
* References: ECMA-48 § 8.3.7
*/
#if 0
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
screen_cursor_clear_wrap(screen);
- screen_cursor_left_tab(screen, num);
#endif
- // FIXMEchpe! need to support the parameter!!!
-
- /* Calculate which column is the previous tab stop. */
- auto newcol = m_screen->cursor.col;
-
- if (m_tabstops) {
- /* Find the next tabstop. */
- while (newcol > 0) {
- newcol--;
- if (get_tabstop(newcol % m_column_count)) {
- break;
- }
- }
- }
-
- /* Warp the cursor. */
- _vte_debug_print(VTE_DEBUG_PARSER,
- "Moving cursor to column %ld.\n", (long)newcol);
- set_cursor_column(newcol);
+ move_cursor_tab_backward(seq.collect1(0, 1));
}
void
@@ -1811,19 +1786,10 @@ VteTerminalPrivate::CHT(vte::parser::Sequence const& seq)
* References: ECMA-48 § 8.3.10
*/
#if 0
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, num);
#endif
- auto const val = seq.collect1(0, 1, 1, int(m_column_count - m_screen->cursor.col));
- // FIXMEchpe stop when cursor.col reaches m_column_count!
- for (auto i = 0; i < val; i++)
- move_cursor_tab();
+ move_cursor_tab_forward(seq.collect1(0, 1));
}
void
@@ -3458,13 +3424,15 @@ VteTerminalPrivate::DECST8C(vte::parser::Sequence const& seq)
* Clear the tab-ruler and reset it to a tab at every 8th column,
* starting at 9 (though, setting a tab at 1 is fine as it has no
* effect).
+ *
+ * References: VT525
*/
-#if 0
- unsigned int i;
- for (i = 0; i < screen->page->width; i += 8)
- screen->tabs[i / 8] = 0x1;
-#endif
+ if (seq.collect1(0) != 5)
+ return;
+
+ m_tabstops.reset(8);
+ m_tabstops.unset(0);
}
void
@@ -4160,10 +4128,9 @@ VteTerminalPrivate::HT(vte::parser::Sequence const& seq)
*/
#if 0
screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, 1);
#endif
- move_cursor_tab();
+ move_cursor_tab_forward();
}
void
@@ -4178,18 +4145,8 @@ VteTerminalPrivate::HTS(vte::parser::Sequence const& seq)
*
* References: ECMA-48 § 8.3.62
*/
-#if 0
- unsigned int pos;
-
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] |= 1U << (pos % 8);
-#endif
- if (m_tabstops == nullptr) {
- m_tabstops = g_hash_table_new(nullptr, nullptr);
- }
- set_tabstop(m_screen->cursor.col);
+ m_tabstops.set(m_screen->cursor.col);
}
void
@@ -5108,6 +5065,45 @@ VteTerminalPrivate::SUB(vte::parser::Sequence const& seq)
}
void
+VteTerminalPrivate::TAC(vte::parser::Sequence const& seq)
+{
+ /*
+ * TAC - tabulation aligned centre
+ *
+ * Defaults:
+ * args[0]: no default
+ *
+ * References: ECMA-48 § 8.3.151
+ */
+}
+
+void
+VteTerminalPrivate::TALE(vte::parser::Sequence const& seq)
+{
+ /*
+ * TALE - tabulation aligned leading edge
+ *
+ * Defaults:
+ * args[0]: no default
+ *
+ * References: ECMA-48 § 8.3.152
+ */
+}
+
+void
+VteTerminalPrivate::TATE(vte::parser::Sequence const& seq)
+{
+ /*
+ * TATE - tabulation aligned trailing edge
+ *
+ * Defaults:
+ * args[0]: no default
+ *
+ * References: ECMA-48 § 8.3.153
+ */
+}
+
+void
VteTerminalPrivate::TBC(vte::parser::Sequence const& seq)
{
/*
@@ -5122,43 +5118,32 @@ VteTerminalPrivate::TBC(vte::parser::Sequence const& seq)
*
* References: ECMA-48 § 8.3.154
*/
-#if 0
- unsigned int mode = 0, pos;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] &= ~(1U << (pos % 8));
- break;
- case 3:
- if (screen->page->width > 0)
- memset(screen->tabs, 0, (screen->page->width + 7) / 8);
- break;
- }
-#endif
- auto const param = seq.collect1(0, 0);
+ auto const param = seq.collect1(0);
switch (param) {
+ case -1:
case 0:
- clear_tabstop(m_screen->cursor.col);
+ /* Clear character tabstop at the current presentation position */
+ m_tabstops.unset(m_screen->cursor.col);
break;
- case 1: // FIXME implement
+ case 1:
+ /* Clear line tabstop at the current line */
break;
- case 2: // FIXME implement
+ case 2:
+ /* Clear all character tabstops in the current line */
+ /* NOTE: vttest issues this but claims it's a 'no-op' */
+ m_tabstops.clear();
break;
case 3:
- if (m_tabstops != nullptr) {
- g_hash_table_destroy(m_tabstops);
- m_tabstops = nullptr;
- }
+ /* Clear all character tabstops */
+ m_tabstops.clear();
break;
- case 4: // FIXME implement
+ case 4:
+ /* Clear all line tabstops */
break;
- case 5: // FIXME implement
+ case 5:
+ /* Clear all (character and line) tabstops */
+ m_tabstops.clear();
break;
default:
break;
@@ -5166,6 +5151,41 @@ VteTerminalPrivate::TBC(vte::parser::Sequence const& seq)
}
void
+VteTerminalPrivate::TCC(vte::parser::Sequence const& seq)
+{
+ /*
+ * TCC - tabulation centred on character
+ *
+ * Defaults:
+ * args[0]: no default
+ * args[1]: 32 (SPACE)
+ *
+ * References: ECMA-48 § 8.3.155
+ */
+}
+
+void
+VteTerminalPrivate::TSR(vte::parser::Sequence const& seq)
+{
+ /*
+ * TSR - tabulation stop remove
+ * This clears a tab stop at position @arg[0] in the active line (presentation),
+ * and on any lines below it.
+ *
+ * Defaults:
+ * args[0]: no default
+ *
+ * References: ECMA-48 § 8.3.156
+ */
+
+ auto const pos = seq.collect1(0);
+ if (pos < 0 || pos >= m_column_count)
+ return;
+
+ m_tabstops.unset(pos);
+}
+
+void
VteTerminalPrivate::VPA(vte::parser::Sequence const& seq)
{
/*
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]