From 2367f3d8e66b6f794850cc85b6bb85c6d789b286 Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Mon, 10 Jul 2017 15:48:50 -0500 Subject: [PATCH] text: make do_unindent() an exact copy of do_indent() This is the first step toward splitting them into two dedicated functions, instead of letting do_indent() handle both cases. --- src/global.c | 6 +- src/proto.h | 3 +- src/text.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 165 insertions(+), 8 deletions(-) diff --git a/src/global.c b/src/global.c index 6ae36816..3e1b9ac1 100644 --- a/src/global.c +++ b/src/global.c @@ -931,7 +931,7 @@ void shortcut_init(void) #ifndef NANO_TINY add_to_funcs(do_indent_void, MMAIN, N_("Indent Text"), IFSCHELP(nano_indent_msg), TOGETHER, NOVIEW); - add_to_funcs(do_unindent, MMAIN, + add_to_funcs(do_unindent_void, MMAIN, N_("Unindent Text"), IFSCHELP(nano_unindent_msg), BLANKAFTER, NOVIEW); #endif #ifdef ENABLE_WORDCOMPLETION @@ -1106,7 +1106,7 @@ void shortcut_init(void) add_to_sclist(MMAIN, "M-6", 0, do_copy_text, 0); add_to_sclist(MMAIN, "M-^", 0, do_copy_text, 0); add_to_sclist(MMAIN, "M-}", 0, do_indent_void, 0); - add_to_sclist(MMAIN, "M-{", 0, do_unindent, 0); + add_to_sclist(MMAIN, "M-{", 0, do_unindent_void, 0); add_to_sclist(MMAIN, "M-U", 0, do_undo, 0); add_to_sclist(MMAIN, "M-E", 0, do_redo, 0); #endif @@ -1490,7 +1490,7 @@ sc *strtosc(const char *input) else if (!strcasecmp(input, "indent")) s->scfunc = do_indent_void; else if (!strcasecmp(input, "unindent")) - s->scfunc = do_unindent; + s->scfunc = do_unindent_void; else if (!strcasecmp(input, "scrollup")) s->scfunc = do_scroll_up; else if (!strcasecmp(input, "scrolldown")) diff --git a/src/proto.h b/src/proto.h index a444f7ab..e708f33f 100644 --- a/src/proto.h +++ b/src/proto.h @@ -525,7 +525,8 @@ void do_tab(void); #ifndef NANO_TINY void do_indent(ssize_t cols); void do_indent_void(void); -void do_unindent(void); +void do_unindent(ssize_t cols); +void do_unindent_void(void); #endif bool white_string(const char *s); #ifdef ENABLE_COMMENT diff --git a/src/text.c b/src/text.c index 1be1a4e2..d267f62e 100644 --- a/src/text.c +++ b/src/text.c @@ -440,11 +440,167 @@ void do_indent_void(void) do_indent(tabsize); } -/* Unindent the current line, or all lines covered by the mark if the - * mark is on, tabsize columns. */ -void do_unindent(void) +/* Indent or unindent the current line (or, if the mark is on, all lines + * covered by the mark) len columns, depending on whether len is + * positive or negative. If the TABS_TO_SPACES flag is set, indent or + * unindent by len spaces. Otherwise, indent or unindent by (len / + * tabsize) tabs and (len % tabsize) spaces. */ +void do_unindent(ssize_t cols) { - do_indent(-tabsize); + bool indent_changed = FALSE; + /* Whether any indenting or unindenting was done. */ + bool unindent = FALSE; + /* Whether we're unindenting text. */ + char *line_indent = NULL; + /* The text added to each line in order to indent it. */ + size_t line_indent_len = 0; + /* The length of the text added to each line in order to indent + * it. */ + filestruct *top, *bot, *f; + size_t top_x, bot_x; + + assert(openfile->current != NULL && openfile->current->data != NULL); + + /* If cols is zero, get out. */ + if (cols == 0) + return; + + /* If cols is negative, make it positive and set unindent to TRUE. */ + if (cols < 0) { + cols = -cols; + unindent = TRUE; + /* Otherwise, we're indenting, in which case the file will always be + * modified, so set indent_changed to TRUE. */ + } else + indent_changed = TRUE; + + /* If the mark is on, use all lines covered by the mark. */ + if (openfile->mark_set) + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x, NULL); + /* Otherwise, use the current line. */ + else { + top = openfile->current; + bot = top; + } + + if (!unindent) { + /* Set up the text we'll be using as indentation. */ + line_indent = charalloc(cols + 1); + + if (ISSET(TABS_TO_SPACES)) { + /* Set the indentation to cols spaces. */ + charset(line_indent, ' ', cols); + line_indent_len = cols; + } else { + /* Set the indentation to (cols / tabsize) tabs and (cols % + * tabsize) spaces. */ + size_t num_tabs = cols / tabsize; + size_t num_spaces = cols % tabsize; + + charset(line_indent, '\t', num_tabs); + charset(line_indent + num_tabs, ' ', num_spaces); + + line_indent_len = num_tabs + num_spaces; + } + + line_indent[line_indent_len] = '\0'; + } + + /* 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); + + if (!unindent) { + /* If we're indenting, add the characters in line_indent to + * the beginning of the non-whitespace text of this line. */ + f->data = charealloc(f->data, line_len + line_indent_len + 1); + charmove(&f->data[indent_len + line_indent_len], + &f->data[indent_len], line_len - indent_len + 1); + strncpy(f->data + indent_len, line_indent, line_indent_len); + openfile->totsize += line_indent_len; + + /* Keep track of the change in the current line. */ + if (openfile->mark_set && f == openfile->mark_begin && + openfile->mark_begin_x >= indent_len) + openfile->mark_begin_x += line_indent_len; + + if (f == openfile->current && openfile->current_x >= indent_len) { + openfile->current_x += line_indent_len; + openfile->placewewant = xplustabs(); + } + + /* If the NO_NEWLINES flag isn't set, and this is the + * magicline, add a new magicline. */ + if (!ISSET(NO_NEWLINES) && f == openfile->filebot) + new_magicline(); + } else { + size_t indent_col = strnlenpt(f->data, indent_len); + /* The length in columns of the indentation on this line. */ + + if (cols <= indent_col) { + size_t indent_new = actual_x(f->data, indent_col - cols); + /* 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. */ + + /* If we're unindenting, and there's at least cols + * 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; + + /* 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_begin_x <= indent_len) + openfile->mark_begin_x = indent_new; + else + openfile->mark_begin_x -= indent_shift; + } + + if (f == openfile->current && + openfile->current_x > indent_new) { + if (openfile->current_x <= indent_len) + openfile->current_x = indent_new; + else + openfile->current_x -= indent_shift; + openfile->placewewant = xplustabs(); + } + + /* We've unindented, so the indentation changed. */ + indent_changed = TRUE; + } + } + } + + if (!unindent) + /* Clean up. */ + free(line_indent); + + if (indent_changed) { + /* Throw away the undo stack, to prevent making mistakes when + * the user tries to undo something in the reindented text. */ + discard_until(NULL, openfile); + + /* Mark the file as modified. */ + set_modified(); + + /* Update the screen. */ + refresh_needed = TRUE; + } +} + +/* Unindent the current line, or all lines covered by the mark if the mark + * is on, tabsize columns. */ +void do_unindent_void(void) +{ + do_unindent(-tabsize); } #endif /* !NANO_TINY */