Commit 40e211bc authored by David Lawrence Ramsey's avatar David Lawrence Ramsey
Browse files

add full multibyte character support to do_wrap()

git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2394 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
No related merge requests found
Showing with 186 additions and 190 deletions
+186 -190
...@@ -49,16 +49,13 @@ CVS code - ...@@ -49,16 +49,13 @@ CVS code -
based on ideas from mutt 1.4.2.1; input of Unicode characters based on ideas from mutt 1.4.2.1; input of Unicode characters
in hexadecimal suggested by Michael Piefel) in hexadecimal suggested by Michael Piefel)
- More steps toward wide character/multibyte character support. - More steps toward wide character/multibyte character support.
Movement and cursor display in the edit window should now New functions control_rep(), parse_char(), move_left(), and
(mostly) work properly with files containing multibyte move_right(); changes to do_left(), do_right(), do_delete(),
characters, and text display of such files should now (mostly) breakable(), break_line(), do_output(), get_buffer(),
work properly as well. New functions control_rep(), unget_input(), actual_x(), strnlenpt(), display_string(),
parse_char(), move_left(), and move_right(); changes to titlebar(), statusbar(), onekey(), edit_add(),
do_left(), do_right(), do_delete(), breakable(), break_line(), do_replace_highlight(), and do_credits(). (David Benbennick
do_output(), get_buffer(), unget_input(), actual_x(), and DLR)
strnlenpt(), display_string(), titlebar(), statusbar(),
onekey(), edit_add(), do_replace_highlight(), and
do_credits(). (David Benbennick and DLR)
- Overhaul the high-level input routines for the statusbar to - Overhaul the high-level input routines for the statusbar to
make them read the shortcut lists for functions instead of make them read the shortcut lists for functions instead of
manually running them, to make nanogetstr() less complex, and manually running them, to make nanogetstr() less complex, and
...@@ -99,22 +96,22 @@ CVS code - ...@@ -99,22 +96,22 @@ CVS code -
control_mbrep(), control_wrep(), mbwidth(), mb_cur_max(), control_mbrep(), control_wrep(), mbwidth(), mb_cur_max(),
make_mbchar(), mbstrlen(), mbstrnlen(), mbstrcasecmp(), make_mbchar(), mbstrlen(), mbstrnlen(), mbstrcasecmp(),
mbstrncasecmp(), mbstrcasestr(), and mbrevstrcasestr(); mbstrncasecmp(), mbstrcasestr(), and mbrevstrcasestr();
changes to help_init(), break_line(), is_byte() (moved to changes to help_init(), do_wrap(), break_line(), is_byte()
chars.c), is_blank_char() (moved to chars.c), is_cntrl_char() (moved to chars.c), is_blank_char() (moved to chars.c),
(moved to chars.c), nstricmp() (renamed nstrcasecmp() and is_cntrl_char() (moved to chars.c), nstricmp() (renamed
moved to chars.c), nstrnicmp() (renamed nstrncasecmp() and nstrcasecmp() and moved to chars.c), nstrnicmp() (renamed
moved to chars.c), nstristr() (renamed nstrcasestr() and moved nstrncasecmp() and moved to chars.c), nstristr() (renamed
to chars.c), revstrstr() (moved to chars.c), revstristr() nstrcasestr() and moved to chars.c), revstrstr() (moved to
(renamed revstrcasestr() and moved to chars.c), nstrnlen() chars.c), revstristr() (renamed revstrcasestr() and moved to
(moved to chars.c), parse_char() (renamed parse_mbchar() and chars.c), nstrnlen() (moved to chars.c), parse_char() (renamed
moved to chars.c), move_left() (renamed move_mbleft() and parse_mbchar() and moved to chars.c), move_left() (renamed
moved to chars.c), move_right() (renamed move_mbright() and move_mbleft() and moved to chars.c), move_right() (renamed
moved to chars.c), do_home(), do_verbatim_input(), move_mbright() and moved to chars.c), do_home(),
do_delete(), do_tab(), do_enter(), indent_length(), do_verbatim_input(), do_delete(), do_tab(), do_enter(),
do_next_word(), do_prev_word(), do_wrap(), do_input(), indent_length(), do_next_word(), do_prev_word(), do_wrap(),
do_output(), is_whole_word(), strstrwrapper(), get_buffer(), do_input(), do_output(), is_whole_word(), strstrwrapper(),
unget_input(), unget_kbinput(), get_input(), parse_kbinput(), get_buffer(), unget_input(), unget_kbinput(), get_input(),
unparse_kbinput(), parse_verbatim_kbinput(), parse_kbinput(), unparse_kbinput(), parse_verbatim_kbinput(),
do_statusbar_input(), do_statusbar_home(), do_statusbar_input(), do_statusbar_home(),
do_statusbar_verbatim_kbinput(), do_statusbar_output(), do_statusbar_verbatim_kbinput(), do_statusbar_output(),
do_help(), help_line_len(), and display_string(); removal of do_help(), help_line_len(), and display_string(); removal of
......
...@@ -2,8 +2,8 @@ TODO file (? means the feature may be implemented, but not definitely) ...@@ -2,8 +2,8 @@ TODO file (? means the feature may be implemented, but not definitely)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
For version 1.4: For version 1.4:
- UTF-8 support. [DONE except for edit window text wrapping and the - UTF-8 support. [DONE except for the NO_CONVERT flag, which should
NO_CONVERT flag, which should allow editing UTF-8 as raw bytes.] allow editing UTF-8 as raw bytes.]
- Support for paragraph searches. [DONE] - Support for paragraph searches. [DONE]
- Support for justifying the entire file at once. [DONE] - Support for justifying the entire file at once. [DONE]
- Support for filename searches in the file browser. - Support for filename searches in the file browser.
......
...@@ -1565,198 +1565,203 @@ void wrap_reset(void) ...@@ -1565,198 +1565,203 @@ void wrap_reset(void)
/* We wrap the given line. Precondition: we assume the cursor has been /* We wrap the given line. Precondition: we assume the cursor has been
* moved forward since the last typed character. Return value: whether * moved forward since the last typed character. Return value: whether
* we wrapped. */ * we wrapped. */
bool do_wrap(filestruct *inptr) bool do_wrap(filestruct *line)
{ {
size_t len = strlen(inptr->data); size_t line_len = strlen(line->data);
/* Length of the line we wrap. */ /* Length of the line we wrap. */
size_t i = 0;
/* Generic loop variable. */
ssize_t wrap_loc = -1; ssize_t wrap_loc = -1;
/* Index of inptr->data where we wrap. */ /* Index of line->data where we wrap. */
ssize_t word_back = -1;
#ifndef NANO_SMALL #ifndef NANO_SMALL
const char *indentation = NULL; const char *indent_string = NULL;
/* Indentation to prepend to the new line. */ /* Indentation to prepend to the new line. */
size_t indent_len = 0; /* strlen(indentation) */ size_t indent_len = 0; /* The length of indent_string. */
#endif #endif
const char *after_break; /* Text after the wrap point. */ const char *after_break; /* The text after the wrap point. */
size_t after_break_len; /* strlen(after_break) */ size_t after_break_len; /* The length of after_break. */
bool wrapping = FALSE; /* Do we prepend to the next line? */ bool wrapping = FALSE; /* Do we prepend to the next line? */
const char *wrap_line = NULL; const char *next_line = NULL;
/* The next line, minus indentation. */ /* The next line, minus indentation. */
size_t wrap_line_len = 0; /* strlen(wrap_line) */ size_t next_line_len = 0; /* The length of next_line. */
char *newline = NULL; /* The line we create. */ char *new_line = NULL; /* The line we create. */
size_t new_line_len = 0; /* Eventual length of newline. */ size_t new_line_len = 0; /* The eventual length of new_line. */
/* There are three steps. First, we decide where to wrap. Then, we /* There are three steps. First, we decide where to wrap. Then, we
* create the new wrap line. Finally, we clean up. */ * create the new wrap line. Finally, we clean up. */
/* Step 1, finding where to wrap. We are going to add a new line /* Step 1, finding where to wrap. We are going to add a new line
* after a whitespace character. In this step, we set wrap_loc as the * after a blank character. In this step, we call break_line() to
* location of this replacement. * get the location of the last blank we can break the line at, and
* * and set wrap_loc to the location of the character after it, so
* Where should we break the line? We need the last legal wrap point * that the blank is preserved at the end of the line.
* such that the last word before it ended at or before fill. If there
* is no such point, we settle for the first legal wrap point.
*
* A legal wrap point is a whitespace character that is not followed by
* whitespace.
* *
* If there is no legal wrap point or we found the last character of the * If there is no legal wrap point, or we reach the last character
* line, we should return without wrapping. * of the line while trying to find one, we should return without
* * wrapping. Note that if autoindent is turned on, we don't break
* Note that the initial indentation does not count as a legal wrap * at the end of it! */
* point if we are going to auto-indent!
*
* Note that the code below could be optimized, by not calling
* strnlenpt() so often. */
#ifndef NANO_SMALL /* Find the last blank where we can break the line. */
if (ISSET(AUTOINDENT)) wrap_loc = break_line(line->data, fill, FALSE);
i = indent_length(inptr->data);
#endif /* If we couldn't break the line, or we've reached the end of it, we
wrap_line = inptr->data + i; * don't wrap. */
for (; i < len; i++, wrap_line++) { if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
/* Record where the last word ended. */
if (!is_blank_char(*wrap_line))
word_back = i;
/* If we have found a legal wrap point and the current word
* extends too far, then we stop. */
if (wrap_loc != -1 &&
strnlenpt(inptr->data, word_back + 1) > fill)
break;
/* We record the latest legal wrap point. */
if (word_back != i && !is_blank_char(wrap_line[1]))
wrap_loc = i;
}
if (i == len)
return FALSE; return FALSE;
/* Step 2, making the new wrap line. It will consist of indentation /* Otherwise, move forward to the character just after the blank. */
* + after_break + " " + wrap_line (although indentation and wrap_loc += move_mbright(line->data + wrap_loc, 0);
* wrap_line are conditional on flags and #defines). */
/* after_break is the text that will be moved to the next line. */ /* If we've reached the end of the line, we don't wrap. */
after_break = inptr->data + wrap_loc + 1; if (line->data[wrap_loc] == '\0')
after_break_len = len - wrap_loc - 1; return FALSE;
assert(after_break_len == strlen(after_break)); #ifndef NANO_SMALL
/* If autoindent is turned on, and we're on the character just after
* the indentation, we don't wrap. */
if (ISSET(AUTOINDENT)) {
/* Get the indentation of this line. */
indent_string = line->data;
indent_len = indent_length(indent_string);
/* new_line_len will later be increased by the lengths of indentation if (wrap_loc == indent_len)
* and wrap_line. */ return FALSE;
new_line_len = after_break_len; }
#endif
/* We prepend the wrapped text to the next line, if the flag is set, /* Step 2, making the new wrap line. It will consist of indentation
* and there is a next line, and prepending would not make the line * followed by the text after the wrap point, optionally followed by
* too long. */ * a space (if the text after the wrap point doesn't end in a blank)
if (same_line_wrap && inptr->next) { * and the text of the next line, if they can fit without
wrap_line = inptr->next->data; * wrapping, the next line exists, and the same_line_wrap flag is
wrap_line_len = strlen(wrap_line); * set. */
/* after_break is the text that will be wrapped to the next line. */
after_break = line->data + wrap_loc;
after_break_len = line_len - wrap_loc;
assert(strlen(after_break) == after_break_len);
/* We prepend the wrapped text to the next line, if the
* same_line_wrap flag is set, there is a next line, and prepending
* would not make the line too long. */
if (same_line_wrap && line->next != NULL) {
const char *end = after_break + move_mbleft(after_break,
after_break_len);
/* If after_break doesn't end in a blank, make sure it ends in a
* space. */
if (!is_blank_mbchar(end)) {
line_len++;
line->data = charealloc(line->data, line_len + 1);
line->data[line_len - 1] = ' ';
line->data[line_len] = '\0';
after_break = line->data + wrap_loc;
after_break_len++;
totsize++;
}
next_line = line->next->data;
next_line_len = strlen(next_line);
/* +1 for the space between after_break and wrap_line. */ if ((after_break_len + next_line_len) <= fill) {
if ((new_line_len + 1 + wrap_line_len) <= fill) {
wrapping = TRUE; wrapping = TRUE;
new_line_len += 1 + wrap_line_len; new_line_len += next_line_len;
} }
} }
/* new_line_len is now the length of the text that will be wrapped
* to the next line, plus (if we're prepending to it) the length of
* the text of the next line. */
new_line_len += after_break_len;
#ifndef NANO_SMALL #ifndef NANO_SMALL
if (ISSET(AUTOINDENT)) { if (ISSET(AUTOINDENT)) {
/* Indentation comes from the next line if wrapping, else from if (wrapping) {
* this line. */ /* If we're wrapping, the indentation will come from the
indentation = wrapping ? wrap_line : inptr->data; * next line. */
indent_len = indent_length(indentation); indent_string = next_line;
if (wrapping) indent_len = indent_length(indent_string);
/* The wrap_line text should not duplicate indentation. next_line += indent_len;
* Note in this case we need not increase new_line_len. */ } else {
wrap_line += indent_len; /* Otherwise, it will come from this line, in which case
else * we should increase new_line_len to make room for it. */
new_line_len += indent_len; new_line_len += indent_len;
totsize += mbstrnlen(indent_string, indent_len);
}
} }
#endif #endif
/* Now we allocate the new line and copy into it. */ /* Now we allocate the new line and copy the text into it. */
newline = charalloc(new_line_len + 1); /* +1 for \0 */ new_line = charalloc(new_line_len + 1);
new_line_len = 0; new_line[0] = '\0';
*newline = '\0';
#ifndef NANO_SMALL #ifndef NANO_SMALL
if (ISSET(AUTOINDENT)) { if (ISSET(AUTOINDENT)) {
strncpy(newline, indentation, indent_len); /* Copy the indentation. */
newline[indent_len] = '\0'; charcpy(new_line, indent_string, indent_len);
totsize += mbstrlen(newline); new_line[indent_len] = '\0';
new_line_len = indent_len; new_line_len += indent_len;
} }
#endif #endif
strcat(newline, after_break);
new_line_len += after_break_len;
/* We end the old line after wrap_loc. Note that this does not eat /* Copy all the text after the wrap point of the current line. */
* the space. */ strcat(new_line, after_break);
null_at(&inptr->data, wrap_loc + 1);
totsize++; /* Break the current line at the wrap point. */
null_at(&line->data, wrap_loc);
if (wrapping) { if (wrapping) {
/* In this case, totsize increases by 1 since we add a space /* If we're wrapping, copy the text from the next line, minus
* between after_break and wrap_line. If the line already ends * the indentation that we already copied above. */
* in a tab or a space, we don't add a space and decrement strcat(new_line, next_line);
* totsize to account for that. */
if (!is_blank_char(newline[new_line_len - 1])) free(line->next->data);
strcat(newline, " "); line->next->data = new_line;
else
totsize--;
strcat(newline, wrap_line);
free(inptr->next->data);
inptr->next->data = newline;
} else { } else {
filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct)); /* Otherwise, make a new line and copy the text after where we
* broke this line to the beginning of the new line. */
splice_node(current, make_new_node(current), current->next);
current->next->data = new_line;
totlines++; totlines++;
temp->data = newline; totsize++;
temp->prev = inptr;
temp->next = inptr->next;
temp->prev->next = temp;
/* If temp->next is NULL, then temp is the last line of the
* file, so we must set filebot. */
if (temp->next != NULL)
temp->next->prev = temp;
else
filebot = temp;
} }
/* Step 3, clean up. Here we reposition the cursor and mark, and do /* Step 3, clean up. Reposition the cursor and mark, and do some
* some other sundry things. */ * other sundry things. */
/* Later wraps of this line will be prepended to the next line. */ /* Set the same_line_wrap flag, so that later wraps of this line
* will be prepended to the next line. */
same_line_wrap = TRUE; same_line_wrap = TRUE;
/* Each line knows its line number. We recalculate these if we /* Each line knows its line number. We recalculate these if we
* inserted a new line. */ * inserted a new line. */
if (!wrapping) if (!wrapping)
renumber(inptr); renumber(line);
/* If the cursor was after the break point, we must move it. */ /* If the cursor was after the break point, we must move it. We
* also clear the same_line_wrap flag in this case. */
if (current_x > wrap_loc) { if (current_x > wrap_loc) {
same_line_wrap = FALSE;
current = current->next; current = current->next;
current_x -= current_x -=
#ifndef NANO_SMALL #ifndef NANO_SMALL
-indent_len + -indent_len +
#endif #endif
wrap_loc + 1; wrap_loc;
wrap_reset();
placewewant = xplustabs(); placewewant = xplustabs();
} }
#ifndef NANO_SMALL #ifndef NANO_SMALL
/* If the mark was on this line after the wrap point, we move it /* If the mark was on this line after the wrap point, we move it
* down. If it was on the next line and we wrapped, we move it * down. If it was on the next line and we wrapped onto that line,
* right. */ * we move it right. */
if (mark_beginbuf == inptr && mark_beginx > wrap_loc) { if (mark_beginbuf == line && mark_beginx > wrap_loc) {
mark_beginbuf = inptr->next; mark_beginbuf = line->next;
mark_beginx -= wrap_loc - indent_len + 1; mark_beginx -= wrap_loc - indent_len + 1;
} else if (wrapping && mark_beginbuf == inptr->next) } else if (wrapping && mark_beginbuf == line->next)
mark_beginx += after_break_len; mark_beginx += after_break_len;
#endif /* !NANO_SMALL */ #endif
return TRUE; return TRUE;
} }
...@@ -2323,14 +2328,13 @@ void do_spell(void) ...@@ -2323,14 +2328,13 @@ void do_spell(void)
} }
#endif /* !DISABLE_SPELLER */ #endif /* !DISABLE_SPELLER */
#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) #if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) || !defined(DISABLE_WRAPPING)
/* We are trying to break a chunk off line. We find the last blank such /* 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 * 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, * such blank, then we find the first blank. We then take the last
* we then take the last blank in that group of blanks. The terminating * blank in that group of blanks. The terminating '\0' counts as a
* '\0' counts as a blank, as does a '\n' if newline is TRUE. */ * blank, as does a '\n' if newline is TRUE. */
ssize_t break_line(const char *line, ssize_t goal, bool newline, bool ssize_t break_line(const char *line, ssize_t goal, bool newline)
force)
{ {
ssize_t blank_loc = -1; ssize_t blank_loc = -1;
/* Current tentative return value. Index of the last blank we /* Current tentative return value. Index of the last blank we
...@@ -2364,14 +2368,12 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool ...@@ -2364,14 +2368,12 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool
if (blank_loc == -1) { if (blank_loc == -1) {
/* No blank was found that was short enough. */ /* No blank was found that was short enough. */
if (force) {
bool found_blank = FALSE; bool found_blank = FALSE;
while (*line != '\0') { while (*line != '\0') {
line_len = parse_mbchar(line, NULL, NULL, NULL); line_len = parse_mbchar(line, NULL, NULL, NULL);
if (is_blank_mbchar(line) || if (is_blank_mbchar(line) || (newline && *line == '\n')) {
(newline && *line == '\n')) {
if (!found_blank) if (!found_blank)
found_blank = TRUE; found_blank = TRUE;
} else if (found_blank) } else if (found_blank)
...@@ -2383,7 +2385,6 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool ...@@ -2383,7 +2385,6 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool
return -1; return -1;
} }
}
/* Move to the last blank after blank_loc, if there is one. */ /* Move to the last blank after blank_loc, if there is one. */
line -= cur_loc; line -= cur_loc;
...@@ -2401,7 +2402,7 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool ...@@ -2401,7 +2402,7 @@ ssize_t break_line(const char *line, ssize_t goal, bool newline, bool
return blank_loc; return blank_loc;
} }
#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY */ #endif /* !DISABLE_HELP || !DISABLE_JUSTIFY || !DISABLE_WRAPPING */
#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY) #if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
/* The "indentation" of a line is the whitespace between the quote part /* The "indentation" of a line is the whitespace between the quote part
...@@ -3091,8 +3092,7 @@ void do_justify(bool full_justify) ...@@ -3091,8 +3092,7 @@ void do_justify(bool full_justify)
/* If this line is too long, try to wrap it to the next line /* If this line is too long, try to wrap it to the next line
* to make it short enough. */ * to make it short enough. */
break_pos = break_line(current->data + indent_len, break_pos = break_line(current->data + indent_len,
fill - strnlenpt(current->data, indent_len), FALSE, fill - strnlenpt(current->data, indent_len), FALSE);
TRUE);
/* We can't break the line, or don't need to, so get out. */ /* We can't break the line, or don't need to, so get out. */
if (break_pos == -1 || break_pos + indent_len == line_len) if (break_pos == -1 || break_pos + indent_len == line_len)
......
...@@ -405,7 +405,7 @@ void do_mark(void); ...@@ -405,7 +405,7 @@ void do_mark(void);
#endif #endif
#ifndef DISABLE_WRAPPING #ifndef DISABLE_WRAPPING
void wrap_reset(void); void wrap_reset(void);
bool do_wrap(filestruct *inptr); bool do_wrap(filestruct *line);
#endif #endif
#ifndef DISABLE_SPELLER #ifndef DISABLE_SPELLER
bool do_int_spell_fix(const char *word); bool do_int_spell_fix(const char *word);
...@@ -413,9 +413,8 @@ const char *do_int_speller(const char *tempfile_name); ...@@ -413,9 +413,8 @@ const char *do_int_speller(const char *tempfile_name);
const char *do_alt_speller(char *tempfile_name); const char *do_alt_speller(char *tempfile_name);
void do_spell(void); void do_spell(void);
#endif #endif
#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) #if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) || !defined(DISABLE_WRAPPING)
ssize_t break_line(const char *line, ssize_t goal, bool newline, bool ssize_t break_line(const char *line, ssize_t goal, bool newline);
force);
#endif #endif
#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY) #if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
size_t indent_length(const char *line); size_t indent_length(const char *line);
......
...@@ -3832,7 +3832,7 @@ size_t help_line_len(const char *ptr) ...@@ -3832,7 +3832,7 @@ size_t help_line_len(const char *ptr)
/* Try to break the line at (COLS - 8) columns if we have more than /* Try to break the line at (COLS - 8) columns if we have more than
* 80 columns, and at 72 columns otherwise. */ * 80 columns, and at 72 columns otherwise. */
size_t retval = break_line(ptr, help_cols, TRUE, TRUE); size_t retval = break_line(ptr, help_cols, TRUE);
size_t retval_save = retval; size_t retval_save = retval;
/* Get the length of the entire line up to a null or a newline. */ /* Get the length of the entire line up to a null or a newline. */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment