From 490f12ad1314116132175d10e3ce72d7a2811b52 Mon Sep 17 00:00:00 2001 From: Armaan Bhojwani Date: Tue, 6 Jul 2021 19:08:17 -0400 Subject: [PATCH] vis: add soft-wrap patch --- vis/build | 2 + vis/depends | 4 +- vis/patches/soft-wrap.patch | 632 ++++++++++++++++++++++++++++++++++++ vis/sources | 1 + vis/version | 2 +- 5 files changed, 638 insertions(+), 3 deletions(-) create mode 100644 vis/patches/soft-wrap.patch diff --git a/vis/build b/vis/build index 52807b7..f2c9adc 100755 --- a/vis/build +++ b/vis/build @@ -1,5 +1,7 @@ #!/bin/sh -e +patch -p1 < soft-wrap.patch + ./configure \ --prefix=/usr \ --enable-lua \ diff --git a/vis/depends b/vis/depends index 49f1e86..04dc213 100644 --- a/vis/depends +++ b/vis/depends @@ -1,4 +1,4 @@ libtermkey make -lpeg make -lua make +lpeg +lua ncurses diff --git a/vis/patches/soft-wrap.patch b/vis/patches/soft-wrap.patch new file mode 100644 index 0000000..9d9c04b --- /dev/null +++ b/vis/patches/soft-wrap.patch @@ -0,0 +1,632 @@ +From cc3a7e5566f7a33deeed5cbdcb9057e585c91dde Mon Sep 17 00:00:00 2001 +From: Andrey Proskurin <> +Date: Sun, 9 May 2021 00:34:16 +0000 +Subject: [PATCH 1/5] view: refactor view_addch + +--- + view.c | 158 ++++++++++++++++++++++++++++----------------------------- + 1 file changed, 79 insertions(+), 79 deletions(-) + +diff --git a/view.c b/view.c +index 74967dc6..b10deb92 100644 +--- a/view.c ++++ b/view.c +@@ -164,98 +164,98 @@ Filerange view_viewport_get(View *view) { + return (Filerange){ .start = view->start, .end = view->end }; + } + ++static bool view_add_cell(View *view, const Cell *cell) { ++ size_t lineno = view->line->lineno; ++ ++ if (view->col + cell->width > view->width) { ++ for (int i = view->col; i < view->width; i++) ++ view->line->cells[i] = view->cell_blank; ++ view->line = view->line->next; ++ view->col = 0; ++ } ++ ++ if (!view->line) ++ return false; ++ ++ view->line->width += cell->width; ++ view->line->len += cell->len; ++ view->line->lineno = lineno; ++ view->line->cells[view->col] = *cell; ++ view->col++; ++ /* set cells of a character which uses multiple columns */ ++ for (int i = 1; i < cell->width; i++) ++ view->line->cells[view->col++] = cell_unused; ++ return true; ++} ++ ++static bool view_expand_tab(View *view, Cell *cell) { ++ cell->width = 1; ++ ++ int displayed_width = view->tabwidth - (view->col % view->tabwidth); ++ for (int w = 0; w < displayed_width; ++w) { ++ ++ int t = (w == 0) ? SYNTAX_SYMBOL_TAB : SYNTAX_SYMBOL_TAB_FILL; ++ const char *symbol = view->symbols[t]->symbol; ++ strncpy(cell->data, symbol, sizeof(cell->data) - 1); ++ cell->len = (w == 0) ? 1 : 0; ++ ++ if (!view_add_cell(view, cell)) ++ return false; ++ } ++ ++ cell->len = 1; ++ return true; ++} ++ ++static bool view_expand_newline(View *view, Cell *cell) { ++ const char *symbol = view->symbols[SYNTAX_SYMBOL_EOL]->symbol; ++ strncpy(cell->data, symbol, sizeof(cell->data) - 1); ++ cell->width = 1; ++ ++ if (!view_add_cell(view, cell)) ++ return false; ++ ++ for (int i = view->col; i < view->width; ++i) ++ view->line->cells[i] = view->cell_blank; ++ ++ size_t lineno = view->line->lineno; ++ view->line = view->line->next; ++ view->col = 0; ++ if (view->line) ++ view->line->lineno = lineno + 1; ++ ++ return true; ++} ++ + /* try to add another character to the view, return whether there was space left */ + static bool view_addch(View *view, Cell *cell) { + if (!view->line) + return false; + +- int width; +- size_t lineno = view->line->lineno; + unsigned char ch = (unsigned char)cell->data[0]; + cell->style = view->cell_blank.style; + + switch (ch) { + case '\t': +- cell->width = 1; +- width = view->tabwidth - (view->col % view->tabwidth); +- for (int w = 0; w < width; w++) { +- if (view->col + 1 > view->width) { +- view->line = view->line->next; +- view->col = 0; +- if (!view->line) +- return false; +- view->line->lineno = lineno; +- } +- +- cell->len = w == 0 ? 1 : 0; +- int t = w == 0 ? SYNTAX_SYMBOL_TAB : SYNTAX_SYMBOL_TAB_FILL; +- strncpy(cell->data, view->symbols[t]->symbol, sizeof(cell->data)-1); +- view->line->cells[view->col] = *cell; +- view->line->len += cell->len; +- view->line->width += cell->width; +- view->col++; +- } +- cell->len = 1; +- return true; ++ return view_expand_tab(view, cell); + case '\n': +- cell->width = 1; +- if (view->col + cell->width > view->width) { +- view->line = view->line->next; +- view->col = 0; +- if (!view->line) +- return false; +- view->line->lineno = lineno; +- } +- +- strncpy(cell->data, view->symbols[SYNTAX_SYMBOL_EOL]->symbol, sizeof(cell->data)-1); +- +- view->line->cells[view->col] = *cell; +- view->line->len += cell->len; +- view->line->width += cell->width; +- for (int i = view->col + 1; i < view->width; i++) +- view->line->cells[i] = view->cell_blank; +- +- view->line = view->line->next; +- if (view->line) +- view->line->lineno = lineno + 1; +- view->col = 0; +- return true; +- default: +- if (ch < 128 && !isprint(ch)) { +- /* non-printable ascii char, represent it as ^(char + 64) */ +- *cell = (Cell) { +- .data = { '^', ch == 127 ? '?' : ch + 64, '\0' }, +- .len = 1, +- .width = 2, +- .style = cell->style, +- }; +- } +- +- if (ch == ' ') { +- strncpy(cell->data, view->symbols[SYNTAX_SYMBOL_SPACE]->symbol, sizeof(cell->data)-1); +- +- } +- +- if (view->col + cell->width > view->width) { +- for (int i = view->col; i < view->width; i++) +- view->line->cells[i] = view->cell_blank; +- view->line = view->line->next; +- view->col = 0; +- } ++ return view_expand_newline(view, cell); ++ case ' ': ++ const char *symbol = view->symbols[SYNTAX_SYMBOL_SPACE]->symbol; ++ strncpy(cell->data, symbol, sizeof(cell->data) - 1); ++ return view_add_cell(view, cell); ++ } + +- if (view->line) { +- view->line->width += cell->width; +- view->line->len += cell->len; +- view->line->lineno = lineno; +- view->line->cells[view->col] = *cell; +- view->col++; +- /* set cells of a character which uses multiple columns */ +- for (int i = 1; i < cell->width; i++) +- view->line->cells[view->col++] = cell_unused; +- return true; +- } +- return false; ++ if (ch < 128 && !isprint(ch)) { ++ /* non-printable ascii char, represent it as ^(char + 64) */ ++ *cell = (Cell) { ++ .data = { '^', ch == 127 ? '?' : ch + 64, '\0' }, ++ .len = 1, ++ .width = 2, ++ .style = cell->style, ++ }; + } ++ return view_add_cell(view, cell); + } + + static void cursor_to(Selection *s, size_t pos) { + +From 50e75ddf8a73feab300d7789d000f9687a509f18 Mon Sep 17 00:00:00 2001 +From: Andrey Proskurin <> +Date: Sun, 9 May 2021 18:17:20 +0000 +Subject: [PATCH 2/5] view.c: add word wrapping + +--- + view.c | 61 +++++++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 45 insertions(+), 16 deletions(-) + +diff --git a/view.c b/view.c +index b10deb92..e7ca8141 100644 +--- a/view.c ++++ b/view.c +@@ -80,6 +80,10 @@ struct View { + bool need_update; /* whether view has been redrawn */ + bool large_file; /* optimize for displaying large files */ + int colorcolumn; ++ // TODO lua option: breakat / brk ++ const char *breakat; /* characters which might cause a word wrap */ ++ int wrapcol; /* used while drawing view content, column where word wrap might happen */ ++ bool prevch_breakat; /* used while drawing view content, previous char is part of breakat */ + }; + + static const SyntaxSymbol symbols_none[] = { +@@ -109,6 +113,7 @@ static bool view_viewport_up(View *view, int n); + static bool view_viewport_down(View *view, int n); + + static void view_clear(View *view); ++static bool view_add_cell(View *view, const Cell *cell); + static bool view_addch(View *view, Cell *cell); + static void selection_free(Selection*); + /* set/move current cursor position to a given (line, column) pair */ +@@ -156,6 +161,8 @@ static void view_clear(View *view) { + view->bottomline->next = NULL; + view->line = view->topline; + view->col = 0; ++ view->wrapcol = 0; ++ view->prevch_breakat = false; + if (view->ui) + view->cell_blank.style = view->ui->style_get(view->ui, UI_STYLE_DEFAULT); + } +@@ -164,19 +171,37 @@ Filerange view_viewport_get(View *view) { + return (Filerange){ .start = view->start, .end = view->end }; + } + ++static void view_wrap_line(View *view) { ++ Line *cur_line = view->line; ++ int cur_col = view->col; ++ int wrapcol = (view->wrapcol > 0) ? view->wrapcol : cur_col; ++ ++ view->line = cur_line->next; ++ view->col = 0; ++ view->wrapcol = 0; ++ if (view->line) { ++ /* move extra cells to the next line */ ++ for (int i = wrapcol; i < cur_col; ++i) { ++ const Cell *cell = &cur_line->cells[i]; ++ view_add_cell(view, cell); ++ cur_line->width -= cell->width; ++ cur_line->len -= cell->len; ++ } ++ } ++ for (int i = wrapcol; i < view->width; ++i) { ++ /* clear remaining of line */ ++ cur_line->cells[i] = view->cell_blank; ++ } ++} ++ + static bool view_add_cell(View *view, const Cell *cell) { + size_t lineno = view->line->lineno; + +- if (view->col + cell->width > view->width) { +- for (int i = view->col; i < view->width; i++) +- view->line->cells[i] = view->cell_blank; +- view->line = view->line->next; +- view->col = 0; +- } ++ if (view->col + cell->width > view->width) ++ view_wrap_line(view); + + if (!view->line) + return false; +- + view->line->width += cell->width; + view->line->len += cell->len; + view->line->lineno = lineno; +@@ -208,22 +233,18 @@ static bool view_expand_tab(View *view, Cell *cell) { + } + + static bool view_expand_newline(View *view, Cell *cell) { ++ size_t lineno = view->line->lineno; + const char *symbol = view->symbols[SYNTAX_SYMBOL_EOL]->symbol; ++ + strncpy(cell->data, symbol, sizeof(cell->data) - 1); + cell->width = 1; +- + if (!view_add_cell(view, cell)) + return false; + +- for (int i = view->col; i < view->width; ++i) +- view->line->cells[i] = view->cell_blank; +- +- size_t lineno = view->line->lineno; +- view->line = view->line->next; +- view->col = 0; ++ view->wrapcol = 0; ++ view_wrap_line(view); + if (view->line) + view->line->lineno = lineno + 1; +- + return true; + } + +@@ -233,8 +254,14 @@ static bool view_addch(View *view, Cell *cell) { + return false; + + unsigned char ch = (unsigned char)cell->data[0]; ++ bool ch_breakat = strchr(view->breakat, ch); ++ if (view->prevch_breakat && !ch_breakat) { ++ /* this is a good place to wrap line if needed */ ++ view->wrapcol = view->col; ++ } ++ view->prevch_breakat = ch_breakat; + cell->style = view->cell_blank.style; +- ++ + switch (ch) { + case '\t': + return view_expand_tab(view, cell); +@@ -519,6 +546,8 @@ View *view_new(Text *text) { + .data = " ", + }; + view->tabwidth = 8; ++ // TODO default value ++ view->breakat = ""; + view_options_set(view, 0); + + if (!view_resize(view, 1, 1)) { + +From b50672e3233e5e2d2a537d697082806a5012d6ac Mon Sep 17 00:00:00 2001 +From: Andrey Proskurin <> +Date: Sun, 9 May 2021 21:56:36 +0000 +Subject: [PATCH 3/5] add `wrapcolumn / wc` and `breakat / brk` options + +--- + man/vis.1 | 5 +++++ + sam.c | 12 ++++++++++++ + view.c | 29 +++++++++++++++++++++++------ + view.h | 2 ++ + vis-cmds.c | 6 ++++++ + 5 files changed, 48 insertions(+), 6 deletions(-) + +diff --git a/man/vis.1 b/man/vis.1 +index 05433663..2f6b4754 100644 +--- a/man/vis.1 ++++ b/man/vis.1 +@@ -1423,6 +1423,11 @@ WARNING: modifying a memory mapped file in-place will cause data loss. + Whether to use vertical or horizontal layout. + .It Cm ignorecase , Cm ic Op Cm off + Whether to ignore case when searching. ++.It Ic wrapcolumn , Ic wc Op Ar 0 ++Wrap lines at minimum of window width and wrapcolumn. ++. ++.It Ic breakat , brk Op Dq Pa "" ++Characters which might cause a word wrap. + .El + . + .Sh COMMAND and SEARCH PROMPT +diff --git a/sam.c b/sam.c +index 29e9c583..d7540e07 100644 +--- a/sam.c ++++ b/sam.c +@@ -301,6 +301,8 @@ enum { + OPTION_CHANGE_256COLORS, + OPTION_LAYOUT, + OPTION_IGNORECASE, ++ OPTION_BREAKAT, ++ OPTION_WRAP_COLUMN, + }; + + static const OptionDef options[] = { +@@ -394,6 +396,16 @@ static const OptionDef options[] = { + VIS_OPTION_TYPE_BOOL, + VIS_HELP("Ignore case when searching") + }, ++ [OPTION_BREAKAT] = { ++ { "breakat", "brk" }, ++ VIS_OPTION_TYPE_STRING|VIS_OPTION_NEED_WINDOW, ++ VIS_HELP("Characters which might cause a word wrap") ++ }, ++ [OPTION_WRAP_COLUMN] = { ++ { "wrapcolumn", "wc" }, ++ VIS_OPTION_TYPE_NUMBER|VIS_OPTION_NEED_WINDOW, ++ VIS_HELP("Wrap lines at minimum of window width and wrapcolumn") ++ }, + }; + + bool sam_init(Vis *vis) { +diff --git a/view.c b/view.c +index e7ca8141..79fc7bc1 100644 +--- a/view.c ++++ b/view.c +@@ -80,9 +80,9 @@ struct View { + bool need_update; /* whether view has been redrawn */ + bool large_file; /* optimize for displaying large files */ + int colorcolumn; +- // TODO lua option: breakat / brk +- const char *breakat; /* characters which might cause a word wrap */ +- int wrapcol; /* used while drawing view content, column where word wrap might happen */ ++ char *breakat; /* characters which might cause a word wrap */ ++ int wrapcolumn; /* wrap lines at minimum of window width and wrapcolumn (if != 0) */ ++ int wrapcol; /* used while drawing view content, column where word wrap might happen */ + bool prevch_breakat; /* used while drawing view content, previous char is part of breakat */ + }; + +@@ -171,6 +171,12 @@ Filerange view_viewport_get(View *view) { + return (Filerange){ .start = view->start, .end = view->end }; + } + ++static int view_max_text_width(const View *view) { ++ if (view->wrapcolumn > 0) ++ return MIN(view->wrapcolumn, view->width); ++ return view->width; ++} ++ + static void view_wrap_line(View *view) { + Line *cur_line = view->line; + int cur_col = view->col; +@@ -197,7 +203,7 @@ static void view_wrap_line(View *view) { + static bool view_add_cell(View *view, const Cell *cell) { + size_t lineno = view->line->lineno; + +- if (view->col + cell->width > view->width) ++ if (view->col + cell->width > view_max_text_width(view)) + view_wrap_line(view); + + if (!view->line) +@@ -519,6 +525,7 @@ void view_free(View *view) { + selection_free(view->selections); + free(view->textbuf); + free(view->lines); ++ free(view->breakat); + free(view); + } + +@@ -546,8 +553,8 @@ View *view_new(Text *text) { + .data = " ", + }; + view->tabwidth = 8; +- // TODO default value +- view->breakat = ""; ++ view->breakat = strdup(""); ++ view->wrapcolumn = 0; + view_options_set(view, 0); + + if (!view_resize(view, 1, 1)) { +@@ -891,6 +898,16 @@ int view_colorcolumn_get(View *view) { + return view->colorcolumn; + } + ++void view_wrapcolumn_set(View *view, int col) { ++ if (col >= 0) ++ view->wrapcolumn = col; ++} ++ ++void view_breakat_set(View *view, const char *breakat) { ++ free(view->breakat); ++ view->breakat = strdup(breakat); ++} ++ + size_t view_screenline_goto(View *view, int n) { + size_t pos = view->start; + for (Line *line = view->topline; --n > 0 && line != view->lastline; line = line->next) +diff --git a/view.h b/view.h +index 31b044b8..65bcb29d 100644 +--- a/view.h ++++ b/view.h +@@ -358,6 +358,8 @@ void view_options_set(View*, enum UiOption options); + enum UiOption view_options_get(View*); + void view_colorcolumn_set(View*, int col); + int view_colorcolumn_get(View*); ++void view_wrapcolumn_set(View*, int col); ++void view_breakat_set(View*, const char *breakat); + + /** Set how many spaces are used to display a tab `\t` character. */ + void view_tabwidth_set(View*, int tabwidth); +diff --git a/vis-cmds.c b/vis-cmds.c +index f5221d14..e2bff70d 100644 +--- a/vis-cmds.c ++++ b/vis-cmds.c +@@ -364,6 +364,12 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Select + case OPTION_IGNORECASE: + vis->ignorecase = toggle ? !vis->ignorecase : arg.b; + break; ++ case OPTION_BREAKAT: ++ view_breakat_set(win->view, arg.s); ++ break; ++ case OPTION_WRAP_COLUMN: ++ view_wrapcolumn_set(win->view, arg.i); ++ break; + default: + if (!opt->func) + return false; + +From ee36292c44370678f261ea843c3ebcf02fa19156 Mon Sep 17 00:00:00 2001 +From: Andrey Proskurin <> +Date: Fri, 14 May 2021 16:44:44 +0000 +Subject: [PATCH 4/5] view.c: check return value of strdup + +--- + view.c | 32 +++++++++++++++++--------------- + view.h | 2 +- + vis-cmds.c | 5 ++++- + 3 files changed, 22 insertions(+), 17 deletions(-) + +diff --git a/view.c b/view.c +index 79fc7bc1..f1864e8b 100644 +--- a/view.c ++++ b/view.c +@@ -273,11 +273,11 @@ static bool view_addch(View *view, Cell *cell) { + return view_expand_tab(view, cell); + case '\n': + return view_expand_newline(view, cell); +- case ' ': ++ case ' ': { + const char *symbol = view->symbols[SYNTAX_SYMBOL_SPACE]->symbol; + strncpy(cell->data, symbol, sizeof(cell->data) - 1); + return view_add_cell(view, cell); +- } ++ }} + + if (ch < 128 && !isprint(ch)) { + /* non-printable ascii char, represent it as ^(char + 64) */ +@@ -541,29 +541,27 @@ View *view_new(Text *text) { + View *view = calloc(1, sizeof(View)); + if (!view) + return NULL; +- view->text = text; +- if (!view_selections_new(view, 0)) { +- view_free(view); +- return NULL; +- } + ++ view->text = text; ++ view->tabwidth = 8; ++ view->breakat = strdup(""); ++ view->wrapcolumn = 0; + view->cell_blank = (Cell) { + .width = 0, + .len = 0, + .data = " ", + }; +- view->tabwidth = 8; +- view->breakat = strdup(""); +- view->wrapcolumn = 0; + view_options_set(view, 0); + +- if (!view_resize(view, 1, 1)) { ++ if (!view->breakat || ++ !view_selections_new(view, 0) || ++ !view_resize(view, 1, 1)) ++ { + view_free(view); + return NULL; + } +- ++ + view_cursor_to(view, 0); +- + return view; + } + +@@ -903,9 +901,13 @@ void view_wrapcolumn_set(View *view, int col) { + view->wrapcolumn = col; + } + +-void view_breakat_set(View *view, const char *breakat) { ++bool view_breakat_set(View *view, const char *breakat) { ++ char *copy = strdup(breakat); ++ if (!copy) ++ return false; + free(view->breakat); +- view->breakat = strdup(breakat); ++ view->breakat = copy; ++ return true; + } + + size_t view_screenline_goto(View *view, int n) { +diff --git a/view.h b/view.h +index 65bcb29d..dadecb48 100644 +--- a/view.h ++++ b/view.h +@@ -359,7 +359,7 @@ enum UiOption view_options_get(View*); + void view_colorcolumn_set(View*, int col); + int view_colorcolumn_get(View*); + void view_wrapcolumn_set(View*, int col); +-void view_breakat_set(View*, const char *breakat); ++bool view_breakat_set(View*, const char *breakat); + + /** Set how many spaces are used to display a tab `\t` character. */ + void view_tabwidth_set(View*, int tabwidth); +diff --git a/vis-cmds.c b/vis-cmds.c +index e2bff70d..d3b5f89a 100644 +--- a/vis-cmds.c ++++ b/vis-cmds.c +@@ -365,7 +365,10 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Select + vis->ignorecase = toggle ? !vis->ignorecase : arg.b; + break; + case OPTION_BREAKAT: +- view_breakat_set(win->view, arg.s); ++ if (!view_breakat_set(win->view, arg.s)) { ++ vis_info_show(vis, "Failed to set breakat"); ++ return false; ++ } + break; + case OPTION_WRAP_COLUMN: + view_wrapcolumn_set(win->view, arg.i); + +From f698e53e4772497c41a12288339e3841dbca9680 Mon Sep 17 00:00:00 2001 +From: Andrey Proskurin +Date: Fri, 14 May 2021 18:46:20 +0000 +Subject: [PATCH 5/5] view.c: add utf-8 support to `breakat` + +--- + view.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/view.c b/view.c +index f1864e8b..a399dd02 100644 +--- a/view.c ++++ b/view.c +@@ -260,7 +260,7 @@ static bool view_addch(View *view, Cell *cell) { + return false; + + unsigned char ch = (unsigned char)cell->data[0]; +- bool ch_breakat = strchr(view->breakat, ch); ++ bool ch_breakat = strstr(view->breakat, cell->data); + if (view->prevch_breakat && !ch_breakat) { + /* this is a good place to wrap line if needed */ + view->wrapcol = view->col; diff --git a/vis/sources b/vis/sources index 5ba0924..63cb838 100644 --- a/vis/sources +++ b/vis/sources @@ -1 +1,2 @@ https://github.com/martanne/vis/releases/download/v0.7/vis-0.7.tar.gz +patches/soft-wrap.patch diff --git a/vis/version b/vis/version index bf826d9..6f0e2c0 100644 --- a/vis/version +++ b/vis/version @@ -1 +1 @@ -0.7 2 +0.7 3 -- 2.39.2