diff --git a/src/folding.c b/src/folding.c index 61ba7569..f63560be 100644 --- a/src/folding.c +++ b/src/folding.c @@ -21,6 +21,9 @@ #include "prototypes.h" #include + /* strlen */ +#include + /* isspace */ #ifdef ENABLE_FOLDING @@ -80,18 +83,10 @@ bool do_unfold_segments(linestruct *top, linestruct *bot) if (top != bot) return FALSE; - linestruct *match_line = openfile->current; - size_t match_x = openfile->current_x; - - if (!top->folded && !find_matching_bracket_pos(&match_line, &match_x)){ - if (openfile->current->lineno > match_line->lineno) { - top = match_line->next; - bot = openfile->current->prev; - } else { - top = openfile->current->next; - bot = match_line->prev; - } - } + /* Attempt to find a bracketed region first. + * If not, top is unmodified. */ + if (!top->folded) + find_bracketed_region(openfile->current, &top, &bot); if (top->folded) { unfold_folded_segment(top); @@ -112,35 +107,26 @@ void do_fold_segment(void) if (do_unfold_segments(top, bot)) { return; } - if (top == bot) { - linestruct *match_line = openfile->current; - size_t match_x = openfile->current_x; - if (!find_matching_bracket_pos(&match_line, &match_x)){ - int linedist = openfile->current->lineno - match_line->lineno; - linedist = linedist < 0 ? -linedist : linedist; - - if (linedist >= 2) { - if (openfile->current->lineno > match_line->lineno) { - top = match_line->next; - bot = openfile->current->prev; - } else { - top = openfile->current->next; - bot = match_line->prev; - } - } + /* When not selecting multiple lines, try to find bounding + * brackets to act as top and bot. */ + if (top == bot) + if (!find_bracketed_region(openfile->current, &top, &bot)) { + statusline(AHEM, _("No valid region found for automatic fold")); + return; } - } + for (linestruct* line = top;line != bot->next;line = line->next) line->folded = TRUE; if (top->lineno < openfile->edittop->lineno && bot->lineno > openfile->edittop->lineno) - openfile->edittop = top->next; + openfile->edittop = top; /* Place the cursor at the start of the fold segment. * Anywhere else within the segment is invalid. */ - openfile->current = get_start_of_folded_segment(openfile->current); + openfile->current = get_start_of_folded_segment(openfile->current); + openfile->mark = NULL; refresh_needed = TRUE; } diff --git a/src/prototypes.h b/src/prototypes.h index c3fcf1ce..77361711 100644 --- a/src/prototypes.h +++ b/src/prototypes.h @@ -472,6 +472,8 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer, void do_gotolinecolumn(void); #ifndef NANO_TINY int find_matching_bracket_pos(linestruct **line, size_t *xpos); +bool find_bracketed_region(linestruct *in_region, + linestruct **top, linestruct **bot); void do_find_bracket(void); void put_or_lift_anchor(void); void to_prev_anchor(void); diff --git a/src/search.c b/src/search.c index 0fe3e339..fc01dfb5 100644 --- a/src/search.c +++ b/src/search.c @@ -880,6 +880,92 @@ void do_gotolinecolumn(void) } #ifndef NANO_TINY +/* Search backwards for a line ending with a bracket, including the current line. + * top is an in/out parameter. The passed value is the start line for searching. + * xpos is an out parameter. It is the position in the line where the bracket is + * found. bracket_half is the halfway mark in the matchbrackets array. This way in + * a loop the halfway value isn't recomputed. */ +bool find_start_of_bracketed_region(linestruct **top, size_t *xpos, size_t bracket_half) +{ + /* Search backwards for a line ending with a bracket, including the current line */ + for (;(*top) != NULL;*top = (*top)->prev) { + char *found; + const char *bracket; + int len = strlen((*top)->data); + + /* Find the last bracket on the search line */ + found = mbrevstrpbrk((*top)->data, matchbrackets, (*top)->data + len); + if (found == NULL) + continue; + + *xpos = found - (*top)->data; + bracket = mbstrchr(matchbrackets, found); + + /* We're looking for opening braces, not closing */ + if (bracket >= matchbrackets + bracket_half) + continue; + + /* Check if this is the last character in the line */ + char *next = (*top)->data + step_right((*top)->data, *xpos); + if (*next == '\0') + return TRUE; + } + *xpos = 0; + return FALSE; +} + +/* Get the halfway point in matchbrackets, where open and close + * brackets meet. */ +size_t get_matchbrackets_halfway(void) +{ + size_t charcount = mbstrlen(matchbrackets) / 2; + size_t halfway = 0; + + for (size_t i = 0;i < charcount;++i) + halfway += char_length(matchbrackets + halfway); + return halfway; +} +/* Get a region surrounding the line in_region. A region here is defined + * as a block of text starting with an opening bracket at the end of a line, + * and ending with a matching closing bracket. + * top and bot are out parameters. Returns the top/bottom line of the region. + * top_pos and bot_pos are out parameters. Returns the position within the + * top and bottom lines where the brackets are found. */ +bool find_bracketed_region(linestruct *in_region, + linestruct **top, linestruct **bot) +{ + size_t bracket_half = get_matchbrackets_halfway(); + size_t top_pos; + size_t bot_pos; + linestruct *was_top = *top; + linestruct *was_bot = *bot; + *top = in_region; + + for (;find_start_of_bracketed_region(top, &top_pos, bracket_half) + ;*top = (*top)->prev) + { + *bot = *top; + bot_pos = top_pos; + + if (!find_matching_bracket_pos(bot, &bot_pos)) { + if ((*bot)->lineno < in_region->lineno) + continue; + + int linedist = (*bot)->lineno - (*top)->lineno; + + if (linedist >= 2) { + *top = (*top)->next; + *bot = (*bot)->prev; + return TRUE; + } + } + } + *top = was_top; + *bot = was_bot; + return FALSE; +} + + /* Search, starting from the current position, for any of the two characters * in bracket_pair. If reverse is TRUE, search backwards, otherwise forwards. * Return TRUE when one of the brackets was found, and FALSE otherwise. */ @@ -997,8 +1083,7 @@ int find_matching_bracket_pos(linestruct **line, size_t *xpos) bool reversed; /* The direction we search. */ - br_ch = mbstrchr(matchbrackets, - openfile->current->data + openfile->current_x); + br_ch = mbstrchr(matchbrackets, (*line)->data + (*xpos)); if (br_ch == NULL) return NOT_A_BRACKET;