#include #include #include #include #include "font.h" #include "view.h" #ifndef alloca #define alloca _alloca #endif void Formatter::update_highlighter(File *file, int64_t offset, char c) { } void Grid::render_into(File *file, Cell *cells, Formatter *formatter, Input_State& input, int wnd_width, int wnd_height) { int line_num_gap = 0; int total_line_num_gap = 0; int ln_digit_width = 0; { int n_digits = 0; int64_t n = row_offset + (int64_t)rows; // no -1 since line numbers are 1-indexed if (n > 0) { while (n) { n /= 10; n_digits++; } line_num_gap = n_digits + 3; } } if (line_num_gap < 7) line_num_gap = 7; total_line_num_gap = line_num_gap; if (line_num_gap > cols) line_num_gap = cols; ln_digit_width = total_line_num_gap - 3; this->last_line_num_gap = total_line_num_gap; char *ln_buf = (char*)alloca(ln_digit_width + 1); ln_buf[ln_digit_width] = 0; uint32_t default_bg = formatter->colors[0]; uint32_t hl_color = formatter->colors[2]; bool hl = ((primary_cursor < grid_offset && secondary_cursor >= grid_offset) || (primary_cursor >= grid_offset && secondary_cursor < grid_offset)); Cell line_num_cell = { .foreground = formatter->colors[3], .background = formatter->colors[4] }; Cell empty = { .background = hl ? hl_color : default_bg }; this->rel_caret_col = -1; this->rel_caret_row = -1; int mouse_col = input.column - line_num_gap; bool mouse_held = (input.left_flags & 1) != 0; bool mouse_was_held = (input.left_flags & 2) != 0; if (mouse_held && !mouse_was_held && mouse_col >= 0 && input.x < wnd_width - THUMB_WIDTH) this->text_held = true; if (!mouse_held) this->text_held = false; int idx = 0; int64_t offset = grid_offset; int64_t new_cursor = primary_cursor; int64_t text_cols = cols - line_num_gap; formatter->cur_mode = mode_at_current_line; char *data = file->data; int64_t total_size = file->total_size; int line = 0; for ( ; line < rows && offset <= total_size; line++) { if (total_size > 0 && offset == total_size && data[offset-1] != '\n') break; int64_t n = row_offset + (int64_t)line + 1; // +1 since line numbers are 1-indexed for (int i = ln_digit_width-1; i >= 0; i--) { if (n > 0) { ln_buf[i] = '0' + (char)(n % 10); n /= 10; } else ln_buf[i] = ' '; } line_num_cell.glyph = 0; cells[idx] = line_num_cell; for (int i = 0; i < line_num_gap-1; i++) { line_num_cell.glyph = i < ln_digit_width ? (uint32_t)(ln_buf[i] - ' ') : 0; cells[idx + i+1] = line_num_cell; } if (line_num_gap >= cols || offset >= total_size) { empty.background = hl ? hl_color : default_bg; for (int j = 0; j < text_cols; j++) cells[idx + line_num_gap + j] = empty; idx += cols; if (offset >= total_size) break; continue; } int64_t vis_cols = 0; bool early_bail = false; bool cursor_set = false; while (vis_cols < col_offset) { char c = data[offset]; formatter->update_highlighter(file, offset, c); if (primary_cursor != secondary_cursor && (offset == primary_cursor || offset == secondary_cursor)) hl = !hl; offset++; if (c == '\n') { early_bail = true; break; } else if (c == '\t') vis_cols += spaces_per_tab - (vis_cols % spaces_per_tab); else vis_cols++; } if (early_bail) { empty.background = hl ? hl_color : default_bg; for (int j = 0; j < text_cols; j++) cells[idx + line_num_gap + j] = empty; idx += cols; continue; } // If we reached a tab character that spans over the given column offset int column = 0; int leading_cols = (int)(vis_cols - col_offset); if (text_held && input.row == line && mouse_col >= 0 && mouse_col < leading_cols) { new_cursor = offset; hl = !hl; cursor_set = true; } empty.background = hl ? hl_color : default_bg; for (column = 0; column < leading_cols; column++) cells[line_num_gap + idx + column] = empty; while (column < text_cols && offset < total_size) { char c = data[offset]; formatter->update_highlighter(file, offset, c); int n_spaces = c == '\t' ? spaces_per_tab - (((int)col_offset + column) % spaces_per_tab) : 1; if (text_held && input.row == line && mouse_col >= column && mouse_col < column + n_spaces) { new_cursor = offset; cursor_set = true; } if (offset == new_cursor) { rel_caret_col = column; rel_caret_row = line; } if (primary_cursor != secondary_cursor && (offset == primary_cursor || offset == secondary_cursor)) hl = !hl; if (c == '\t') { for (int j = 0; j < n_spaces; j++) { cells[line_num_gap + idx + column] = empty; column++; } offset++; continue; } if (c == '\n') break; offset++; if (c < ' ' || c > '~') c = 0x7f; uint32_t fg, bg, glyph_off, modifier; formatter->get_current_attrs(fg, bg, glyph_off, modifier); if (hl) bg = hl_color; cells[line_num_gap + idx + column] = { .glyph = (uint32_t)(c - ' ') + glyph_off, .modifier = modifier, .foreground = fg, .background = bg }; column++; } // target_cursor_col is intentionally not updated here if (text_held && input.row == line && !cursor_set) { new_cursor = offset; rel_caret_col = column; rel_caret_row = line; } empty.background = hl ? hl_color : default_bg; for (int j = column; j < text_cols; j++) cells[line_num_gap + idx + j] = empty; idx += cols; if (offset < total_size) { char c = 0; do { c = data[offset]; formatter->update_highlighter(file, offset, c); offset++; } while (c != '\n'); } } this->end_grid_offset = offset; this->primary_cursor = new_cursor; //if (!input.should_hl) secondary_cursor = primary_cursor if (text_held && !mouse_was_held) secondary_cursor = primary_cursor; int grid_size = rows * cols; for (int i = idx; i < grid_size; i++) cells[i] = empty; } void Grid::move_cursor_vertically(File *file, int dir, int target_col) { char *data = file->data; int64_t size = file->total_size; int64_t spt_64 = (int64_t)spaces_per_tab; int64_t offset = primary_cursor; if (dir > 0) { for (int i = 0; i < dir; i++) { while (offset < size) { char c = data[offset++]; if (c == '\n') break; } if (offset >= size) { primary_cursor = size; return; } } } else { int n = -dir + 1; for (int i = 0; i < n; i++) { while (offset > 0) { char c = data[--offset]; if (c == '\n') break; } } if (offset > 0) offset++; } int64_t col = 0; while (col < target_col && offset < size) { char c = data[offset]; if (c == '\n') break; col += c == '\t' ? spt_64 - (col % spt_64) : 1; offset++; } primary_cursor = offset; } void Grid::adjust_offsets(File *file, int64_t move_down, int64_t move_right) { char *data = file->data; int64_t size = file->total_size; if (!data || size <= 0) return; col_offset += move_right; if (col_offset < 0) col_offset = 0; int64_t old_row_off = row_offset; row_offset += move_down; if (row_offset <= 0) { row_offset = 0; grid_offset = 0; return; } int64_t r = old_row_off; int64_t off = grid_offset; if (row_offset > old_row_off) { while (r < row_offset && off < size) { if (data[off++] == '\n') { grid_offset = off; r++; } } row_offset = r; } else if (row_offset < old_row_off) { int64_t r = old_row_off; int64_t off = grid_offset; while (r >= row_offset && off > 0) { if (data[--off] == '\n') { grid_offset = off; r--; } } grid_offset = off + 1; row_offset = r + 1; } } int64_t Grid::jump_to_offset(File *file, int64_t offset, int flags) { if (!rows) return 0; if (offset < 0) offset = 0; if (offset > file->total_size) offset = file->total_size; int64_t row = 0; int64_t col = 0; int64_t rows_64 = (int64_t)rows; int64_t cols_64 = (int64_t)cols; int64_t spt_64 = (int64_t)spaces_per_tab; char *data = file->data; int64_t size = file->total_size; if (offset < grid_offset) { int64_t off = grid_offset; int64_t lines_up = 0; while (off > offset) { char c = data[--off]; if (c == '\n') { lines_up++; } } row_offset -= lines_up; off = offset; while (off > 0) { char c = data[--off]; if (c == '\n') { off++; break; } // this is likely wrong if (c == '\t') col += spt_64 - (col % spt_64); else col++; } grid_offset = off; } else { int64_t off = grid_offset; int64_t line_offset = grid_offset; int64_t lines_down = 0; int64_t *offset_list = (int64_t*)alloca(rows * sizeof(int64_t)); offset_list[0] = grid_offset; while (off < offset) { char c = data[off++]; if (c == '\n') { lines_down++; offset_list[lines_down % rows_64] = off; line_offset = off; col = 0; } else if (c == '\t') col += spt_64 - (col % spt_64); else col++; } if (flags & JUMP_FLAG_TOP) { grid_offset = line_offset; row_offset += lines_down; } else if (lines_down >= rows_64) { grid_offset = offset_list[(lines_down + 1) % rows_64]; row_offset += lines_down - rows_64 + 1; } } if (flags & JUMP_FLAG_AFFECT_COLUMN) { col_offset = 0; int64_t mid = cols_64 / 2; if (col >= cols_64) col_offset = col - mid; } //primary_cursor = offset; return offset; }