diff --git a/grid.c b/grid.c index 6ab58eb62..7539d72ef 100644 --- a/grid.c +++ b/grid.c @@ -1,4 +1,4 @@ -/* $OpenBSD: grid.c,v 1.157 2026/08/28 08:11:08 nicm Exp $ */ +/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -1660,21 +1660,29 @@ grid_in_set(struct grid *gd, u_int px, u_int py, const char *set) { struct grid_cell gc, tmp_gc; u_int pxx; + int has_tab, has_space; + + has_tab = (strchr(set, '\t') != NULL); + has_space = (strchr(set, ' ') != NULL); grid_get_cell(gd, px, py, &gc); - if (strchr(set, '\t')) { - if (gc.flags & GRID_FLAG_PADDING) { - pxx = px; - do - grid_get_cell(gd, --pxx, py, &tmp_gc); - while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING); - if (tmp_gc.flags & GRID_FLAG_TAB) - return (tmp_gc.data.width - (px - pxx)); - } else if (gc.flags & GRID_FLAG_TAB) - return (gc.data.width); - } - if (gc.flags & GRID_FLAG_PADDING) + if (gc.flags & GRID_FLAG_PADDING) { + if (!has_tab && !has_space) + return (0); + pxx = px; + do + grid_get_cell(gd, --pxx, py, &tmp_gc); + while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING); + if (((has_tab || has_space) && + (tmp_gc.flags & GRID_FLAG_TAB)) || + (has_space && utf8_has_whitespace(&tmp_gc.data))) + return (tmp_gc.data.width - (px - pxx)); return (0); + } + if ((has_tab || has_space) && (gc.flags & GRID_FLAG_TAB)) + return (gc.data.width); + if (has_space && utf8_has_whitespace(&gc.data)) + return (gc.data.width == 0 ? 1 : gc.data.width); return (utf8_cstrhas(set, &gc.data)); } diff --git a/options-table.c b/options-table.c index ecca20153..81f043864 100644 --- a/options-table.c +++ b/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options-table.c,v 1.243 2026/08/25 08:37:08 nicm Exp $ */ +/* $OpenBSD: options-table.c,v 1.244 2026/09/01 12:49:49 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -1251,7 +1251,8 @@ const struct options_table_entry options_table[] = { * underscore. */ .default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", - .text = "Characters considered to separate words." + .text = "Characters considered to separate words; a space matches " + "any character with the Unicode White_Space property." }, /* Window options. */ diff --git a/tmux.1 b/tmux.1 index 2b2b28f17..8ecca4fbd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1162 2026/08/31 19:34:09 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1163 2026/09/01 12:49:49 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 31 2026 $ +.Dd $Mdocdate: September 1 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -2206,14 +2206,14 @@ Move to the end of the next word. .Xc Same as .Ic next\-word -but use a space alone as the word separator. +but treat all non-whitespace characters as part of a word. .It Xo .Ic next\-space\-end (vi: E) .Xc Same as .Ic next\-word\-end -but use a space alone as the word separator. +but treat all non-whitespace characters as part of a word. .It Xo .Ic other\-end (vi: o) @@ -2287,7 +2287,7 @@ Move to the previous word. .Xc Same as .Ic previous\-word -but use a space alone as the word separator. +but treat all non-whitespace characters as part of a word. .It Xo .Ic rectangle\-on .Xc @@ -2572,8 +2572,8 @@ Word separators can be customized with the session option. Next word moves to the start of the next word, next word end to the end of the next word and previous word to the start of the previous word. -The three next and previous space keys work similarly but use a space alone as -the word separator. +The three next and previous space keys work similarly but treat all +non-whitespace characters as part of a word. Setting .Em word\-separators to the empty string makes next/previous word equivalent to next/previous space. @@ -5647,6 +5647,11 @@ If set to both, a bell and a message are produced. Sets the session's conception of what characters are considered word separators, for the purposes of the next and previous word commands in copy mode. +A space in +.Ar string +matches any character with the Unicode +.Em White_Space +property. .El .Pp Available window options are: diff --git a/tmux.h b/tmux.h index 02059ccc7..c920f8408 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1432 2026/08/31 19:34:09 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1433 2026/09/01 12:49:49 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -4057,6 +4057,7 @@ void session_update_history(struct session *); /* utf8.c */ enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *); enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *); +int utf8_has_whitespace(const struct utf8_data *); void utf8_update_width_cache(void); utf8_char utf8_build_one(u_char); enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *); diff --git a/utf8.c b/utf8.c index e634b43f1..076b1ff56 100644 --- a/utf8.c +++ b/utf8.c @@ -1,4 +1,4 @@ -/* $OpenBSD: utf8.c,v 1.71 2026/05/12 09:37:25 nicm Exp $ */ +/* $OpenBSD: utf8.c,v 1.72 2026/09/01 12:49:49 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -594,6 +594,72 @@ utf8_towc(const struct utf8_data *ud, wchar_t *wc) return (UTF8_DONE); } +/* Check for a Unicode whitespace character. */ +int +utf8_has_whitespace(const struct utf8_data *ud) +{ + struct utf8_data tmp; + wchar_t wc; + u_int offset = 0, size; + u_char ch; + + while (offset < ud->size) { + ch = ud->data[offset]; + if (ch < 0x80) { + wc = ch; + size = 1; + } else { + if (ch >= 0xc2 && ch <= 0xdf) + size = 2; + else if (ch >= 0xe0 && ch <= 0xef) + size = 3; + else if (ch >= 0xf0 && ch <= 0xf4) + size = 4; + else + return (0); + if (size > ud->size - offset) + return (0); + + memset(&tmp, 0, sizeof tmp); + memcpy(tmp.data, ud->data + offset, size); + tmp.size = tmp.have = size; + if (utf8_towc(&tmp, &wc) != UTF8_DONE) + return (0); + } + offset += size; + + switch (wc) { + case 0x0009: + case 0x000A: + case 0x000B: + case 0x000C: + case 0x000D: + case 0x0020: + case 0x0085: + case 0x00A0: + case 0x1680: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x2028: + case 0x2029: + case 0x202F: + case 0x205F: + case 0x3000: + return (1); + } + } + return (0); +} + /* Convert wide character to UTF-8 character. */ enum utf8_state utf8_fromwc(wchar_t wc, struct utf8_data *ud)