287 lines
6.2 KiB
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);
|
|
}
|
|
}
|