From 1af6ac07e04f481a94e6df02dec5ee55dc420b8e Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Thu, 25 Oct 2018 17:47:45 -0500 Subject: [PATCH] display: extend whitespace display to work with newlines These default to U+21B5 in UTF-8 mode, and "<" otherwise. --- doc/nano.texi | 6 +++--- doc/nanorc.5 | 6 +++--- src/browser.c | 4 ++-- src/files.c | 8 ++++---- src/global.c | 5 +++-- src/nano.c | 9 ++++++--- src/prompt.c | 2 +- src/proto.h | 5 +++-- src/rcfile.c | 7 +++++-- src/search.c | 4 ++-- src/winio.c | 38 ++++++++++++++++++++++++++------------ 11 files changed, 58 insertions(+), 36 deletions(-) diff --git a/doc/nano.texi b/doc/nano.texi index a643ec80..0294ef95 100644 --- a/doc/nano.texi +++ b/doc/nano.texi @@ -919,9 +919,9 @@ unless @option{--restricted} is given on the command line. @anchor{Whitespace} @item set whitespace "@var{string}" -Set the two characters used to indicate the presence of tabs and -spaces. They must be single-column characters. The default pair -for a UTF-8 locale is @t{"»·"}, and for other locales @t{">."}. +Set the three characters used to indicate the presence of tabs, spaces, and +newlines. They must be single-column characters. The default triad +for a UTF-8 locale is @t{"»·↵"}, and for other locales @t{">.<"}. @item set wordbounds Detect word boundaries differently by treating punctuation diff --git a/doc/nanorc.5 b/doc/nanorc.5 index 96e49df6..e8494374 100644 --- a/doc/nanorc.5 +++ b/doc/nanorc.5 @@ -285,9 +285,9 @@ This mode allows the user to open also other files for viewing, unless \fB--restricted\fR is given on the command line. .TP .B set whitespace "\fIstring\fP" -Set the two characters used to indicate the presence of tabs and -spaces. They must be single-column characters. The default pair -for a UTF-8 locale is "\fB\[Fc]\[md]\fR", and for other locales "\fB>.\fR". +Set the three characters used to indicate the presence of tabs, spaces, and +newlines. They must be single-column characters. The default triad +for a UTF-8 locale is "\fB\[Fc]\[md]\[CR]\fR", and for other locales "\fB>.<\fR". .TP .B set wordbounds Detect word boundaries differently by treating punctuation diff --git a/src/browser.c b/src/browser.c index 460e10be..3f00ac29 100644 --- a/src/browser.c +++ b/src/browser.c @@ -528,7 +528,7 @@ void browser_refresh(void) /* Whether to put an ellipsis before the filename? We don't * waste space on dots when there are fewer than 15 columns. */ char *disp = display_string(thename, dots ? - namelen + infomaxlen + 4 - longest : 0, longest, FALSE); + namelen + infomaxlen + 4 - longest : 0, longest, FALSE, FALSE); /* The filename (or a fragment of it) in displayable format. * When a fragment, account for dots plus one space padding. */ @@ -666,7 +666,7 @@ int filesearch_init(bool forwards) /* If something was searched for before, show it between square brackets. */ if (*last_search != '\0') { - char *disp = display_string(last_search, 0, COLS / 3, FALSE); + char *disp = display_string(last_search, 0, COLS / 3, FALSE, FALSE); thedefault = charalloc(strlen(disp) + 7); /* We use (COLS / 3) here because we need to see more on the line. */ diff --git a/src/files.c b/src/files.c index 5878ff00..04565b4c 100644 --- a/src/files.c +++ b/src/files.c @@ -359,13 +359,13 @@ int do_lockfile(const char *filename) postedname = mallocstrcpy(NULL, "_"); else if (room < strlenpt(filename)) { char *fragment = display_string(filename, - strlenpt(filename) - room + 3, room, FALSE); + strlenpt(filename) - room + 3, room, FALSE, FALSE); postedname = charalloc(strlen(fragment) + 4); strcpy(postedname, "..."); strcat(postedname, fragment); free(fragment); } else - postedname = display_string(filename, 0, room, FALSE); + postedname = display_string(filename, 0, room, FALSE, FALSE); /* Allow extra space for username (14), program name (8), PID (8), * and terminating \0 (1), minus the %s (2) for the file name. */ @@ -2262,7 +2262,7 @@ int do_writeout(bool exiting, bool withprompt) if (name_exists) { char *question = _("File \"%s\" exists; OVERWRITE? "); char *name = display_string(answer, 0, - COLS - strlenpt(question) + 1, FALSE); + COLS - strlenpt(question) + 1, FALSE, FALSE); char *message = charalloc(strlen(question) + strlen(name) + 1); @@ -2713,7 +2713,7 @@ char *input_tab(char *buf, bool allow_files, size_t *place, break; } - disp = display_string(matches[match], 0, longest_name, FALSE); + disp = display_string(matches[match], 0, longest_name, FALSE, FALSE); waddstr(edit, disp); free(disp); diff --git a/src/global.c b/src/global.c index e1d7930f..89267e7e 100644 --- a/src/global.c +++ b/src/global.c @@ -127,8 +127,9 @@ char *matchbrackets = NULL; /* The opening and closing brackets that can be found by bracket * searches. */ char *whitespace = NULL; - /* The characters used when visibly showing tabs and spaces. */ -int whitespace_len[2]; + /* The characters used when visibly showing tabs, spaces, and + * newlines. */ +int whitespace_len[3]; /* The length in bytes of these characters. */ #endif diff --git a/src/nano.c b/src/nano.c index 8c42eaa9..44cded48 100644 --- a/src/nano.c +++ b/src/nano.c @@ -2506,16 +2506,19 @@ int main(int argc, char **argv) #ifdef ENABLE_UTF8 if (using_utf8()) { /* A tab is shown as a Right-Pointing Double Angle Quotation Mark - * (U+00BB), and a space as a Middle Dot (U+00B7). */ - whitespace = mallocstrcpy(NULL, "\xC2\xBB\xC2\xB7"); + * (U+00BB), a space as a Middle Dot (U+00B7), and a newline as a + * Downwards Arrow With Corner Leftwards (U+21B5). */ + whitespace = mallocstrcpy(NULL, "\xC2\xBB\xC2\xB7\xE2\x86\xB5"); whitespace_len[0] = 2; whitespace_len[1] = 2; + whitespace_len[2] = 3; } else #endif { - whitespace = mallocstrcpy(NULL, ">."); + whitespace = mallocstrcpy(NULL, ">.<"); whitespace_len[0] = 1; whitespace_len[1] = 1; + whitespace_len[2] = 1; } } #endif /* !NANO_TINY */ diff --git a/src/prompt.c b/src/prompt.c index e3e394ef..a44dc927 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -395,7 +395,7 @@ void draw_the_promptbar(void) waddch(bottomwin, ':'); waddch(bottomwin, (the_page == 0) ? ' ' : '<'); - expanded = display_string(answer, the_page, COLS - base - 1, FALSE); + expanded = display_string(answer, the_page, COLS - base - 1, FALSE, FALSE); waddstr(bottomwin, expanded); free(expanded); diff --git a/src/proto.h b/src/proto.h index a157ece0..15f981d8 100644 --- a/src/proto.h +++ b/src/proto.h @@ -97,7 +97,7 @@ extern openfilestruct *firstfile; #ifndef NANO_TINY extern char *matchbrackets; extern char *whitespace; -extern int whitespace_len[2]; +extern int whitespace_len[3]; #endif extern const char *exit_tag; @@ -626,7 +626,8 @@ void blank_statusbar(void); void wipe_statusbar(void); void blank_bottombars(void); void check_statusblank(void); -char *display_string(const char *buf, size_t column, size_t span, bool isdata); +char *display_string(const char *buf, size_t column, size_t span, bool isdata, + bool show_newline); void titlebar(const char *path); void statusbar(const char *msg); void warn_and_shortly_pause(const char *msg); diff --git a/src/rcfile.c b/src/rcfile.c index d8a2d1fe..11c9dd17 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -1123,14 +1123,17 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) } } else if (strcasecmp(rcopts[i].name, "whitespace") == 0) { whitespace = option; - if (mbstrlen(whitespace) != 2 || strlenpt(whitespace) != 2) { - rcfile_error(N_("Two single-column characters required")); + if (mbstrlen(whitespace) != 3 || strlenpt(whitespace) != 3) { + rcfile_error(N_("Three single-column characters required")); free(whitespace); whitespace = NULL; } else { whitespace_len[0] = parse_mbchar(whitespace, NULL, NULL); whitespace_len[1] = parse_mbchar(whitespace + whitespace_len[0], NULL, NULL); + whitespace_len[2] = parse_mbchar(whitespace + + whitespace_len[0] + + whitespace_len[1], NULL, NULL); } } else #endif diff --git a/src/search.c b/src/search.c index a8b14291..246143ca 100644 --- a/src/search.c +++ b/src/search.c @@ -83,7 +83,7 @@ void search_init(bool replacing, bool keep_the_answer) /* If something was searched for earlier, include it in the prompt. */ if (*last_search != '\0') { - char *disp = display_string(last_search, 0, COLS / 3, FALSE); + char *disp = display_string(last_search, 0, COLS / 3, FALSE, FALSE); thedefault = charalloc(strlen(disp) + 7); /* We use (COLS / 3) here because we need to see more on the line. */ @@ -388,7 +388,7 @@ void do_findnext(void) /* Report on the status bar that the given string was not found. */ void not_found_msg(const char *str) { - char *disp = display_string(str, 0, (COLS / 2) + 1, FALSE); + char *disp = display_string(str, 0, (COLS / 2) + 1, FALSE, FALSE); size_t numchars = actual_x(disp, strnlenpt(disp, COLS / 2)); statusline(HUSH, _("\"%.*s%s\" not found"), numchars, disp, diff --git a/src/winio.c b/src/winio.c index 5a98456d..cd6aa43d 100644 --- a/src/winio.c +++ b/src/winio.c @@ -1853,8 +1853,10 @@ void check_statusblank(void) * at most span columns. column is zero-based, and span is one-based, so * span == 0 means you get "" returned. The returned string is dynamically * allocated, and should be freed. If isdata is TRUE, the caller might put - * "$" at the beginning or end of the line if it's too long. */ -char *display_string(const char *buf, size_t column, size_t span, bool isdata) + * "$" at the beginning or end of the line if it's too long. If show_newline + * is TRUE, display newlines when in whitespace display mode. */ +char *display_string(const char *buf, size_t column, size_t span, bool isdata, + bool show_newline) { size_t start_index = actual_x(buf, column); /* The index of the first character that the caller wishes to show. */ @@ -1873,7 +1875,7 @@ char *display_string(const char *buf, size_t column, size_t span, bool isdata) buf += start_index; /* Allocate enough space for converting the relevant part of the line. */ - converted = charalloc(strlen(buf) * (MAXCHARLEN + tabsize) + 1); + converted = charalloc(strlen(buf) * (MAXCHARLEN + tabsize) + (show_newline ? MAXCHARLEN + 1 : 1)); /* If the first character starts before the left edge, or would be * overwritten by a "$" token, then show placeholders instead. */ @@ -1988,6 +1990,16 @@ char *display_string(const char *buf, size_t column, size_t span, bool isdata) #endif } +#ifndef NANO_TINY + /* Show a newline as a visible character, or not. */ + if (*buf == '\0' && show_newline && ISSET(WHITESPACE_DISPLAY)) { + int i = whitespace_len[0] + whitespace_len[1]; + + while (i < whitespace_len[0] + whitespace_len[1] + whitespace_len[2]) + converted[index++] = whitespace[i++]; + } +#endif + /* Null-terminate the converted string. */ converted[index] = '\0'; @@ -2127,13 +2139,13 @@ void titlebar(const char *path) /* Print the full path if there's room; otherwise, dottify it. */ if (pathlen + pluglen + statelen <= COLS) { - caption = display_string(path, 0, pathlen, FALSE); + caption = display_string(path, 0, pathlen, FALSE, FALSE); waddstr(topwin, caption); free(caption); } else if (5 + statelen <= COLS) { waddstr(topwin, "..."); caption = display_string(path, 3 + pathlen - COLS + statelen, - COLS - statelen, FALSE); + COLS - statelen, FALSE, FALSE); waddstr(topwin, caption); free(caption); } @@ -2224,7 +2236,7 @@ void statusline(message_type importance, const char *msg, ...) compound = charalloc(MAXCHARLEN * (COLS + 1)); vsnprintf(compound, MAXCHARLEN * (COLS + 1), msg, ap); va_end(ap); - message = display_string(compound, 0, COLS, FALSE); + message = display_string(compound, 0, COLS, FALSE, FALSE); free(compound); start_col = (COLS - strlenpt(message)) / 2; @@ -2755,8 +2767,9 @@ int update_line(filestruct *fileptr, size_t index) from_col = get_page_start(strnlenpt(fileptr->data, index)); /* Expand the line, replacing tabs with spaces, and control - * characters with their displayed forms. */ - converted = display_string(fileptr->data, from_col, editwincols, TRUE); + * characters with their displayed forms. Display a newline + * on all but the last line. */ + converted = display_string(fileptr->data, from_col, editwincols, TRUE, fileptr != openfile->filebot); /* Draw the line. */ edit_draw(fileptr, converted, row, from_col); @@ -2819,8 +2832,9 @@ int update_softwrapped_line(filestruct *fileptr) blank_row(edit, row, 0, COLS); - /* Convert the chunk to its displayable form and draw it. */ - converted = display_string(fileptr->data, from_col, to_col - from_col, TRUE); + /* Convert the chunk to its displayable form and draw it. Display a + * newline on all but the last line. */ + converted = display_string(fileptr->data, from_col, to_col - from_col, TRUE, fileptr != openfile->filebot); edit_draw(fileptr, converted, row++, from_col); free(converted); @@ -3422,7 +3436,7 @@ void spotlight(size_t from_col, size_t to_col) to_col++; } else word = display_string(openfile->current->data, from_col, - to_col - from_col, FALSE); + to_col - from_col, FALSE, FALSE); word_span = strlenpt(word); @@ -3475,7 +3489,7 @@ void spotlight_softwrapped(size_t from_col, size_t to_col) break_col++; } else word = display_string(openfile->current->data, from_col, - break_col - from_col, FALSE); + break_col - from_col, FALSE, FALSE); wattron(edit, interface_color_pair[SELECTED_TEXT]); -- 2.19.1