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
parent
749b1b3d79
commit
0f4716e168
73
src/text.c
73
src/text.c
|
@ -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 */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue