moving: skip combining characters and other zero-width characters

This makes the cursor move smoothly left and right -- instead of
"stuttering" when passing over a zero-width character.

Pressing <Delete> on a normal (spacing) character also deletes
any zero-width characters after it.  But pressing <Backspace>
while the cursor is placed after a zero-width character, just
deletes that zero-width character.  The latter behavior allows
deleting and retyping just the combining diacritic of a character
instead of the whole character.

This addresses https://savannah.gnu.org/bugs/?50773.
Requested-by: Mike Frysinger <vapier@gentoo.org>
master
Benno Schulenberg 2020-11-10 18:27:52 +01:00
parent b7539ea985
commit 687efd210c
4 changed files with 47 additions and 1 deletions

View File

@ -197,6 +197,12 @@ int mbwidth(const char *c)
return 1;
}
/* Return TRUE when the given character occupies zero cells. */
bool is_zerowidth(const char *ch)
{
return (use_utf8 && mbwidth(ch) == 0);
}
/* Convert the given Unicode value to a multibyte character, if possible.
* If the conversion succeeds, return the (dynamically allocated) multibyte
* character and its length. Otherwise, return a length of zero. */

View File

@ -121,7 +121,14 @@ void do_delete(void)
zap_text();
else
#endif
{
do_deletion(DEL);
#ifdef ENABLE_UTF8
while (openfile->current->data[openfile->current_x] != '\0' &&
is_zerowidth(openfile->current->data + openfile->current_x))
do_deletion(DEL);
#endif
}
}
/* Backspace over one character. That is, move the cursor left one
@ -133,7 +140,10 @@ void do_backspace(void)
zap_text();
else
#endif
if (openfile->current_x > 0 || openfile->current != openfile->filetop) {
if (openfile->current_x > 0) {
openfile->current_x = step_left(openfile->current->data, openfile->current_x);
do_deletion(BACK);
} else if (openfile->current != openfile->filetop) {
do_left();
do_deletion(BACK);
}

View File

@ -291,6 +291,10 @@ void do_prev_word(bool allow_punct)
/* If at the head of a line now, this surely is a word start. */
if (openfile->current_x == 0)
break;
#ifdef ENABLE_UTF8
} else if (is_zerowidth(openfile->current->data + openfile->current_x)) {
; /* skip */
#endif
} else if (seen_a_word) {
/* This is space now: we've overshot the start of the word. */
step_forward = TRUE;
@ -339,11 +343,20 @@ bool do_next_word(bool after_ends, bool allow_punct)
if (is_word_char(openfile->current->data + openfile->current_x,
allow_punct))
seen_word = TRUE;
#ifdef ENABLE_UTF8
else if (is_zerowidth(openfile->current->data + openfile->current_x))
; /* skip */
#endif
else if (seen_word)
break;
} else
#endif
{
#ifdef ENABLE_UTF8
if (is_zerowidth(openfile->current->data + openfile->current_x))
; /* skip */
else
#endif
/* If this is not a word character, then it's a separator; else
* if we've already seen a separator, then it's a word start. */
if (!is_word_char(openfile->current->data + openfile->current_x,
@ -582,8 +595,16 @@ void do_left(void)
linestruct *was_current = openfile->current;
if (openfile->current_x > 0)
{
openfile->current_x = step_left(openfile->current->data,
openfile->current_x);
#ifdef ENABLE_UTF8
while (openfile->current_x > 0 &&
is_zerowidth(openfile->current->data + openfile->current_x))
openfile->current_x = step_left(openfile->current->data,
openfile->current_x);
#endif
}
else if (openfile->current != openfile->filetop) {
openfile->current = openfile->current->prev;
openfile->current_x = strlen(openfile->current->data);
@ -598,8 +619,16 @@ void do_right(void)
linestruct *was_current = openfile->current;
if (openfile->current->data[openfile->current_x] != '\0')
{
openfile->current_x = step_right(openfile->current->data,
openfile->current_x);
#ifdef ENABLE_UTF8
while (openfile->current->data[openfile->current_x] != '\0' &&
is_zerowidth(openfile->current->data + openfile->current_x))
openfile->current_x = step_right(openfile->current->data,
openfile->current_x);
#endif
}
else if (openfile->current != openfile->filebot) {
openfile->current = openfile->current->next;
openfile->current_x = 0;

View File

@ -204,6 +204,7 @@ bool is_word_char(const char *c, bool allow_punct);
char control_mbrep(const char *c, bool isdata);
#ifdef ENABLE_UTF8
int mbwidth(const char *c);
bool is_zerowidth(const char *ch);
char *make_mbchar(long code, int *length);
#endif
int char_length(const char *pointer);