diff --git a/src/cut.c b/src/cut.c index 4458abce..0fec39c2 100644 --- a/src/cut.c +++ b/src/cut.c @@ -48,6 +48,7 @@ void do_deletion(undo_type action) memmove(&openfile->current->data[openfile->current_x], &openfile->current->data[openfile->current_x + charlen], line_len - charlen + 1); + UNFOLD_SEGMENT(openfile->current); #ifndef NANO_TINY /* When softwrapping, a changed number of chunks requires a refresh. */ if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount) @@ -62,6 +63,9 @@ void do_deletion(undo_type action) } else if (openfile->current != openfile->filebot) { linestruct *joining = openfile->current->next; + UNFOLD_SEGMENT(openfile->current); + UNFOLD_SEGMENT(joining); + /* If there is a magic line, and we're before it: don't eat it. */ if (joining == openfile->filebot && openfile->current_x != 0 && !ISSET(NO_NEWLINES)) { @@ -192,7 +196,7 @@ void chop_word(bool forward) * on the edge of the original line, then put the cursor on that * edge instead, so that lines will not be joined unexpectedly. */ if (!forward) { - do_prev_word(); + do_prev_word(ALLOW_FOLDED); if (openfile->current != is_current) { if (is_current_x > 0) { openfile->current = is_current; @@ -201,13 +205,14 @@ void chop_word(bool forward) openfile->current_x = strlen(openfile->current->data); } } else { - do_next_word(ISSET(AFTER_ENDS)); + do_next_word(ISSET(AFTER_ENDS), ALLOW_FOLDED); if (openfile->current != is_current && is_current->data[is_current_x] != '\0') { openfile->current = is_current; openfile->current_x = strlen(is_current->data); } } + UNFOLD_SEGMENT(openfile->current); /* Set the mark at the start of that word. */ openfile->mark = openfile->current; @@ -262,9 +267,13 @@ void extract_segment(linestruct *top, size_t top_x, linestruct *bot, size_t bot_ if (top == bot && top_x == bot_x) return; - if (top != bot) - for (linestruct *line = top->next; line != bot->next; line = line->next) + if (top != bot) { + UNFOLD_SEGMENT(top); + for (linestruct *line = top->next; line != bot->next; line = line->next) { + UNFOLD_SEGMENT(line); had_anchor |= line->has_anchor; + } + } #endif if (top == bot) { @@ -378,6 +387,8 @@ void ingraft_buffer(linestruct *topline) #endif linestruct *botline = topline; + UNFOLD_SEGMENT(line); + while (botline->next != NULL) botline = botline->next; @@ -728,6 +739,7 @@ void paste_text(void) statusline(AHEM, _("Cutbuffer is empty")); return; } + UNFOLD_SEGMENT(openfile->current); #ifndef NANO_TINY add_undo(PASTE, NULL); diff --git a/src/definitions.h b/src/definitions.h index d6ca001d..bc108a41 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -273,6 +273,15 @@ #define MSOME MMAIN|MBROWSER #endif +#ifdef ENABLE_FOLDING +#define UNFOLD_SEGMENT(line) unfold_folded_segment(line) +#else +#define UNFOLD_SEGMENT(line) +#endif + +#define ALLOW_FOLDED TRUE +#define DISALLOW_FOLDED FALSE + /* Enumeration types. */ typedef enum { UNSPECIFIED, NIX_FILE, DOS_FILE, MAC_FILE @@ -374,6 +383,12 @@ enum { ZERO }; +typedef enum { + FOUND_BRACKET = 0, + NOT_FOUND_BRACKET, + NOT_A_BRACKET +} bracket_search_result; + /* Structure types. */ #ifdef ENABLE_COLOR typedef struct colortype { @@ -480,6 +495,10 @@ typedef struct linestruct { bool has_anchor; /* Whether the user has placed an anchor at this line. */ #endif +#ifdef ENABLE_FOLDING + bool folded; + /* Whether or not this line is in a fold segment */ +#endif } linestruct; #ifndef NANO_TINY diff --git a/src/files.c b/src/files.c index c6eadc1c..5fdc9a00 100644 --- a/src/files.c +++ b/src/files.c @@ -681,6 +681,7 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable) if (ISSET(SOFTWRAP)) was_leftedge = leftedge_for(xplustabs(), openfile->current); #endif + UNFOLD_SEGMENT(openfile->current); /* Create an empty buffer. */ topline = make_new_node(NULL); diff --git a/src/move.c b/src/move.c index 9b1d80c3..e92c77bb 100644 --- a/src/move.c +++ b/src/move.c @@ -235,19 +235,29 @@ void to_para_end(void) void to_prev_block(void) { linestruct *was_current = openfile->current; + linestruct *line_step = get_prev_visible_line(openfile->current); bool is_text = FALSE, seen_text = FALSE; /* Skip backward until first blank line after some nonblank line(s). */ - while (openfile->current->prev != NULL && (!seen_text || is_text)) { - openfile->current = openfile->current->prev; + while (line_step != NULL && (!seen_text || is_text)) { + openfile->current = line_step; + line_step = get_prev_visible_line(line_step); +#ifdef ENABLE_FOLDING + /* Skip over the visibly drawn line of the folded segment */ + if (openfile->current->folded) + continue; +#endif is_text = !white_string(openfile->current->data); seen_text = seen_text || is_text; } /* Step forward one line again if we passed text but this line is blank. */ - if (seen_text && openfile->current->next != NULL && - white_string(openfile->current->data)) - openfile->current = openfile->current->next; + line_step = get_next_visible_line(openfile->current); + if (seen_text && line_step != NULL && + white_string(openfile->current->data)) { + openfile->current = line_step; + line_step = get_next_visible_line(line_step); + } openfile->current_x = 0; edit_redraw(was_current, CENTERING); @@ -257,12 +267,19 @@ void to_prev_block(void) void to_next_block(void) { linestruct *was_current = openfile->current; + linestruct *line_step = get_next_visible_line(openfile->current); bool is_white = white_string(openfile->current->data); bool seen_white = is_white; /* Skip forward until first nonblank line after some blank line(s). */ - while (openfile->current->next != NULL && (!seen_white || is_white)) { - openfile->current = openfile->current->next; + while (line_step != NULL && (!seen_white || is_white)) { + openfile->current = line_step; + line_step = get_next_visible_line(line_step); +#ifdef ENABLE_FOLDING + /* Skip over the visibly drawn line of the folded segment */ + if (openfile->current->folded) + continue; +#endif is_white = white_string(openfile->current->data); seen_white = seen_white || is_white; } @@ -274,19 +291,35 @@ void to_next_block(void) #endif } -/* Move to the previous word. */ -void do_prev_word(void) +/* Move to the previous word. + * when allow_folded is true, will move to previous word even if inside + * a folded segment */ +void do_prev_word(bool allow_folded) { bool punctuation_as_letters = ISSET(WORD_BOUNDS); bool seen_a_word = FALSE, step_forward = FALSE; + +#ifdef ENABLE_FOLDING + if (!allow_folded && openfile->current->folded) { + openfile->current = get_prev_visible_line(openfile->current); + openfile->current_x = strlen(openfile->current->data); + } +#endif /* Move backward until we pass over the start of a word. */ while (TRUE) { /* If at the head of a line, move to the end of the preceding one. */ if (openfile->current_x == 0) { if (openfile->current->prev == NULL) break; - openfile->current = openfile->current->prev; +#ifdef ENABLE_FOLDING + if (!allow_folded) { + openfile->current = get_prev_visible_line(openfile->current); + if (openfile->current->folded) + openfile->current = openfile->current->prev; + } else +#endif + openfile->current = openfile->current->prev; openfile->current_x = strlen(openfile->current->data); } @@ -315,11 +348,13 @@ void do_prev_word(void) /* Move one character forward again to sit on the start of the word. */ openfile->current_x = step_right(openfile->current->data, openfile->current_x); + UNFOLD_SEGMENT(openfile->current); } /* Move to the next word. If after_ends is TRUE, stop at the ends of words - * instead of at their beginnings. Return TRUE if we started on a word. */ -bool do_next_word(bool after_ends) + * instead of at their beginnings. If allow_folded is true, go to next word + * even if it's inside a folded segment. Return TRUE if we started on a word. */ +bool do_next_word(bool after_ends, bool allow_folded) { bool punctuation_as_letters = ISSET(WORD_BOUNDS); bool started_on_word = is_word_char(openfile->current->data + @@ -329,6 +364,12 @@ bool do_next_word(bool after_ends) bool seen_word = started_on_word; #endif +#ifdef ENABLE_FOLDING + if (!allow_folded && openfile->current->folded) { + openfile->current = get_next_visible_line(openfile->current); + openfile->current_x = 0; + } +#endif /* Move forward until we reach the start of a word. */ while (TRUE) { /* If at the end of a line, move to the beginning of the next one. */ @@ -336,7 +377,18 @@ bool do_next_word(bool after_ends) /* When at end of file, stop. */ if (openfile->current->next == NULL) break; - openfile->current = openfile->current->next; +#ifdef ENABLE_FOLDING + if (!allow_folded) { + openfile->current = get_next_visible_line(openfile->current); + if (openfile->current->folded) { + /* get_next_visible_line() gives the start of a folded segment, + * so skip to the end */ + openfile->current = get_end_of_folded_segment(openfile->current); + openfile->current = openfile->current->next; + } + } else +#endif + openfile->current = openfile->current->next; openfile->current_x = 0; seen_space = TRUE; } else { @@ -376,6 +428,8 @@ bool do_next_word(bool after_ends) } } + UNFOLD_SEGMENT(openfile->current); + return started_on_word; } @@ -384,7 +438,7 @@ void to_prev_word(void) { linestruct *was_current = openfile->current; - do_prev_word(); + do_prev_word(DISALLOW_FOLDED); edit_redraw(was_current, FLOWING); } @@ -395,7 +449,7 @@ void to_next_word(void) { linestruct *was_current = openfile->current; - do_next_word(ISSET(AFTER_ENDS)); + do_next_word(ISSET(AFTER_ENDS), DISALLOW_FOLDED); edit_redraw(was_current, FLOWING); } @@ -413,6 +467,11 @@ void do_home(void) size_t leftedge = 0; size_t left_x = 0; +#ifdef ENABLE_FOLDING + if (openfile->current->folded) + return; +#endif + if (ISSET(SOFTWRAP)) { leftedge = leftedge_for(was_column, openfile->current); left_x = proper_x(openfile->current, &leftedge, FALSE, leftedge, NULL); @@ -470,6 +529,11 @@ void do_end(void) size_t line_len = strlen(openfile->current->data); bool moved_off_chunk = TRUE; +#ifdef ENABLE_FOLDING + if (openfile->current->folded) + return; +#endif + #ifndef NANO_TINY if (ISSET(SOFTWRAP)) { bool kickoff = TRUE; @@ -612,7 +676,7 @@ void do_left(void) openfile->current_x); #endif } else if (openfile->current != openfile->filetop) { - openfile->current = openfile->current->prev; + openfile->current = get_prev_visible_line(openfile->current); openfile->current_x = strlen(openfile->current->data); } @@ -624,6 +688,13 @@ void do_right(void) { linestruct *was_current = openfile->current; +#ifdef ENABLE_FOLDING + if (openfile->current->folded) { + openfile->current = get_next_visible_line(openfile->current); + if (openfile->current == NULL) + openfile->current = was_current; + } else +#endif if (openfile->current->data[openfile->current_x] != '\0') { openfile->current_x = step_right(openfile->current->data, openfile->current_x); @@ -634,7 +705,7 @@ void do_right(void) openfile->current_x); #endif } else if (openfile->current != openfile->filebot) { - openfile->current = openfile->current->next; + openfile->current = get_next_visible_line(openfile->current); openfile->current_x = 0; } diff --git a/src/nano.c b/src/nano.c index 8578ee7d..783f2997 100644 --- a/src/nano.c +++ b/src/nano.c @@ -78,6 +78,9 @@ linestruct *make_new_node(linestruct *prevnode) #ifndef NANO_TINY newnode->has_anchor = FALSE; #endif +#ifdef ENABLE_FOLDING + newnode->folded = FALSE; +#endif return newnode; } @@ -117,6 +120,7 @@ void delete_node(linestruct *line) /* Disconnect a node from a linked list of linestructs and delete it. */ void unlink_node(linestruct *line) { + UNFOLD_SEGMENT(line); if (line->prev != NULL) line->prev->next = line->next; if (line->next != NULL) @@ -156,6 +160,9 @@ linestruct *copy_node(const linestruct *src) #ifndef NANO_TINY dst->has_anchor = src->has_anchor; #endif +#ifdef ENABLE_FOLDING + dst->folded = src->folded; +#endif return dst; } @@ -1461,6 +1468,7 @@ void inject(char *burst, size_t count) #ifndef NANO_TINY size_t original_row = 0; size_t old_amount = 0; + UNFOLD_SEGMENT(thisline); if (ISSET(SOFTWRAP)) { if (openfile->current_y == editwinrows - 1) diff --git a/src/prototypes.h b/src/prototypes.h index d2b2e1a5..cefb9ab5 100644 --- a/src/prototypes.h +++ b/src/prototypes.h @@ -365,8 +365,8 @@ void to_para_end(void); #endif void to_prev_block(void); void to_next_block(void); -void do_prev_word(void); -bool do_next_word(bool after_ends); +void do_prev_word(bool allow_folded); +bool do_next_word(bool after_ends, bool allow_folded); void to_prev_word(void); void to_next_word(void); void do_home(void); @@ -579,6 +579,8 @@ linestruct *line_from_number(ssize_t number); #endif /* Most functions in winio.c. */ +linestruct *get_next_visible_line(linestruct *line); +linestruct *get_prev_visible_line(linestruct *line); #ifndef NANO_TINY void record_macro(void); void run_macro(void); @@ -674,3 +676,13 @@ void flip_newbuffer(void); #endif void discard_buffer(void); void do_cancel(void); + +/* Most functions in folding.c. */ +#ifdef ENABLE_FOLDING +void do_fold_segment(void); +void unfold_folded_segment(linestruct *line); +void unfold_folded_segment_from(linestruct *line); +linestruct *get_start_of_folded_segment(linestruct* line); +linestruct *get_end_of_folded_segment(linestruct* line); +int get_folded_segment_length(linestruct *line); +#endif diff --git a/src/search.c b/src/search.c index 7fbb244c..03ef89e4 100644 --- a/src/search.c +++ b/src/search.c @@ -308,6 +308,8 @@ int findnextstr(const char *needle, bool whole_word_only, int modus, } } + UNFOLD_SEGMENT(line); + found_x = found - line->data; nodelay(midwin, FALSE); @@ -754,6 +756,7 @@ void goto_line_posx(ssize_t linenumber, size_t pos_x) openfile->current_x = pos_x; openfile->placewewant = xplustabs(); + UNFOLD_SEGMENT(openfile->current); refresh_needed = TRUE; } @@ -808,16 +811,27 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer, line = 1; #ifdef ENABLE_COLOR +#ifdef ENABLE_FOLDING + linestruct *edit_bottom = openfile->edittop; + for (int i = 0;edit_bottom != openfile->filebot && i < editwinrows;++i) + edit_bottom = get_next_visible_line(edit_bottom); + if (line > edit_bottom->lineno || + (ISSET(SOFTWRAP) && line > openfile->current->lineno)) + recook |= perturbed; +#else if (line > openfile->edittop->lineno + editwinrows || (ISSET(SOFTWRAP) && line > openfile->current->lineno)) recook |= perturbed; -#endif +#endif /* ENABLE_FOLDING */ +#endif /* ENABLE_COLOR */ /* Iterate to the requested line. */ for (openfile->current = openfile->filetop; line > 1 && openfile->current != openfile->filebot; line--) openfile->current = openfile->current->next; + UNFOLD_SEGMENT(openfile->current); + /* Take a negative column number to mean: from the end of the line. */ if (column < 0) column = breadth(openfile->current->data) + column + 2; @@ -982,6 +996,7 @@ void do_find_bracket(void) /* When balance reached zero, we've found the complementary bracket. */ if (balance == 0) { + UNFOLD_SEGMENT(openfile->current); edit_redraw(was_current, FLOWING); return; } @@ -1015,6 +1030,7 @@ void go_to_and_confirm(linestruct *line) if (line != openfile->current) { openfile->current = line; openfile->current_x = 0; + UNFOLD_SEGMENT(openfile->current); #ifdef ENABLE_COLOR if (line->lineno > openfile->edittop->lineno + editwinrows || (ISSET(SOFTWRAP) && line->lineno > was_current->lineno)) diff --git a/src/text.c b/src/text.c index e3713c1d..67caa20c 100644 --- a/src/text.c +++ b/src/text.c @@ -96,6 +96,8 @@ void indent_a_line(linestruct *line, char *indentation) if (indent_len == 0) return; + UNFOLD_SEGMENT(line); + /* Add the fabricated indentation to the beginning of the line. */ line->data = nrealloc(line->data, length + indent_len + 1); memmove(line->data + indent_len, line->data, length + 1); @@ -226,6 +228,8 @@ void unindent_a_line(linestruct *line, size_t indent_len) if (indent_len == 0) return; + UNFOLD_SEGMENT(line); + /* Remove the first tab's worth of whitespace from this line. */ memmove(line->data, line->data + indent_len, length - indent_len + 1); @@ -317,6 +321,8 @@ bool comment_line(undo_type action, linestruct *line, const char *comment_seq) /* Length of postfix. */ size_t line_len = strlen(line->data); + UNFOLD_SEGMENT(line); + if (!ISSET(NO_NEWLINES) && line == openfile->filebot) return FALSE; @@ -859,6 +865,12 @@ void do_enter(void) { linestruct *newnode = make_new_node(openfile->current); size_t extra = 0; +#ifdef ENABLE_FOLDING + if (openfile->current->folded) { + newnode->folded = TRUE; + openfile->current->folded = FALSE; + } +#endif #ifndef NANO_TINY linestruct *sampleline = openfile->current; bool allblanks = FALSE; @@ -1585,6 +1597,7 @@ void concat_paragraph(linestruct *line, size_t count) line->data = nrealloc(line->data, line_len + next_line_len - next_lead_len + 1); strcat(line->data, next_line->data + next_lead_len); + UNFOLD_SEGMENT(line); #ifndef NANO_TINY line->has_anchor |= next_line->has_anchor; #endif @@ -2852,6 +2865,7 @@ void do_linter(void) /* Place the cursor to indicate the affected line. */ place_the_cursor(); + UNFOLD_SEGMENT(openfile->current); wnoutrefresh(midwin); kbinput = get_kbinput(footwin, VISIBLE); @@ -2993,7 +3007,7 @@ void count_lines_words_and_characters(void) * incrementing the word count for each successful step. */ while (openfile->current->lineno < botline->lineno || (openfile->current == botline && openfile->current_x < bot_x)) { - if (do_next_word(FALSE)) + if (do_next_word(FALSE, ALLOW_FOLDED)) words++; } diff --git a/src/winio.c b/src/winio.c index fee2a84e..e69cfd2a 100644 --- a/src/winio.c +++ b/src/winio.c @@ -140,6 +140,55 @@ void run_macro(void) } #endif /* !NANO_TINY */ +/* Return the current offset of target from openfile->edittop */ +int get_row_from_edittop(linestruct *target) +{ +#ifdef ENABLE_FOLDING + linestruct *line = openfile->edittop; + int row = 0; + while (line != NULL && line != target && row < editwinrows) { + line = get_end_of_folded_segment(line); + line = line->next; + ++row; + } + return row; +#else + return target->lineno - openfile->edittop->lineno; +#endif +} + +/* Return the next line, taking fold segments into account */ +linestruct *get_next_visible_line(linestruct *line) +{ + if (!line) + return NULL; + +#ifdef ENABLE_FOLDING + line = get_end_of_folded_segment(line); + if (line == NULL) + return NULL; +#endif + return line->next; +} + +/* Return the previous line, taking fold segments into account */ +linestruct *get_prev_visible_line(linestruct *line) +{ + if (!line) + return NULL; + +#ifdef ENABLE_FOLDING + if (line->folded) { + line = get_start_of_folded_segment(line); + return line == NULL ? NULL : line->prev; + } + if (line->prev != NULL && line->prev->folded) + return get_start_of_folded_segment(line->prev); + +#endif + return line->prev; +} + /* Allocate the requested space for the keystroke buffer. */ void reserve_space_for(size_t newsize) { @@ -2875,8 +2924,13 @@ int update_softwrapped_line(linestruct *line) /* Find out on which screen row the target line should be shown. */ while (someline != line && someline != NULL) { - row += 1 + extra_chunks_in(someline); - someline = someline->next; +#ifdef ENABLE_FOLDING + if (someline->folded) + ++row; + else +#endif + row += 1 + extra_chunks_in(someline); + someline = get_next_visible_line(someline); } /* If the first chunk is offscreen, don't even try to display it. */ @@ -2945,7 +2999,7 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge) break; i -= chunk; - *line = (*line)->prev; + *line = get_prev_visible_line(*line); *leftedge = HIGHEST_POSITIVE; } @@ -2953,8 +3007,13 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge) *leftedge = leftedge_for(*leftedge, *line); } else #endif - for (i = nrows; i > 0 && (*line)->prev != NULL; i--) - *line = (*line)->prev; + { + linestruct *prev = get_prev_visible_line(*line); + for (i = nrows; i > 0 && prev != NULL; i--) { + *line = prev; + prev = get_prev_visible_line(prev); + } + } return i; } @@ -2995,8 +3054,13 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge) *leftedge = current_leftedge; } else #endif - for (i = nrows; i > 0 && (*line)->next != NULL; i--) - *line = (*line)->next; + { + linestruct *next = get_next_visible_line(*line); + for (i = nrows; i > 0 && next != NULL; i--) { + *line = next; + next = get_next_visible_line(*line); + } + } return i; } @@ -3025,18 +3089,44 @@ void draw_scrollbar(void) { int fromline = openfile->edittop->lineno - 1; int totallines = openfile->filebot->lineno; - int coveredlines = editwinrows; + int coveredlines = 0; + linestruct *line = openfile->edittop; if (ISSET(SOFTWRAP)) { - linestruct *line = openfile->edittop; - int extras = extra_chunks_in(line) - chunk_for(openfile->firstcolumn, line); - - while (line->lineno + extras < fromline + editwinrows && line->next) { - line = line->next; - extras += extra_chunks_in(line); + int i = 0; +#ifdef ENABLE_FOLDING + if (line->folded) + coveredlines += get_folded_segment_length(line); + else +#endif + { + i = extra_chunks_in(line) - chunk_for(openfile->firstcolumn, line); + coveredlines = i; } - coveredlines = line->lineno - fromline; + for (;i < editwinrows && line->next != NULL;++i) { + line = get_next_visible_line(line); +#ifdef ENABLE_FOLDING + if (line->folded) + coveredlines += get_folded_segment_length(line); + else +#endif + { + i += extra_chunks_in(line); + ++coveredlines; + } + } + } else { +#ifdef ENABLE_FOLDING + for (int i = 0;i < editwinrows && line->next != NULL;++i) { + if (line->folded) + coveredlines += get_folded_segment_length(line); + else + ++coveredlines; + } +#else + coveredlines = editwinrows; +#endif /* ENABLE_FOLDING */ } int lowest = (fromline * editwinrows) / totallines; @@ -3104,7 +3194,7 @@ void edit_scroll(bool direction) while (nrows > 0 && line != NULL) { nrows -= update_line(line, (line == openfile->current) ? openfile->current_x : 0); - line = line->next; + line = get_next_visible_line(line); } } @@ -3334,8 +3424,11 @@ void edit_redraw(linestruct *old_current, update_type manner) while (line != openfile->current) { update_line(line, 0); - line = (line->lineno > openfile->current->lineno) ? - line->prev : line->next; + if (line->lineno > openfile->current->lineno) { + line = get_prev_visible_line(line); + } else { + line = get_next_visible_line(line); + } } } else #endif