From 9bd56204ee775164c97bbc574ccd67b184a86555 Mon Sep 17 00:00:00 2001
From: David Lawrence Ramsey <pooka109@gmail.com>
Date: Fri, 18 Mar 2005 21:29:33 +0000
Subject: [PATCH] add multibyte character support to help_line_len(), so that
 UTF-8 help text is wrapped properly

git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2393 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
---
 ChangeLog   |  20 +++----
 TODO        |   4 +-
 src/nano.c  | 154 ++++++++++++++++++++++++++++------------------------
 src/proto.h |   7 ++-
 src/winio.c |  41 ++++++--------
 5 files changed, 116 insertions(+), 110 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f088b1c5..14a14972 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -99,12 +99,12 @@ CVS code -
 	  control_mbrep(), control_wrep(), mbwidth(), mb_cur_max(),
 	  make_mbchar(), mbstrlen(), mbstrnlen(), mbstrcasecmp(),
 	  mbstrncasecmp(), mbstrcasestr(), and mbrevstrcasestr();
-	  changes to help_init(), is_byte() (moved to chars.c),
-	  is_blank_char() (moved to chars.c), is_cntrl_char() (moved to
-	  chars.c), nstricmp() (renamed nstrcasecmp() and moved to
-	  chars.c), nstrnicmp() (renamed nstrncasecmp() and moved to
-	  chars.c), nstristr() (renamed nstrcasestr() and moved to
-	  chars.c), revstrstr() (moved to chars.c), revstristr()
+	  changes to help_init(), break_line(), is_byte() (moved to
+	  chars.c), is_blank_char() (moved to chars.c), is_cntrl_char()
+	  (moved to chars.c), nstricmp() (renamed nstrcasecmp() and
+	  moved to chars.c), nstrnicmp() (renamed nstrncasecmp() and
+	  moved to chars.c), nstristr() (renamed nstrcasestr() and moved
+	  to chars.c), revstrstr() (moved to chars.c), revstristr()
 	  (renamed revstrcasestr() and moved to chars.c), nstrnlen()
 	  (moved to chars.c), parse_char() (renamed parse_mbchar() and
 	  moved to chars.c), move_left() (renamed move_mbleft() and
@@ -116,9 +116,9 @@ CVS code -
 	  unget_input(), unget_kbinput(), get_input(), parse_kbinput(),
 	  unparse_kbinput(), parse_verbatim_kbinput(),
 	  do_statusbar_input(), do_statusbar_home(),
-	  do_statusbar_verbatim_kbinput(), do_statusbar_output(), and
-	  display_string(); removal of buffer_to_keys() and
-	  keys_to_buffer(). (DLR)
+	  do_statusbar_verbatim_kbinput(), do_statusbar_output(),
+	  do_help(), help_line_len(), and display_string(); removal of
+	  buffer_to_keys() and keys_to_buffer(). (DLR)
 	- Add -O/--morespace command line option, plus a corresponding
 	  Meta-O toggle and a "morespace" rcfile option.  When these are
 	  used, the normally-unused blank line below the titlebar will
@@ -188,7 +188,7 @@ CVS code -
 	  make whitespace display mode work with multibyte characters,
 	  and add a few related documentation updates.  New function
 	  make_mbstring(); changes to make_mbchar(), make_mbstring(),
-	  do_help(), main(), parse_rcfile(), and display_string(). (DLR)
+	  main(), parse_rcfile(), display_string(), and do_help(). (DLR)
 - cut.c:
   do_cut_text()
 	- If keep_cutbuffer is FALSE, only blow away the text in the
diff --git a/TODO b/TODO
index 48698f44..0ea981fb 100644
--- a/TODO
+++ b/TODO
@@ -2,8 +2,8 @@ TODO file (? means the feature may be implemented, but not definitely)
 -----------------------------------------------------------------------------
 
 For version 1.4:
-- UTF-8 support. [DONE except for edit window text wrapping, help window
-  text wrapping, and the NO_CONVERT flag.]
+- UTF-8 support. [DONE except for edit window text wrapping and the
+  NO_CONVERT flag, which should allow editing UTF-8 as raw bytes.]
 - Support for paragraph searches. [DONE]
 - Support for justifying the entire file at once. [DONE]
 - Support for filename searches in the file browser.
diff --git a/src/nano.c b/src/nano.c
index a9ce1540..9d20772d 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -2323,6 +2323,86 @@ void do_spell(void)
 }
 #endif /* !DISABLE_SPELLER */
 
+#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY)
+/* We are trying to break a chunk off line.  We find the last blank such
+ * that the display length to there is at most goal + 1.  If there is no
+ * such blank, and force is TRUE, then we find the first blank.  Anyway,
+ * we then take the last blank in that group of blanks.  The terminating
+ * '\0' counts as a blank, as does a '\n' if newline is TRUE. */
+ssize_t break_line(const char *line, ssize_t goal, bool newline, bool
+	force)
+{
+    ssize_t blank_loc = -1;
+	/* Current tentative return value.  Index of the last blank we
+	 * found with short enough display width.  */
+    ssize_t cur_loc = 0;
+	/* Current index in line. */
+    int line_len;
+
+    assert(line != NULL);
+
+    while (*line != '\0' && goal >= 0) {
+	size_t pos = 0;
+
+	line_len = parse_mbchar(line, NULL, NULL, &pos);
+
+	if (is_blank_mbchar(line) || (newline && *line == '\n')) {
+	    blank_loc = cur_loc;
+
+	    if (newline && *line == '\n')
+		break;
+	}
+
+	goal -= pos;
+	line += line_len;
+	cur_loc += line_len;
+    }
+
+    if (goal >= 0)
+	/* In fact, the whole line displays shorter than goal. */
+	return cur_loc;
+
+    if (blank_loc == -1) {
+	/* No blank was found that was short enough. */
+	if (force) {
+	    bool found_blank = FALSE;
+
+	    while (*line != '\0') {
+		line_len = parse_mbchar(line, NULL, NULL, NULL);
+
+		if (is_blank_mbchar(line) ||
+			(newline && *line == '\n')) {
+		    if (!found_blank)
+			found_blank = TRUE;
+		} else if (found_blank)
+		    return cur_loc - line_len;
+
+		line += line_len;
+		cur_loc += line_len;
+	    }
+
+	    return -1;
+	}
+    }
+
+    /* Move to the last blank after blank_loc, if there is one. */
+    line -= cur_loc;
+    line += blank_loc;
+    line_len = parse_mbchar(line, NULL, NULL, NULL);
+    line += line_len;
+
+    while (*line != '\0' && (is_blank_mbchar(line) ||
+	(newline && *line == '\n'))) {
+	line_len = parse_mbchar(line, NULL, NULL, NULL);
+
+	line += line_len;
+	blank_loc += line_len;
+    }
+
+    return blank_loc;
+}
+#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY */
+
 #if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
 /* The "indentation" of a line is the whitespace between the quote part
  * and the non-whitespace of the line. */
@@ -2759,77 +2839,6 @@ filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
     return first_line;
 }
 
-/* We are trying to break a chunk off line.  We find the last blank such
- * that the display length to there is at most goal + 1.  If there is no
- * such blank, and force is TRUE, then we find the first blank.  Anyway,
- * we then take the last blank in that group of blanks.  The terminating
- * '\0' counts as a blank. */
-ssize_t break_line(const char *line, ssize_t goal, bool force)
-{
-    ssize_t blank_loc = -1;
-	/* Current tentative return value.  Index of the last blank we
-	 * found with short enough display width.  */
-    ssize_t cur_loc = 0;
-	/* Current index in line. */
-    int line_len;
-
-    assert(line != NULL);
-
-    while (*line != '\0' && goal >= 0) {
-	size_t pos = 0;
-
-	line_len = parse_mbchar(line, NULL, NULL, &pos);
-
-	if (is_blank_mbchar(line))
-	    blank_loc = cur_loc;
-
-	goal -= pos;
-	line += line_len;
-	cur_loc += line_len;
-    }
-
-    if (goal >= 0)
-	/* In fact, the whole line displays shorter than goal. */
-	return cur_loc;
-
-    if (blank_loc == -1) {
-	/* No blank was found that was short enough. */
-	if (force) {
-	    bool found_blank = FALSE;
-
-	    while (*line != '\0') {
-		line_len = parse_mbchar(line, NULL, NULL, NULL);
-
-		if (is_blank_mbchar(line)) {
-		    if (!found_blank)
-			found_blank = TRUE;
-		} else if (found_blank)
-		    return cur_loc - line_len;
-
-		line += line_len;
-		cur_loc += line_len;
-	    }
-
-	    return -1;
-	}
-    }
-
-    /* Move to the last blank after blank_loc, if there is one. */
-    line -= cur_loc;
-    line += blank_loc;
-    line_len = parse_mbchar(line, NULL, NULL, NULL);
-    line += line_len;
-
-    while (*line != '\0' && is_blank_mbchar(line)) {
-	line_len = parse_mbchar(line, NULL, NULL, NULL);
-
-	line += line_len;
-	blank_loc += line_len;
-    }
-
-    return blank_loc;
-}
-
 /* Find the beginning of the current paragraph if we're in one, or the
  * beginning of the next paragraph if we're not.  Afterwards, save the
  * quote length and paragraph length in *quote and *par.  Return TRUE if
@@ -3082,7 +3091,8 @@ void do_justify(bool full_justify)
 	    /* If this line is too long, try to wrap it to the next line
 	     * to make it short enough. */
 	    break_pos = break_line(current->data + indent_len,
-		fill - strnlenpt(current->data, indent_len), TRUE);
+		fill - strnlenpt(current->data, indent_len), FALSE,
+		TRUE);
 
 	    /* We can't break the line, or don't need to, so get out. */
 	    if (break_pos == -1 || break_pos + indent_len == line_len)
diff --git a/src/proto.h b/src/proto.h
index c4339783..f15e0b3f 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -413,6 +413,10 @@ const char *do_int_speller(const char *tempfile_name);
 const char *do_alt_speller(char *tempfile_name);
 void do_spell(void);
 #endif
+#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY)
+ssize_t break_line(const char *line, ssize_t goal, bool newline, bool
+	force);
+#endif
 #if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
 size_t indent_length(const char *line);
 #endif
@@ -431,7 +435,6 @@ void do_para_end(bool allow_update);
 void do_para_end_void(void);
 filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
 	quote_len);
-ssize_t break_line(const char *line, ssize_t goal, bool force);
 bool find_paragraph(size_t *const quote, size_t *const par);
 void do_justify(bool full_justify);
 void do_justify_void(void);
@@ -680,7 +683,7 @@ void display_main_list(void);
 void do_cursorpos(bool constant);
 void do_cursorpos_void(void);
 #ifndef DISABLE_HELP
-int help_line_len(const char *ptr);
+size_t help_line_len(const char *ptr);
 void do_help(void);
 #endif
 void do_replace_highlight(bool highlight_flag, const char *word);
diff --git a/src/winio.c b/src/winio.c
index f1cd5a26..8f475304 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -3826,31 +3826,26 @@ void do_cursorpos_void(void)
 
 #ifndef DISABLE_HELP
 /* Calculate the next line of help_text, starting at ptr. */
-int help_line_len(const char *ptr)
+size_t help_line_len(const char *ptr)
 {
-    int j = 0;
+    int help_cols = (COLS > 80) ? COLS - 8 : 72;
 
-    while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
-	ptr++;
-	j++;
-    }
-    if (j == COLS - 5) {
-	/* Don't wrap at the first of two spaces following a period. */
-	if (*ptr == ' ' && *(ptr + 1) == ' ')
-	    j++;
-	/* Don't print half a word if we've run out of space. */
-	while (*ptr != ' ' && j > 0) {
-	    ptr--;
-	    j--;
-	}
-	/* A word longer than (COLS - 5) chars just gets broken. */
-	if (j == 0)
-	    j = COLS - 5;
-    }
+    /* Try to break the line at (COLS - 8) columns if we have more than
+     * 80 columns, and at 72 columns otherwise. */
+    size_t retval = break_line(ptr, help_cols, TRUE, TRUE);
+    size_t retval_save = retval;
 
-    assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
+    /* Get the length of the entire line up to a null or a newline. */
+    while (*(ptr + retval) != '\0' && *(ptr + retval) != '\n')
+	retval += move_mbright(ptr + retval, 0);
 
-    return j;
+    /* If the entire line doesn't go more than 8 columns beyond where we
+     * tried to break it, we should display it as-is.  Otherwise, we
+     * should display it only up to the break. */
+    if (strnlenpt(ptr, retval) > help_cols + 8)
+	retval = retval_save;
+
+    return retval;
 }
 
 /* Our dynamic, shortcut-list-compliant help function. */
@@ -3944,8 +3939,6 @@ void do_help(void)
 	    blank_edit();
 	}
 
-	assert(COLS > 5);
-
 	/* Calculate where in the text we should be, based on the
 	 * page. */
 	for (i = 0; i < line; i++) {
@@ -3955,7 +3948,7 @@ void do_help(void)
 	}
 
 	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
-	    int j = help_line_len(ptr);
+	    size_t j = help_line_len(ptr);
 
 	    mvwaddnstr(edit, i, 0, ptr, j);
 	    ptr += j;
-- 
GitLab