diff --git a/src/text.c b/src/text.c index d4907c137dc40144b3cd2399928cff789c127e35..04b29a82b669e21cd5836a69a35a6878b144b3ac 100644 --- a/src/text.c +++ b/src/text.c @@ -363,12 +363,31 @@ void do_indent(void) refresh_needed = TRUE; } +/* If the given text starts with a tab's worth of whitespace, return the + * number of bytes this whitespace occupies. Otherwise, return zero. */ +size_t length_of_white(const char *text) +{ + size_t bytes_of_white = 1; + + while (TRUE) { + if (*text == '\t') + return bytes_of_white; + + if (*text != ' ') + return 0; + + if (bytes_of_white == tabsize) + return tabsize; + + bytes_of_white++; + text++; + } +} + /* Unindent the current line (or the marked lines) by tabsize columns. * The removed indent can be a mixture of spaces plus at most one tab. */ void do_unindent(void) { - bool indent_changed = FALSE; - /* Whether any unindenting was done. */ filestruct *top, *bot, *f; size_t top_x, bot_x; @@ -384,53 +403,48 @@ void do_unindent(void) bot = top; } + /* Go through the lines to check whether they a) are empty or blank + * or b) start with a tab's worth of whitespace. */ + for (f = top; f != bot->next; f = f->next) { + if (!white_string(f->data) && length_of_white(f->data) == 0) { + statusline(HUSH, _("Can unindent only by a full tab size")); + return; + } + } + /* Go through each line of the text. */ for (f = top; f != bot->next; f = f->next) { size_t line_len = strlen(f->data); - size_t indent_len = indent_length(f->data); - size_t indent_col = strnlenpt(f->data, indent_len); - /* The length in columns of the indentation on this line. */ - - if (tabsize <= indent_col) { - size_t indent_new = actual_x(f->data, indent_col - tabsize); - /* The length of the indentation remaining on - * this line after we unindent. */ - size_t indent_shift = indent_len - indent_new; - /* The change in the indentation on this line - * after we unindent. */ + size_t indent_len = length_of_white(f->data); + + /* If the line consists of a small amount of whitespace, skip it. */ + if (white_string(f->data) && indent_len < tabsize) + continue; /* If there's at least tabsize * columns' worth of indentation at the beginning of the * non-whitespace text of this line, remove it. */ - charmove(&f->data[indent_new], &f->data[indent_len], - line_len - indent_shift - indent_new + 1); - null_at(&f->data, line_len - indent_shift + 1); - openfile->totsize -= indent_shift; + charmove(f->data, &f->data[indent_len], line_len - indent_len + 1); + null_at(&f->data, line_len - indent_len + 1); + openfile->totsize -= indent_len; /* Keep track of the change in the current line. */ - if (openfile->mark_set && f == openfile->mark_begin && - openfile->mark_begin_x > indent_new) { + if (openfile->mark_set && f == openfile->mark_begin) { if (openfile->mark_begin_x <= indent_len) - openfile->mark_begin_x = indent_new; + openfile->mark_begin_x = 0; else - openfile->mark_begin_x -= indent_shift; + openfile->mark_begin_x -= indent_len; } - if (f == openfile->current && - openfile->current_x > indent_new) { + if (f == openfile->current) { if (openfile->current_x <= indent_len) - openfile->current_x = indent_new; + openfile->current_x = 0; else - openfile->current_x -= indent_shift; + openfile->current_x -= indent_len; openfile->placewewant = xplustabs(); } - - /* We've unindented, so the indentation changed. */ - indent_changed = TRUE; - } } - if (indent_changed) { /* Throw away the undo stack, to prevent making mistakes when * the user tries to undo something in the unindented text. */ discard_until(NULL, openfile); @@ -440,7 +454,6 @@ void do_unindent(void) /* Update the screen. */ refresh_needed = TRUE; - } } #endif /* !NANO_TINY */