text: make unindenting remove from the beginning of the line

Also, only unindent when all selected lines can be unindented,
which means that the relative indentation will be preserved.
For this purpose, it ignores empty lines and lines consisting
of only whitespace.

When unindenting is not possible, a message is shown.

This fixes https://savannah.gnu.org/bugs/?47684.
master
David Lawrence Ramsey 2017-07-07 13:53:00 -05:00 committed by Benno Schulenberg
parent 749b1b3d79
commit 0f4716e168
1 changed files with 43 additions and 30 deletions

View File

@ -363,12 +363,31 @@ void do_indent(void)
refresh_needed = TRUE; 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. /* 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. */ * The removed indent can be a mixture of spaces plus at most one tab. */
void do_unindent(void) void do_unindent(void)
{ {
bool indent_changed = FALSE;
/* Whether any unindenting was done. */
filestruct *top, *bot, *f; filestruct *top, *bot, *f;
size_t top_x, bot_x; size_t top_x, bot_x;
@ -384,53 +403,48 @@ void do_unindent(void)
bot = top; 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. */ /* Go through each line of the text. */
for (f = top; f != bot->next; f = f->next) { for (f = top; f != bot->next; f = f->next) {
size_t line_len = strlen(f->data); size_t line_len = strlen(f->data);
size_t indent_len = indent_length(f->data); size_t indent_len = length_of_white(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) { /* If the line consists of a small amount of whitespace, skip it. */
size_t indent_new = actual_x(f->data, indent_col - tabsize); if (white_string(f->data) && indent_len < tabsize)
/* The length of the indentation remaining on continue;
* this line after we unindent. */
size_t indent_shift = indent_len - indent_new;
/* The change in the indentation on this line
* after we unindent. */
/* If there's at least tabsize /* If there's at least tabsize
* columns' worth of indentation at the beginning of the * columns' worth of indentation at the beginning of the
* non-whitespace text of this line, remove it. */ * non-whitespace text of this line, remove it. */
charmove(&f->data[indent_new], &f->data[indent_len], charmove(f->data, &f->data[indent_len], line_len - indent_len + 1);
line_len - indent_shift - indent_new + 1); null_at(&f->data, line_len - indent_len + 1);
null_at(&f->data, line_len - indent_shift + 1); openfile->totsize -= indent_len;
openfile->totsize -= indent_shift;
/* Keep track of the change in the current line. */ /* Keep track of the change in the current line. */
if (openfile->mark_set && f == openfile->mark_begin && if (openfile->mark_set && f == openfile->mark_begin) {
openfile->mark_begin_x > indent_new) {
if (openfile->mark_begin_x <= indent_len) if (openfile->mark_begin_x <= indent_len)
openfile->mark_begin_x = indent_new; openfile->mark_begin_x = 0;
else else
openfile->mark_begin_x -= indent_shift; openfile->mark_begin_x -= indent_len;
} }
if (f == openfile->current && if (f == openfile->current) {
openfile->current_x > indent_new) {
if (openfile->current_x <= indent_len) if (openfile->current_x <= indent_len)
openfile->current_x = indent_new; openfile->current_x = 0;
else else
openfile->current_x -= indent_shift; openfile->current_x -= indent_len;
openfile->placewewant = xplustabs(); 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 /* Throw away the undo stack, to prevent making mistakes when
* the user tries to undo something in the unindented text. */ * the user tries to undo something in the unindented text. */
discard_until(NULL, openfile); discard_until(NULL, openfile);
@ -440,7 +454,6 @@ void do_unindent(void)
/* Update the screen. */ /* Update the screen. */
refresh_needed = TRUE; refresh_needed = TRUE;
}
} }
#endif /* !NANO_TINY */ #endif /* !NANO_TINY */