Files
ivy/frontend/line-ed/hl-range.c

287 lines
6.2 KiB
C

#include "hl-range.h"
#include "line-ed.h"
#include "prompt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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);
}
}