int center(buffer * const b) { line_desc * const ld = b->cur_line_desc; const int right_margin = b->opt.right_margin ? b->opt.right_margin : ne_columns; int64_t len, start_pos = 0, end_pos = ld->line_len; while(start_pos < ld->line_len && isasciispace(ld->line[start_pos])) start_pos = next_pos(ld->line, start_pos, b->encoding); if (start_pos == ld->line_len) return OK; while(isasciispace(ld->line[prev_pos(ld->line, end_pos, b->encoding)])) end_pos = prev_pos(ld->line, end_pos, b->encoding); len = b->encoding == ENC_UTF8 ? utf8strlen(&ld->line[start_pos], end_pos - start_pos) : end_pos - start_pos; if (len >= right_margin) return OK; b->cur_pos = -1; start_undo_chain(b); delete_stream(b, ld, b->cur_line, end_pos, ld->line_len - end_pos); delete_stream(b, ld, b->cur_line, 0, start_pos); insert_spaces(b, ld, b->cur_line, 0, (right_margin - len) / 2); end_undo_chain(b); return OK; }
int64_t word_wrap(buffer * const b) { const int64_t len = b->cur_line_desc->line_len; char * const line = b->cur_line_desc->line; int64_t pos, first_pos; if (!(pos = b->cur_pos)) return ERROR; /* Find the first possible position we could break a line on. */ first_pos = 0; /* Skip leading white space */ while (first_pos < len && ne_isspace(get_char(&line[first_pos], b->encoding), b->encoding)) first_pos = next_pos(line, first_pos, b->encoding); /* Skip non_space after leading white space */ while (first_pos < len && !ne_isspace(get_char(&line[first_pos], b->encoding), b->encoding)) first_pos = next_pos(line, first_pos, b->encoding); /* Now we know that the line shouldn't be broken before &line[first_pos]. */ /* Search left from the current position to find a candidate space to break the line on.*/ while((pos = prev_pos(line, pos, b->encoding)) && !ne_isspace(get_char(&line[pos], b->encoding), b->encoding)); if (! pos || pos < first_pos) return ERROR; start_undo_chain(b); const int64_t result = b->cur_pos - pos - 1; if (pos < b->cur_pos) b->cur_pos = -1; delete_one_char(b, b->cur_line_desc, b->cur_line, pos); insert_one_line(b, b->cur_line_desc, b->cur_line, pos); end_undo_chain(b); return result; }
int undelete_line(buffer * const b) { line_desc * const ld = b->cur_line_desc; if (!b->last_deleted) return ERROR; start_undo_chain(b); if (b->cur_pos > ld->line_len) insert_spaces(b, ld, b->cur_line, ld->line_len, b->win_x + b->cur_x - calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding)); insert_one_line(b, ld, b->cur_line, b->cur_pos); insert_stream(b, ld, b->cur_line, b->cur_pos, b->last_deleted->stream, b->last_deleted->len); end_undo_chain(b); return OK; }
int word_wrap2(buffer * const b) { static char avcmd[16]; if (b->cur_pos > b->cur_line_desc->line_len) return OK; bool non_blank_added = false; int avshift; char * line = b->cur_line_desc->line; int64_t pos, original_line; /* If the char to our left is a space, we need to insert a non-space to attach our WORDWRAP_BOOKMARK to because spaces at the split point get removed, which effectively leaves our bookmark on the current line. */ delay_update(); pos = prev_pos(line, b->cur_pos, b->encoding); if (pos >= 0 && (non_blank_added = ne_isspace(get_char(&line[pos], b->encoding), b->encoding))) { start_undo_chain(b); insert_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos, 'X'); line = b->cur_line_desc->line; goto_pos(b, next_pos(line, b->cur_pos, b->encoding)); } b->bookmark[WORDWRAP_BOOKMARK].pos = b->cur_pos; b->bookmark[WORDWRAP_BOOKMARK].line = original_line = b->cur_line; b->bookmark[WORDWRAP_BOOKMARK].cur_y = b->cur_y; b->bookmark_mask |= (1 << WORDWRAP_BOOKMARK); paragraph(b); goto_line_pos(b, b->bookmark[WORDWRAP_BOOKMARK].line, b->bookmark[WORDWRAP_BOOKMARK].pos); line = b->cur_line_desc->line; b->bookmark[WORDWRAP_BOOKMARK].cur_y += b->bookmark[WORDWRAP_BOOKMARK].line - original_line; if (avshift = b->cur_y - b->bookmark[WORDWRAP_BOOKMARK].cur_y) { snprintf(avcmd, 16, "%c%d", avshift > 0 ? 'T' :'B', avshift > 0 ? avshift : -avshift); adjust_view(b, avcmd); } b->bookmark_mask &= ~(1 << WORDWRAP_BOOKMARK); if (non_blank_added) { goto_pos(b, prev_pos(b->cur_line_desc->line, b->cur_pos, b->encoding)); delete_one_char(b, b->cur_line_desc, b->cur_line, b->cur_pos); end_undo_chain(b); } return stop ? STOPPED : OK; }
int replace(buffer * const b, const int n, const char * const string) { int64_t len; assert(string != NULL); last_replace_empty_match = false; len = strlen(string); start_undo_chain(b); delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, n); if (len) insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, string, len); end_undo_chain(b); if (! b->opt.search_back) goto_pos(b, b->cur_pos + len); return OK; }
int replace_regexp(buffer * const b, const char * const string) { assert(string != NULL); bool reg_used = false; char *p, *q, *t = NULL; if (q = p = str_dup(string)) { int len = strlen(p); while(true) { while(*q && *q != '\\') q++; if (!*q) break; int i = *(q + 1) - '0'; if (*(q + 1) == '\\') { memmove(q, q + 1, strlen(q + 1) + 1); q++; len--; } else if (i >= 0 && i < RE_NREGS && re_reg.start[i] >= 0) { if (b->encoding == ENC_UTF8) { /* In the UTF-8 case, the replacement group index must be mapped through map_group to recover the real group. */ if ((i = map_group[i]) >= RE_NREGS) { free(p); return GROUP_NOT_AVAILABLE; } } *q++ = 0; *q++ = i; reg_used = true; } else { free(p); return WRONG_CHAR_AFTER_BACKSLASH; } } if (reg_used) { if (t = malloc(re_reg.end[0] - re_reg.start[0] + 1)) { memcpy(t, b->cur_line_desc->line + re_reg.start[0], re_reg.end[0] - re_reg.start[0]); t[re_reg.end[0] - re_reg.start[0]] = 0; } else { free(p); return OUT_OF_MEMORY; } } for(int i = re_reg.num_regs; i-- != 0;) { re_reg.end[i] -= re_reg.start[0]; re_reg.start[i] -= re_reg.start[0]; } start_undo_chain(b); delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, re_reg.end[0]); q = p; int64_t pos = 0; while(true) { if (strlen(q)) { insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos + pos, q, strlen(q)); pos += strlen(q); } q += strlen(q) + 1; if (q - p > len) break; assert(*q < RE_NREGS); if (re_reg.end[*(unsigned char *)q] - re_reg.start[*(unsigned char *)q]) { char c = t[re_reg.end[*(unsigned char *)q]]; t[re_reg.end[*(unsigned char *)q]] = 0; insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos + pos, t + re_reg.start[*(unsigned char *)q], re_reg.end[*(unsigned char *)q] - re_reg.start[*(unsigned char *)q]); t[re_reg.end[*(unsigned char *)q]] = c; pos += re_reg.end[*(unsigned char *)q] - re_reg.start[*(unsigned char *)q]; } q++; } end_undo_chain(b); if (! b->opt.search_back) goto_pos(b, b->cur_pos + pos); free(t); free(p); } else return OUT_OF_MEMORY; last_replace_empty_match = re_reg.start[0] == re_reg.end[0]; return OK; }
int shift(buffer * const b, char *p, char *msg, int msg_size) { const bool use_tabs = b->opt.tabs && b->opt.shift_tabs; const int64_t init_line = b->cur_line, init_pos = b->cur_pos, init_y = b->cur_y; line_desc *ld = NULL, *start_line_desc = NULL; int64_t shift_size = 1; char dir = '>'; int shift_mag = b->opt.tab_size, rc = 0; /* Parse parm p; looks like [<|>] ### [s|t], but we allow them in any order, once, with optional white space. */ if (p) { int dir_b = 0, size_b = 0, st_b = 0; while (*p) { if (isasciispace(*p)) p++; else if (!dir_b && (dir_b = (*p == '<' || *p == '>'))) dir = *p++; else if (!size_b && (size_b = isdigit((unsigned char)*p))) { errno = 0; shift_size = strtoll(p, &p, 10); if (errno) return INVALID_SHIFT_SPECIFIED; } else if (!st_b && (st_b = (*p == 's' || *p == 'S'))) { shift_mag = 1; p++; } else if (!st_b && (st_b = (*p == 't' || *p == 'T'))) p++; else return INVALID_SHIFT_SPECIFIED; } } shift_size *= max(1, shift_mag); if (shift_size == 0) return INVALID_SHIFT_SPECIFIED; int64_t first_line = b->cur_line, last_line = b->cur_line, left_col = 0; if (b->marking) { if (b->mark_is_vertical) left_col = min(calc_width(b->cur_line_desc, b->block_start_pos, b->opt.tab_size, b->encoding), calc_width(b->cur_line_desc, b->cur_pos, b->opt.tab_size, b->encoding)); first_line = min(b->block_start_line, b->cur_line); last_line = max(b->block_start_line, b->cur_line); } /* If we're shifting left (dir=='<'), verify that we have sufficient white space to remove on all the relevant lines before making any changes, i. */ if (dir == '<') { shift_size = -shift_size; /* signed shift_size now also indicates direction. */ for (int64_t line = first_line; !rc && line <= last_line; line++) { int64_t pos; goto_line(b, line); pos = calc_pos(b->cur_line_desc, left_col, b->opt.tab_size, b->encoding); while (pos < b->cur_line_desc->line_len && left_col - calc_width(b->cur_line_desc, pos, b->opt.tab_size, b->encoding) > shift_size) { if (isasciispace(b->cur_line_desc->line[pos])) pos = next_pos(b->cur_line_desc->line, pos, b->encoding); else { rc = INSUFFICIENT_WHITESPACE; break; } } } } if (!rc) { start_undo_chain(b); for (int64_t line = first_line; line <= last_line; line++) { int64_t pos, c_pos, c_col_orig, offset; b->attr_len = -1; goto_line(b, line); b->cur_pos = -1; ld = b->cur_line_desc; if (line == first_line) start_line_desc = ld; pos = calc_pos(ld, left_col, b->opt.tab_size, b->encoding); /* If left_col is in the middle of a tab, pos will be on that tab. */ /* whitespace adjustment strategy: 1. Starting from left_col, advance to the right to the first non-blank character C. 2. Note C's col. The desired new column is this value +/- shift_size. 3. Move left looking for the first tab or non-whitespace or the left_col, whichever comes first. Whitespace changes all take place at that transition point. 4. While C's col is wrong if C's col is too far to the right, if we're on a space, delete it; else if there's a tab to our left, delete it; else we should not have started, because it's not possible! if C's col is too far to the left, if its needs to be beyond the next tab stop, insert a tab and move right; else insert a space. */ /* 1. */ while (pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, b->encoding); if (pos >= ld->line_len) continue; /* We ran off the end of the line. */ /* line[pos] should be the first non-blank character. */ /* 2. */ c_pos = pos; c_col_orig = calc_width(ld, c_pos, b->opt.tab_size, b->encoding); /* 3. */ while (pos && ld->line[pos-1] == ' ') pos = prev_pos(ld->line, pos, b->encoding); /* If pos is non-zero, it should be on a blank, with only blanks between here and c_pos. */ /* 4. */ /* offset = how_far_we_have_moved - how_far_we_want_to_move. */ while (!stop && (offset = calc_width(ld, c_pos, b->opt.tab_size, b->encoding)-c_col_orig - shift_size)) { if (offset > 0) { /* still too far right; remove whitespace */ if (ld->line[pos] == ' ') { delete_stream(b, ld, b->cur_line, pos, 1); c_pos--; } else if (pos) { /* should be a tab just to our left */ pos = prev_pos(ld->line, pos, b->encoding); /* now we're on the tab */ if (ld->line[pos] == '\t') { delete_stream(b, ld, b->cur_line, pos, 1); c_pos--; } else break; /* Should have been a tab. This should never happen! Give up on this line and go mangle the next one. */ } else break; /* This should never happen; give up on this line and go mangle the next one. */ } else if (offset < 0) { /* too far left; insert whitespace */ char c = ' '; if (use_tabs && (b->opt.tab_size - calc_width(ld, pos, b->opt.tab_size, b->encoding) % b->opt.tab_size) <= -offset ) c = '\t'; if (insert_one_char(b, ld, b->cur_line, pos, c)) { break; } pos++; c_pos++; } } } end_undo_chain(b); if (b->syn) { b->attr_len = -1; need_attr_update = true; update_syntax_states(b, -1, start_line_desc, (line_desc *)ld->ld_node.next); } update_window_lines(b, b->top_line_desc, 0, ne_lines - 2, false); } /* put the screen back where way we found it. */ goto_line_pos(b, init_line, init_pos); delay_update(); const int64_t avshift = b->cur_y - init_y; if (avshift) { snprintf(msg, msg_size, "%c%" PRId64, avshift > 0 ? 'T' :'B', avshift > 0 ? avshift : -avshift); adjust_view(b, msg); } return rc; }
int paragraph(buffer * const b) { line_desc *ld = b->cur_line_desc, *start_line_desc = ld; if (!ld->line) return line_down(b); /* Establish appropriate leading space. This will be taken from the line following the current line if it is non-blank. Otherwise it will be taken from the current line. Save a copy of it for later as space[]. **/ if ( !( (ld->ld_node.next->next && save_space((line_desc *)ld->ld_node.next, b->opt.tab_size, b->encoding)) || save_space(ld, b->opt.tab_size, b->encoding) ) ) return line_down(b); int64_t pos = b->cur_pos; b->cur_pos = -1; start_undo_chain(b); /* This insertion and deletion of a single character ensures that the cursor ends up here after an undo. */ int64_t line = b->cur_line; insert_one_char(b, ld, line, 0, ' '); delete_stream(b, ld, line, 0, 1); const int right_margin = b->opt.right_margin ? b->opt.right_margin : ne_columns; bool done; do { done = true; /* set this to false if we do any work in the loop. */ trim_trailing_space(b, ld, line, b->encoding); /* Suck up subsequent lines until this one is long enough to need splitting */ while ((calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding) <= right_margin) && ld->ld_node.next->next && is_part_of_paragraph((line_desc *)ld->ld_node.next, b->opt.tab_size, &pos, b->encoding)) { line_desc *ld_next = (line_desc *)ld->ld_node.next; insert_one_char(b, ld, line, ld->line_len, ' '); if (pos) delete_stream(b, ld_next, line + 1, 0, pos); /* pos was set by is_part_of_paragraph() above. */ delete_stream(b, ld, line, ld->line_len, 1); /* joins next line to this one */ trim_trailing_space(b, ld, line, b->encoding); done = false; } if (calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding) > right_margin) { int64_t spaces; int64_t split_pos; /* Skip past leading spaces... */ pos = 0; while(pos < ld->line_len && isasciispace(ld->line[pos])) pos = next_pos(ld->line, pos, b->encoding); /* Find the split point */ split_pos = spaces = 0; while (pos < ld->line_len && (calc_width(ld, pos, b->opt.tab_size, b->encoding) < right_margin || ! split_pos)) { if (isasciispace(ld->line[pos])) { split_pos = pos; spaces = 0; while (pos < ld->line_len && isasciispace(ld->line[pos])) { pos = next_pos(ld->line, pos, b->encoding); spaces++; } } else pos = next_pos(ld->line, pos, b->encoding); } if (split_pos) { done = false; /* Remove any space at the split point. */ if (spaces) delete_stream(b, ld, line, split_pos, spaces); /* Split the line at the split point. (We are done with this line) */ insert_one_line(b, ld, line, split_pos); /* Make the new next line the current line **/ if (ld->ld_node.next->next) { ld = (line_desc *)ld->ld_node.next; line++; if (pa_space && pa_space_len && pa_space_pos) insert_stream(b, ld, line, 0, pa_space, pa_space_pos); trim_trailing_space(b, ld, line, b->encoding); } } else { /* Line not split; is there a next one in the paragraph? */ if ( ld->ld_node.next->next && is_part_of_paragraph((line_desc *)ld->ld_node.next, b->opt.tab_size, &pos, b->encoding) ) { ld = (line_desc *)ld->ld_node.next; line++; done = false; } } } } while (!stop && !done); end_undo_chain(b); if (pa_space) { free(pa_space); pa_space = NULL; } if (b->syn) { b->attr_len = -1; need_attr_update = true; update_syntax_states(b, -1, start_line_desc, (line_desc *)ld->ld_node.next); } update_window_lines(b, b->cur_line_desc, b->cur_y, ne_lines - 2, false); goto_line_pos(b, line, pos); if (stop || line_down(b) == ERROR) return stop ? STOPPED : ERROR; /* Try to find the first non-blank starting with this line. */ ld = b->cur_line_desc; line = b->cur_line; do { if (ld->line) { for (pos = 0; pos < ld->line_len; pos = next_pos(ld->line, pos, b->encoding)) { if (!isasciispace(ld->line[pos])) { goto_line_pos(b, line, pos); return ld->ld_node.next ? OK : ERROR; } } } ld = (line_desc *)ld->ld_node.next; line++; } while (ld->ld_node.next); return b->cur_line_desc->ld_node.next ? OK : ERROR; }
static int to_something(buffer *b, int (to_first)(int), int (to_rest)(int)) { assert_buffer(b); /* If we are after the end of the line, just return ERROR. */ if (b->cur_line == b->num_lines -1 && b->cur_pos >= b->cur_line_desc->line_len) return ERROR; int64_t pos = b->cur_pos; int c; /* First of all, we search for the word start, if we're not over it. */ if (pos >= b->cur_line_desc->line_len || !ne_isword(c = get_char(&b->cur_line_desc->line[pos], b->encoding), b->encoding)) if (search_word(b, 1) != OK) return ERROR; bool changed = false; int64_t new_len = 0; pos = b->cur_pos; const int64_t cur_char = b->cur_char; const int cur_x = b->cur_x; /* Then, we compute the word position extremes, length of the result (which may change because of casing). */ while (pos < b->cur_line_desc->line_len && ne_isword(c = get_char(&b->cur_line_desc->line[pos], b->encoding), b->encoding)) { const int new_c = new_len ? to_rest(c) : to_first(c); changed |= (c != new_c); if (b->encoding == ENC_UTF8) new_len += utf8seqlen(new_c); else new_len++; pos = next_pos(b->cur_line_desc->line, pos, b->encoding); } const int64_t len = pos - b->cur_pos; if (!len) { char_right(b); return OK; } if (changed) { /* We actually perform changes only if some character was case folded. */ char * word = malloc(new_len * sizeof *word); if (!word) return OUT_OF_MEMORY; pos = b->cur_pos; new_len = 0; /* Second pass: we actually build the transformed word. */ while (pos < b->cur_line_desc->line_len && ne_isword(c = get_char(&b->cur_line_desc->line[pos], b->encoding), b->encoding)) { if (b->encoding == ENC_UTF8) new_len += utf8str(new_len ? to_rest(c) : to_first(c), word + new_len); else { word[new_len] = new_len ? to_rest(c) : to_first(c); new_len++; } pos = next_pos(b->cur_line_desc->line, pos, b->encoding); } start_undo_chain(b); delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, len); if (new_len) insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, word, new_len); free(word); end_undo_chain(b); if (cur_char < b->attr_len) b->attr_len = cur_char; update_line(b, b->cur_line_desc, b->cur_y, cur_x, false); need_attr_update = true; } return search_word(b, 1); }