From: NeilBrown Date: Wed, 21 Jun 2023 07:48:57 +0000 (+1000) Subject: lib-renderline: expect control chars instead of <> for attributes. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=2470bb9a2e7d87509afadb876cdacb2bd31b78e4;p=edlib.git lib-renderline: expect control chars instead of <> for attributes. Using <> to distinguish attributes is clumsy as it requires that '<' be quoted. We already quote control characters, making them ^X so use control characters for distinguishing attributes. SOH (\1) marks start of attrs STX (\2) marks end of attrs, start of text ETX (\3) marks end of the text for those attrs. ACK (\6) is used as a filler when needed. A string that start \6 or \1 is assumed not to contain meaningful <> Otherwise we translate to remain compatible with old code. Signed-off-by: NeilBrown --- diff --git a/DOC/TODO.md b/DOC/TODO.md index 93504574..5e305a88 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -58,7 +58,7 @@ Requirements for a v1.0 release Maybe a pencil in a window pane - [ ] configuration - [ ] vi mode -- [ ] office mode +- [ ] CUA mode - [ ] nano mode(?) - [ ] multiple front ends: elvi, elma, elnm, eled? - [ ] introspection @@ -803,7 +803,7 @@ Module features New Modules ----------- -- [ ] separate out from mode-emacs: +- [ ] separate out CUA-base from mode-emacs: - selection management - mouse management @@ -823,7 +823,10 @@ New Modules - numeric prefix - much more -- [ ] "office" mode +- [ ] "CUA" mode + + - "extra" on CUA-base which might conflict with emacs/vi, including + control-letter and function keys. - C-c for copy, C-x for cut, C-v for paste, - C-a select all diff --git a/lib-renderline.c b/lib-renderline.c index b14fc8c0..d238bf0f 100644 --- a/lib-renderline.c +++ b/lib-renderline.c @@ -367,11 +367,8 @@ static void update_line_height(struct pane *p safe, struct pane *focus safe, while (*line) { char c = *line++; const char *st = line; - if (c == '<' && *line == '<') { - line += 1; - continue; - } - if (c != '<') + + if (c != soh && c != etx && c != ack) continue; if (line - 1 > segstart) { @@ -381,14 +378,16 @@ static void update_line_height(struct pane *p safe, struct pane *focus safe, buf_final(&attr), l, scale); free(l); } - while (*line && line[-1] != '>') - line += 1; - segstart = line; - if (st[0] != '/') { + if (c == soh) { char *c2; char *b; const char *aend; + /* move 'line' over the attrs */ + while (*line && line[-1] != stx) + line += 1; + segstart = line; + /* attrs must not contain ",," */ aend = strstr(st, ",,"); if (aend) @@ -397,9 +396,9 @@ static void update_line_height(struct pane *p safe, struct pane *focus safe, aend = line; buf_concat_len(&attr, st, aend-st); - /* Replace trailing '>' with ',', and append ',' + /* Replace trailing 'stx' with ',', and append ',' * so ",," marks where to strip back to when we - * find . + * find etx */ attr.b[attr.len-1] = ','; buf_append(&attr, ','); @@ -419,7 +418,8 @@ static void update_line_height(struct pane *p safe, struct pane *focus safe, attr_found = 1; update_line_height_attr(p, focus, h, a, w, b, "", scale); - } else { + } else if (c == etx) { + segstart = line; /* strip back to ",," */ if (attr.len >= 2) attr.len -= 2; @@ -427,6 +427,8 @@ static void update_line_height(struct pane *p safe, struct pane *focus safe, (attr.b[attr.len] != ',' || attr.b[attr.len+1] != ',')) attr.len -= 1; + } else if (c == ack) { + segstart = line; } } if (line > segstart && line[-1] == '\n') @@ -495,11 +497,11 @@ static int render_image(struct pane *p safe, struct pane *focus safe, width = p->parent->w/2; height = p->parent->h/2; - while (*line == '<') + while (*line == soh) line += 1; - while (*line && *line != '>') { - int len = strcspn(line, ",>"); + while (*line && *line != stx && *line != etx) { + int len = strcspn(line, "," STX ETX); if (strstarts(line, "image:")) { fname = strndup(line+6, len-6); @@ -689,7 +691,7 @@ DEF_CMD(renderline) if (dodraw) home_call(focus, "Draw:clear", p); - if (strstarts(line, " must be on a line by itself. * Maybe this can be changed later if I decide on * something that makes sense. @@ -792,7 +794,7 @@ DEF_CMD(renderline) } if ((ret == WRAP || x >= p->w - mwidth) && - (line[0] != '<' || line[1] == '<')) { + line[0] != soh && line[0] != ack) { /* No room for more text */ if (wrap && *line && *line != '\n') { int wrap_prefix_size; @@ -830,7 +832,7 @@ DEF_CMD(renderline) ch = *line; if (line == line_start + offset) rd->curs_width = mwidth; - if (ch >= ' ' && ch != '<') { + if (ch >= ' ') { bool was_in_lws = in_lws; line += 1; /* Only flush out if string is getting a bit long. @@ -885,70 +887,63 @@ DEF_CMD(renderline) start = line; if (ret != OK || !ch) continue; - if (ch == '<') { - line += 1; - if (*line == '<') { - in_lws = False; - ret = draw_some(p, focus, &rlst, &x, start, &line, - buf_final(&attr), in_lws, - wrap ? mwidth : 0, - in_tab ?:offset - (start - line_start), - posx, scale); - if (ret != OK) - continue; - start += 2; - line = start; - } else { - const char *a = line; - - while (*line && line[-1] != '>') - line += 1; - - if (a[0] != '/') { - int ln = attr.len; - char *tb; - const char *aend; - - /* attrs must not contain ",," */ - aend = strstr(a, ",,"); - if (aend) - aend += 1; - if (!aend || line < aend) - aend = line; - - buf_concat_len(&attr, a, aend-a); - /* Replace trailing '>' with ',', and - * append ',' so ",," marks where to - * strip back to when we find . - */ - attr.b[attr.len-1] = ','; - buf_append(&attr, ','); - tb = strstr(buf_final(&attr)+ln, - "tab:"); - if (tb) - x = margin + - atoi(tb+4) * scale / 1000; - } else { - /* strip back to ",," */ - if (attr.len > 0) - attr.len -= 2; - while (attr.len >=2 && - (attr.b[attr.len-1] != ',' || - attr.b[attr.len-2] != ',')) - attr.len -= 1; - if (attr.len == 1) - attr.len = 0; - } - if (offset == start - line_start) - offset += line-start; - start = line; - mwidth = -1; - } - continue; - } - line += 1; - if (ch == '\n') { + switch (ch) { + case soh: { + const char *a = line; + char *tb; + const char *aend; + int ln; + + while (*line && line[-1] != stx) + line += 1; + + ln = attr.len; + + /* attrs must not contain ",," */ + aend = strstr(a, ",,"); + if (aend) + aend += 1; + if (!aend || line < aend) + aend = line; + + buf_concat_len(&attr, a, aend-a); + /* Replace trailing stx with ',', and + * append ',' so ",," marks where to + * strip back to when we find etx. + */ + attr.b[attr.len-1] = ','; + buf_append(&attr, ','); + tb = strstr(buf_final(&attr)+ln, + "tab:"); + if (tb) + x = margin + + atoi(tb+4) * scale / 1000; + if (offset == start - line_start) + offset += line-start; + start = line; + mwidth = -1; + break; + } + case etx: + /* strip back to ",," */ + if (attr.len > 0) + attr.len -= 2; + while (attr.len >=2 && + (attr.b[attr.len-1] != ',' || + attr.b[attr.len-2] != ',')) + attr.len -= 1; + if (attr.len == 1) + attr.len = 0; + if (offset == start - line_start) + offset += line-start; + start = line; + mwidth = -1; + break; + case ack: + start = line; + break; + case '\n': xypos = line-1; flush_line(p, focus, dodraw, &rlst, y+ascent, scale, 0, &wrap_margin, NULL, &xypos, &xyattr, &cursattr); @@ -956,12 +951,14 @@ DEF_CMD(renderline) x = 0; wrap_offset = 0; start = line; - } else if (ch == '\f') { + break; + case '\f': x = 0; start = line; wrap_offset = 0; end_of_page = 1; - } else if (ch == '\t') { + break; + case '\t': { int xc = (wrap_offset + x) / mwidth; /* Note xc might be negative, so "xc % 8" won't work here */ int w = 8 - (xc & 7); @@ -981,7 +978,9 @@ DEF_CMD(renderline) } else in_tab = 0; start = line; - } else { + break; + } + default:{ char buf[4]; const char *b; int l = attr.len; @@ -998,6 +997,8 @@ DEF_CMD(renderline) posx, scale); attr.len = l; start = line; + break; + } } } if (!*line && (line > start || offset == start - line_start)) { @@ -1089,6 +1090,39 @@ DEF_CMD(renderline_get) return 1; } +static char *cvt(char *str safe) +{ + /* Convert: + * << to < ack (ack is a no-op) + * < stuff > to soh stuff stx + * to ack ack etx + */ + char *c; + for (c = str; *c; c += 1) { + if (c[0] == soh || c[0] == ack) + break; + if (c[0] == '<' && c[1] == '<') { + c[1] = ack; + c++; + continue; + } + if (c[0] != '<') + continue; + if (c[1] == '/') { + c[0] = ack; + c[1] = ack; + c[2] = etx; + c += 2; + continue; + } + c[0] = soh; + while (c[0] && c[1] != '>') + c++; + c[1] = stx; + } + return str; +} + DEF_CMD(renderline_set) { struct rline_data *rd = ci->home->data; @@ -1096,7 +1130,7 @@ DEF_CMD(renderline_set) struct xy xyscale = pane_scale(ci->focus); if (ci->str) - rd->line = strdup(ci->str); + rd->line = cvt(strdup(ci->str)); else rd->line = NULL; if (strcmp(rd->line ?:"", old ?:"") != 0 || diff --git a/misc.h b/misc.h index 9d861f85..b0490df6 100644 --- a/misc.h +++ b/misc.h @@ -60,6 +60,24 @@ static inline void buf_reinit(struct buf *b safe) b->len = 0; } +/* Formatting can be embedded in text strings in some places using + * SOH STX ETX. + * SOH format-attributes STX the text ETX + * "the text" can contain nested SOH/STX/ETX sequences. + * The same can be done with + * < format-attributes> the text + * but that is being phased out. + */ +#define SOH "\001" +#define STX "\002" +#define ETX "\003" +#define soh '\001' +#define stx '\002' +#define etx '\003' +/* ACK is used as a no-op. */ +#define ack '\006' +#define ACK "\006" + /* Performance measurements. * 1/ timers. */ diff --git a/tests.d/02-grep b/tests.d/02-grep index cae82b30..e662c692 100644 --- a/tests.d/02-grep +++ b/tests.d/02-grep @@ -222,7 +222,7 @@ Display 80,30 DD0B850460093327B7F2F877E46EA378 1,7 Key "-`" Display 80,30 9BFCD6FB7DDEEB9FF736EEE48A983DF2 1,6 Key "-`" -Display 80,30 5BC5B368B3E25A73BE8F8D595D61AEE6 1,6 +Display 80,30 CAEBB71698ADA0E51D9B5E710B1B0969 1,6 Key "-`" Display 80,30 3A381382535261F222BA0E5DC190CC2A 1,6 Key ":A-0"