#include "hl-range.h" #include "line-ed.h" #include "prompt.h" #include #include #include int compare_coords(size_t ax, size_t ay, size_t bx, size_t by) { if (ay > by) { return 1; } if (ay < by) { return -1; } if (ax > bx) { return 1; } if (ax < bx) { return -1; } return 0; } struct hl_range *get_hl_range(struct line_ed *ed, size_t x, size_t y) { if (b_queue_empty(&ed->l_hl_ranges)) { return NULL; } struct hl_range *best_match = NULL; b_queue_entry *entry = b_queue_first(&ed->l_hl_ranges); while (entry) { struct hl_range *cur = b_unbox(struct hl_range, entry, h_entry); int cmp_end = compare_coords(x, y, cur->h_end_x, cur->h_end_y); if (cmp_end != 1) { return cur; } entry = b_queue_next(entry); } return NULL; } struct hl_range *get_next_hl_range(struct hl_range *range) { b_queue_entry *entry = &range->h_entry; entry = b_queue_next(entry); if (!entry) { return NULL; } range = b_unbox(struct hl_range, entry, h_entry); return range; } int apply_hl_range(struct hl_range *range, b_tty *tty, size_t x, size_t y) { if (!range) { b_tty_reset_vmode(tty); return 0; } int cmp_start = compare_coords(x, y, range->h_start_x, range->h_start_y); int cmp_end = compare_coords(x, y, range->h_end_x, range->h_end_y); if (cmp_start < 0) { b_tty_reset_vmode(tty); } if (cmp_start >= 0 && cmp_end <= 0) { b_tty_set_vmode(tty, &range->h_vmode); } if (cmp_end == 1) { return -1; } return 0; } struct hl_range *create_highlight( size_t start_x, size_t start_y, size_t end_x, size_t end_y, const b_tty_vmode *vmode) { struct hl_range *out = malloc(sizeof *out); if (!out) { return NULL; } memset(out, 0x0, sizeof *out); out->h_start_x = start_x; out->h_start_y = start_y; out->h_end_x = end_x; out->h_end_y = end_y; memcpy(&out->h_vmode, vmode, sizeof *vmode); return out; } enum hl_range_comparison compare_hl_ranges( const struct hl_range *a, const struct hl_range *b) { int cmp_a_end_b_start = compare_coords( a->h_end_x, a->h_end_y, b->h_start_x, b->h_start_y); if (cmp_a_end_b_start == -1) { return HL_RANGE_LESS; } int cmp_b_end_a_start = compare_coords( b->h_end_x, b->h_end_y, a->h_start_x, a->h_start_y); if (cmp_b_end_a_start == -1) { return HL_RANGE_GREATER; } /* A end IS greater than B start * B end IS greater than A start */ int cmp_a_start_b_start = compare_coords( a->h_start_x, a->h_start_y, b->h_start_x, b->h_start_y); int cmp_a_end_b_end = compare_coords(a->h_end_x, a->h_end_y, b->h_end_x, b->h_end_y); switch (cmp_a_start_b_start) { case 1: return cmp_a_end_b_end <= 0 ? HL_RANGE_A_IN_B : HL_RANGE_GREATER_OVERLAP; case 0: switch (cmp_a_end_b_end) { case 1: return HL_RANGE_B_IN_A; case 0: return HL_RANGE_EQUAL; case -1: return HL_RANGE_A_IN_B; default: break; } case -1: return cmp_a_end_b_end >= 0 ? HL_RANGE_B_IN_A : HL_RANGE_LESS_OVERLAP; default: break; } /* should never reach here */ return HL_RANGE_EQUAL; } static void move_end_to_meet_start( struct hl_range *move_end_of, struct hl_range *to_meet_start_of) { move_end_of->h_end_y = to_meet_start_of->h_start_y; if (to_meet_start_of->h_start_x == 0) { move_end_of->h_end_y--; move_end_of->h_end_x = (unsigned int)-1; } else { move_end_of->h_end_x = to_meet_start_of->h_start_x - 1; } } static void move_start_to_meet_end( struct hl_range *move_start_of, struct hl_range *to_meet_end_of) { move_start_of->h_end_y = to_meet_end_of->h_end_y; move_start_of->h_start_x = to_meet_end_of->h_end_x + 1; } void line_ed_put_highlight( struct line_ed *ed, unsigned long start_x, unsigned long start_y, unsigned long end_x, unsigned long end_y, const struct b_tty_vmode *vmode) { struct hl_range *highlight = create_highlight(start_x, start_y, end_x, end_y, vmode); if (!highlight) { return; } struct hl_range *h2 = NULL; b_queue_entry *entry = NULL; entry = b_queue_first(&ed->l_hl_ranges); if (!entry) { b_queue_push_back(&ed->l_hl_ranges, &highlight->h_entry); return; } struct hl_range *cur = NULL; enum hl_range_comparison prev_cmp = -1; b_queue_entry *insert_before = NULL; b_queue_entry *insert_after = NULL; bool end = false; while (entry) { b_queue_entry *next = b_queue_next(entry); cur = b_unbox(struct hl_range, entry, h_entry); enum hl_range_comparison cmp = compare_hl_ranges(cur, highlight); switch (cmp) { case HL_RANGE_A_IN_B: b_queue_delete(&ed->l_hl_ranges, entry); free(cur); break; case HL_RANGE_B_IN_A: move_end_to_meet_start(cur, highlight); h2 = create_highlight( 0, 0, cur->h_end_x, cur->h_end_y, &cur->h_vmode); move_start_to_meet_end(h2, highlight); b_queue_insert_after( &ed->l_hl_ranges, &highlight->h_entry, &cur->h_entry); b_queue_insert_after( &ed->l_hl_ranges, &h2->h_entry, &highlight->h_entry); insert_before = insert_after = NULL; end = true; break; case HL_RANGE_LESS_OVERLAP: move_end_to_meet_start(cur, highlight); case HL_RANGE_LESS: insert_after = entry; break; case HL_RANGE_GREATER_OVERLAP: move_start_to_meet_end(cur, highlight); insert_before = entry; break; case HL_RANGE_GREATER: b_queue_insert_before( &ed->l_hl_ranges, &highlight->h_entry, entry); insert_before = insert_after = NULL; end = true; break; default: break; } entry = next; if (end) { break; } } if (insert_before) { b_queue_insert_before( &ed->l_hl_ranges, &highlight->h_entry, insert_before); } else if (insert_after) { b_queue_insert_after( &ed->l_hl_ranges, &highlight->h_entry, insert_after); } } void line_ed_clear_highlights(struct line_ed *ed) { b_queue_entry *entry = b_queue_pop_front(&ed->l_hl_ranges); while (entry) { struct hl_range *range = b_unbox(struct hl_range, entry, h_entry); free(range); entry = b_queue_pop_front(&ed->l_hl_ranges); } } void line_ed_print_highlights(struct line_ed *ed) { b_queue_entry *entry = b_queue_first(&ed->l_hl_ranges); while (entry) { struct hl_range *h = b_unbox(struct hl_range, entry, h_entry); printf("(%zu, %zu) -> (%zu, %zu)\n", h->h_start_x, h->h_start_y, h->h_end_x, h->h_end_y); entry = b_queue_next(entry); } }