/*
- * Copyright Neil Brown ©2016-2021 <neil@brown.name>
+ * Copyright Neil Brown ©2016-2023 <neil@brown.name>
* May be distributed under terms of GPLv2 - see file:COPYING
*
* This module provides render-line and render-line-prev, making use of
#include <string.h>
#include <stdio.h>
-
+#define PANE_DATA_TYPE struct mu_info
#include "core.h"
#include "misc.h"
struct mu_info {
int view;
};
+#include "core-pane.h"
static struct map *mu_map safe;
-#define LARGE_LINE 1000
+#define LARGE_LINE 5000
+
+static int is_render_eol(wint_t ch, struct pane *p safe, struct mark *m safe)
+{
+ char *attr;
+ if (!is_eol(ch))
+ return 0;
+ attr = pane_mark_attr(p, m, "markup:not_eol");
+ if (attr && *attr)
+ return 0;
+ return 1;
+}
DEF_CMD(render_prev)
{
struct pane *f = ci->focus;
struct mu_info *mu = ci->home->data;
struct mark *boundary = NULL;
+ struct mark *doc_boundary = NULL;
int count = 0;
int rpt = RPT_NUM(ci);
wint_t ch;
if (!m)
return Enoarg;
+ if (!rpt) {
+ boundary = vmark_at_or_before(f, m, mu->view, ci->home);
+ doc_boundary = call_ret(mark, "doc:get-boundary", f, -1, m);
+ }
while ((ch = doc_prev(f, m)) != WEOF &&
- (!is_eol(ch) || rpt > 0) &&
+ (!is_render_eol(ch, f, m) || rpt > 0) &&
count < LARGE_LINE &&
- (!boundary || boundary->seq< m->seq)) {
- rpt = 0;
- if (!count)
+ (!boundary || mark_ordered_not_same(boundary, m)) &&
+ (!doc_boundary || mark_ordered_not_same(doc_boundary, m))) {
+ if (rpt) {
boundary = vmark_at_or_before(f, m, mu->view, ci->home);
+ doc_boundary = call_ret(mark, "doc:get-boundary", f, -1, m);
+ }
+ rpt = 0;
count += 1;
}
- if (ch != WEOF && !is_eol(ch)) {
- /* need to ensure there is a stable boundary here */
- if (!boundary || boundary->seq >= m->seq) {
+ if (ch != WEOF && !is_render_eol(ch, f, m) &&
+ (!doc_boundary || !mark_same(doc_boundary, m))) {
+ /* Just crossed the boundary, or the max count.
+ * Need to step back, and ensure there is a stable boundary
+ * here.
+ */
+ mark_free(doc_boundary);
+ doc_next(f, m);
+ if (!boundary || !mark_same(boundary, m)) {
boundary = vmark_new(f, mu->view, ci->home);
if (boundary)
mark_to_mark(boundary, m);
}
return 1;
}
+ mark_free(doc_boundary);
if (ch == WEOF && rpt)
return Efail;
/* Found a '\n', so step forward over it for start-of-line. */
- if (is_eol(ch))
+ if (is_render_eol(ch, f, m))
doc_next(f, m);
return 1;
}
* above it in the stack and push them onto tmpst, which is then in
* reverse priority order. As we do that, we count them in 'popped'.
* Changes can be made in the secondary stack.
- * When all change have been made, we add 'popped' "</>" marked to the output,
+ * When all change have been made, we add 'popped' ETX marked to the output,
* then process everything in 'tmpst', either discarding it if end<=chars, or
* outputting the attributes and pushing back on 'ast'.
*/
struct attr_stack *next;
char *attr safe;
int end;
- short priority;
+ unsigned short priority;
} *ast, *tmpst;
int min_end;
int chars;
+ struct buf insert;
short popped;
};
struct attr_stack *to = ar->ast;
while (ar->popped > 0) {
- buf_concat(b, "</>");
+ buf_append(b, etx);
ar->popped -= 1;
}
free(from->attr);
free(from);
} else {
- buf_append(b, '<');
+ buf_append(b, soh);
buf_concat(b, from->attr);
- buf_append(b, '>');
+ buf_append(b, stx);
from->next = to;
to = from;
if (from->end < ar->min_end)
new = calloc(1, sizeof(*new));
new->next = *here;
new->attr = strdup(attr);
- if (INT_MAX - end <= ar->chars)
+ if (end == 0 || INT_MAX - end <= ar->chars)
end = INT_MAX - 1 - ar->chars;
new->end = ar->chars + end;
new->priority = prio;
}
static void as_clear(struct attr_return *ar safe,
- int prio, const char *attr safe)
+ int prio, const char *attr)
{
struct attr_stack *st;
as_pop(ar, 1);
for (st = ar->tmpst; st && st->priority <= prio; st = st->next)
- if (st->priority == prio && strcmp(st->attr, attr) == 0)
+ if (st->priority == prio &&
+ (attr == NULL || strcmp(st->attr, attr) == 0))
st->end = ar->chars;
}
DEF_CB(text_attr_callback)
{
- struct attr_return *ar = container_of(ci->comm, struct attr_return, rtn);
- if (!ci->str)
- return Enoarg;
- if (ci->num >= 0)
- as_add(ar, ci->num, ci->num2, ci->str);
- else
- as_clear(ar, ci->num2, ci->str);
- // FIXME ->str2 should be inserted
+ struct attr_return *ar = container_of(ci->comm, struct attr_return,
+ rtn);
+ int prio = ci->num2;
+ if (prio < 0)
+ prio = 0;
+ if (prio > 65535)
+ prio = 65535;
+ if (ci->num >= 0) {
+ if (ci->str)
+ as_add(ar, ci->num, prio, ci->str);
+ } else
+ as_clear(ar, prio, ci->str);
+ if (ci->str2) {
+ const char *c = ci->str2;
+ wint_t wch;
+ while ((wch = get_utf8(&c, NULL)) != WEOF)
+ buf_append(&ar->insert, wch);
+ }
return 1;
}
const char *val;
while ((key = attr_get_next_key(m->attrs, key, -1, &val)) != NULL &&
- strncmp(key, "render:", 7) == 0)
+ strstarts(key, "render:"))
call_comm("map-attr", f, &ar->rtn, 0, m, key, 0, NULL, val);
}
DEF_CMD(render_line)
{
/* Render the line from 'mark' to the first '\n' or until
- * 'num2' chars.
- * Convert '<' to '<<' and if a char has the 'highlight' attribute,
- * include that between '<>'.
+ * 'num' chars.
*/
struct buf b;
struct pane *focus = ci->focus;
struct mu_info *mu = ci->home->data;
struct mark *m = ci->mark;
struct mark *pm = ci->mark2; /* The location to render as cursor */
- struct mark *boundary;
+ struct mark *boundary, *start_boundary = NULL;
+ struct mark *doc_boundary;
int o = ci->num;
+ int pm_offset = -1;
wint_t ch;
int chars = 0;
int ret;
char *noret;
char *attr;
- if (o == NO_NUMERIC)
- o = -1;
-
ar.rtn = text_attr_callback;
ar.fwd = text_attr_forward;
ar.ast = ar.tmpst = NULL;
ar.min_end = -1;
ar.chars = 0;
+ buf_init(&ar.insert);
ar.popped = 0;
if (!m)
return Efail;
if ((attr = pane_mark_attr(focus, m, "markup:func")) != NULL) {
/* An alternate function handles this line */
- ret = call_comm(attr, focus, ci->comm2, o, m, NULL, ci->num2, pm);
+ ret = call_comm(attr, focus, ci->comm2, o, m, NULL, 0, pm);
if (ret)
return ret;
}
boundary = vmark_at_or_before(focus, m, mu->view, ci->home);
- if (boundary)
+ if (boundary) {
+ if (mark_same(m, boundary))
+ start_boundary = boundary;
boundary = vmark_next(boundary);
+ }
+ doc_boundary = call_ret(mark, "doc:get-boundary", focus, 1, m);
+
buf_init(&b);
+ /* Assert that '<' are not quoted */
+ buf_append(&b, ack);
call_comm("map-attr", focus, &ar.rtn, 0, m, "start-of-line");
+ if (ar.insert.len) {
+ buf_concat(&b, buf_final(&ar.insert));
+ buf_reinit(&ar.insert);
+ }
while (1) {
struct mark *m2;
+ int is_true_eol = 0;
if (o >= 0 && b.len >= o)
break;
- if (pm && mark_same(m, pm))
+
+ if (boundary && mark_ordered_or_same(boundary, m))
+ break;
+ if (doc_boundary && mark_ordered_or_same(doc_boundary, m))
break;
+ if (pm && mark_same(m, pm) && pm_offset < 0)
+ pm_offset = b.len;
+
if (ar.ast && ar.min_end <= chars) {
int depth = find_finished(ar.ast, chars, &ar.min_end);
as_pop(&ar, depth);
if (o >= 0 && b.len >= o)
break;
+ if (ar.insert.len) {
+ buf_concat(&b, buf_final(&ar.insert));
+ buf_reinit(&ar.insert);
+ }
+
ch = doc_next(focus, m);
if (ch == WEOF)
break;
+
if (!oneline && is_eol(ch)) {
- add_newline = 1;
- break;
+ doc_prev(focus, m);
+ is_true_eol = is_render_eol(ch, focus, m);
+ doc_next(focus, m);
}
- if (boundary && boundary->seq <= m->seq)
+ if (is_true_eol) {
+ add_newline = 1;
break;
- if (ch == '<') {
- if (o >= 0 && b.len+1 >= o) {
- doc_prev(focus, m);
- break;
- }
- buf_append(&b, '<');
}
+ chars++;
if (ch == '\r' && noret) {
/* do nothing */
- } else if (ch < ' ' && ch != '\t' && (oneline || !is_eol(ch))) {
- buf_concat(&b, "<fg:red>^");
+ } else if (ch < ' ' && ch != '\t') {
+ buf_concat(&b, SOH "fg:red" STX "^");
buf_append(&b, '@' + ch);
- buf_concat(&b, "</>");
+ buf_concat(&b, ETX);
} else if (ch == 0x7f) {
- buf_concat(&b, "<fg:red>^?</>");
+ buf_concat(&b, SOH "fg:red" STX "^?" ETX);
} else if (ch >= 0x80 && iswcntrl(ch)) {
/* Extra unicode control */
- buf_concat(&b, "<fg:magenta>^");
+ buf_concat(&b, SOH "fg:magenta" STX "^");
buf_append(&b, 96 + (ch & 0x1f));
- buf_concat(&b, "</>");
+ buf_concat(&b, ETX);
} else
buf_append(&b, ch);
- chars++;
}
if (add_newline && want_vis_newline(ar.ast))
buf_concat(&b, "↩");
buf_append(&b, '\n');
}
- ret = comm_call(ci->comm2, "callback:render", focus, 0, NULL,
+ if (start_boundary && chars < LARGE_LINE - 5)
+ /* This boundary is no longer well-placed. */
+ mark_free(start_boundary);
+
+ mark_free(doc_boundary);
+
+ if (pm && pm_offset < 0)
+ pm_offset = b.len;
+
+ ret = comm_call(ci->comm2, "callback:render", focus, pm_offset, NULL,
buf_final(&b));
free(b.b);
+ free(ar.insert.b);
return ret ?: 1;
}
struct pane *ret;
struct mu_info *mu;
- alloc(mu, pane);
- ret = pane_register(p, 0, &markup_handle.c, mu);
+ ret = pane_register(p, 0, &markup_handle.c);
if (!ret)
return NULL;
+ mu = ret->data;
mu->view = home_call(p, "doc:add-view", ret) - 1;
return ret;
key_add(mu_map, "doc:render-line", &render_line);
key_add(mu_map, "doc:render-line-prev", &render_prev);
key_add(mu_map, "Clone", &mu_clone);
- key_add(mu_map, "Free", &edlib_do_free);
key_add(mu_map, "Notify:clip", &mu_clip);
call_comm("global-set-command", ed, &markup_attach,