#include #include #include "line-ed.h" #include "refresh.h" #include "buffer.h" #include "cursor.h" #include "hl-range.h" #include /* prints the provided string to the terminal, applying any relevant highlight ranges. * this function prints all characters in `s` until it encounters a null char (\0) or * linefeed (\n). * * the (x, y) coordinates provided should be the data coordinates of the * first character in `s`. */ void print_text(struct line_ed *ed, size_t x, size_t y, const char *s) { b_tty *tty = ed->l_tty; struct hl_range *cur_range = get_hl_range(ed, x, y); for (size_t i = 0; s[i] != '\n' && s[i] != '\0'; i++) { if (!cur_range) { b_tty_reset_vmode(tty); fputc(s[i], stdout); continue; } while (1) { if (!cur_range) { break; } int hl_result = apply_hl_range(cur_range, tty, x + i, y); if (hl_result == 0) { break; } cur_range = get_next_hl_range(cur_range); apply_hl_range(cur_range, tty, x + i, y); } fputc(s[i], stdout); } } void print_buffer(struct line_ed *ed) { const char *line_buf = ed->l_buf; size_t line_len = strcspn(line_buf, "\n"); unsigned int y = 0; while (1) { print_text(ed,0, y, line_buf); line_buf += line_len; if (*line_buf == '\n') { line_buf++; } if (*line_buf == '\0') { break; } y++; line_len = strcspn(line_buf, "\n"); fputc('\r', stdout); fputc('\n', stdout); } } /* this function is called after a character is inserted into the line_ed buffer. * the function performs the following steps: * 1. get a pointer to the start of the line that was modified. * 2. determine the first character in the line that needs to be redrawn. * this may result in an append, a partial reprint, or a full reprint. * 3. re-print the relevant portion of the buffer: * for an append (a character added to the end of the line): * * write the inserted char to the terminal. * * done. * for a partial reprint: * * clear all printed chars from the logical cursor position to the end of the line. * * print the portion of the line corresponding to the cleared section. * * move the physical (terminal) cursor backwards until its position * matches the logical (line_ed) cursor. * for a full reprint: * * same as a partial reprint except that, rather than reprinting * from the logical cursor position, the entire line is reprinted. */ void put_refresh(struct line_ed *ed, struct refresh_state *state) { /* get the data for the line that is being refreshed */ const char *line_buf = line_start(ed, ed->l_cursor_y); size_t line_len = strcspn(line_buf, "\n"); /* the index of the first char in line_buf that needs to be reprinted */ size_t start_x = state->r_prev_cursor_x; /* the distance between the first char to be reprinted and the end * of the line. * the physical cursor will be moved back by this amount after the * line is reprinted. */ long cursor_rdelta = (long)(line_len - start_x); if (ed->l_flags & LINE_ED_FULL_REPRINT) { if (start_x) { //fprintf(stdout, "\033[%uD", start_x); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_CURSOR, -(long long)start_x); } start_x = 0; } print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x); /* decrement the rdelta (move the cursor back one fewer cells), * so that the physical cursor will be placed AFTER the character that * was just inserted. */ cursor_rdelta--; b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_CURSOR, -cursor_rdelta); #if 0 for (unsigned int i = 0; i < cursor_rdelta; i++) { fputs("\010", stdout); } #endif fflush(stdout); } /* this function is called after a character is removed from the line_ed buffer. * IF the character was a linefeed. * * this is separate from backspace_simple_refresh because, in this situation, * the cursor position depends on the length of the previous line before * the linefeed was deleted, and we have to reprint every line following the * two that were combined. */ void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state) { /* get the data for the line that is being refreshed. * relative to where the cursor was before the linefeed was deleted, * this is the PREVIOUS line. */ const char *line_buf = line_start(ed, ed->l_cursor_y); size_t line_len = strcspn(line_buf, "\n"); /* the index of the first char in line_buf that needs to be reprinted. * subtract one to account for the linefeed that has since been deleted. */ size_t start_x = state->r_prev_line_len - 1; /* the column to move the physical cursor to after it has been moved * to the previous line. * NOTE that this number includes the length of the prompt! * we add 1 to start_x to ensure that the cursor is moved to the cell * AFTER the last char of the line. */ size_t new_x; line_ed_coords_to_physical_coords(ed, start_x + 1, ed->l_cursor_y, &new_x, NULL); /* the physical cursor is currently at the beginning of the line that * has just been moved up. from here, clear this line and the rest * from the screen. */ //fputs("\033[J", stdout); b_tty_clear(ed->l_tty, B_TTY_CLEAR_SCREEN | B_TTY_CLEAR_FROM_CURSOR); if (ed->l_flags & LINE_ED_FULL_REPRINT) { /* next, move the physical cursor up and to the beginning of the previous line */ size_t tmp_x; line_ed_coords_to_physical_coords(ed, 0, ed->l_cursor_y, &tmp_x, NULL); b_tty_move_cursor_y(ed->l_tty, B_TTY_POS_CURSOR, -1); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_START, tmp_x); //fprintf(stdout, "\033[A\033[%uG", tmp_x + 1); start_x = 0; } else { /* next, move the physical cursor up and to the end of the previous line */ //fprintf(stdout, "\033[A\033[%uG", new_x); b_tty_move_cursor_y(ed->l_tty, B_TTY_POS_CURSOR, -1); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_START, new_x); } /* now reprint all of the buffer lines, starting with the first of the * two lines that were concatenated. */ size_t ydiff = 0; while (1) { print_text(ed, start_x, ed->l_cursor_y + ydiff, line_buf + start_x); line_buf += line_len + 1; line_len = strcspn(line_buf, "\n"); start_x = 0; if (*line_buf == '\0') { break; } fputc('\r', stdout); fputc('\n', stdout); ydiff++; } /* finally, move the cursor BACK to the point where the two lines * were concatenated. */ if (ydiff) { //fprintf(stdout, "\033[%uA", ydiff); b_tty_move_cursor_y(ed->l_tty, B_TTY_POS_CURSOR, ydiff); } //fprintf(stdout, "\033[%uG", new_x); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_START, new_x); fflush(stdout); } /* this function is called after a character is removed from the line_ed buffer. * IF the character was not a linefeed. */ void backspace_simple_refresh(struct line_ed *ed, struct refresh_state *state) { /* get the data for the line that is being refreshed */ const char *line_buf = line_start(ed, ed->l_cursor_y); size_t line_len = strcspn(line_buf, "\n"); /* the index of the first char in line_buf that needs to be reprinted */ size_t start_x = ed->l_cursor_x; //get_data_cursor_position(ed, &start_x, NULL); /* the distance between the first char to be reprinted and the end * of the line. * the physical cursor will be moved back by this amount after the * line is reprinted. */ long long cursor_rdelta = (long long)(line_len - start_x); if (ed->l_flags & LINE_ED_FULL_REPRINT) { if (start_x) { //fprintf(stdout, "\033[%uD", start_x); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_CURSOR, -(long long)start_x); } start_x = 0; } //fputc('\010', stdout); b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_CURSOR, -1); print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x); fputc(' ', stdout); /* increment the rdelta (move the cursor back one more cell), so * that the cursor will appear to move back one cell (to accord with * the fact that backspace was just pressed) */ cursor_rdelta++; b_tty_move_cursor_x(ed->l_tty, B_TTY_POS_CURSOR, -cursor_rdelta); #if 0 for (unsigned int i = 0; i < cursor_rdelta; i++) { fputs("\010", stdout); } #endif fflush(stdout); }