aboutsummaryrefslogtreecommitdiffstats
path: root/surfaces
diff options
context:
space:
mode:
Diffstat (limited to 'surfaces')
-rw-r--r--surfaces/draw.c36
-rw-r--r--surfaces/pixels.c3
-rw-r--r--surfaces/vega.h10
-rw-r--r--surfaces/web.c7
-rw-r--r--surfaces/width.c8
5 files changed, 51 insertions, 13 deletions
diff --git a/surfaces/draw.c b/surfaces/draw.c
index ec0b536..3135705 100644
--- a/surfaces/draw.c
+++ b/surfaces/draw.c
@@ -6,15 +6,16 @@ static const char *const tone_names[VEGA_TONES] = {"normal", "muted", "accent",
/* ---- CLI ------------------------------------------------------------ */
-/* Text for a pipe: control characters become their pictures so a value never
- * breaks a line. */
+/* Text for a pipe: what would act on the output instead of being shown
+ * becomes its picture (vega_cell), so a value never breaks a line nor turns
+ * one around. */
static void put_text(FILE *out, const char *text, size_t length) {
for (size_t at = 0; at < length;) {
uint32_t codepoint;
size_t size = vega_decode(text, length, at, &codepoint);
- if (vega_visible(codepoint) == codepoint) fwrite(text + at, 1, size, out);
+ if (vega_cell(codepoint) == codepoint) fwrite(text + at, 1, size, out);
else {
- uint32_t picture = vega_visible(codepoint);
+ uint32_t picture = vega_cell(codepoint);
fputc(0xE0 | (int)(picture >> 12), out);
fputc(0x80 | (int)(picture >> 6 & 0x3F), out);
fputc(0x80 | (int)(picture & 0x3F), out);
@@ -125,10 +126,29 @@ void vega_print(FILE *out, PureApp *app, const VegaScreen *screen, bool color) {
/* ---- NUI ------------------------------------------------------------ */
-/* Spoken text: control characters become spaces, and trailing ones go. */
+/* A code point as it is said: what would act on the output (controls, line
+ * and paragraph separators) as a space, the controls that turn text around
+ * not at all (0), else itself. */
+static uint32_t spoken(uint32_t codepoint) {
+ if (vega_visible(codepoint) != codepoint) return ' ';
+ return vega_cell(codepoint) != codepoint ? 0 : codepoint;
+}
+
+/* Spoken text: as spoken says, without the spaces it ends with. */
static void speak(FILE *out, const char *text, size_t length) {
- while (length && ((unsigned char)text[length - 1] <= 0x20)) --length;
- for (size_t i = 0; i < length; ++i) fputc((unsigned char)text[i] < 0x20 ? ' ' : text[i], out);
+ size_t end = 0;
+ for (size_t at = 0; at < length;) {
+ uint32_t codepoint, said;
+ at += vega_decode(text, length, at, &codepoint);
+ if ((said = spoken(codepoint)) && said != ' ') end = at;
+ }
+ for (size_t at = 0; at < end;) {
+ uint32_t codepoint, said;
+ size_t size = vega_decode(text, end, at, &codepoint);
+ if ((said = spoken(codepoint)) == ' ') fputc(' ', out);
+ else if (said) fwrite(text + at, 1, size, out);
+ at += size;
+ }
}
/* "Item 2 of 3: milk, done." Tones other than normal and muted are said. */
@@ -292,7 +312,7 @@ static void put_cells(Pen *pen, const char *text, size_t length) {
for (size_t at = 0; at < length;) {
uint32_t codepoint;
at += vega_decode(text, length, at, &codepoint);
- codepoint = vega_visible(codepoint);
+ codepoint = vega_cell(codepoint);
unsigned width = vega_width(codepoint);
if (!width) {
if (pen->column && !line[pen->column - 1].mark) {
diff --git a/surfaces/pixels.c b/surfaces/pixels.c
index d58d5b2..59d5d07 100644
--- a/surfaces/pixels.c
+++ b/surfaces/pixels.c
@@ -312,8 +312,7 @@ static bool set_type(const VegaGrid *grid, VegaFace *face, uint32_t *out, size_t
for (size_t at = 0; at < t->length;) {
uint32_t code;
at += vega_decode(grid->text + t->at, t->length, at, &code);
- code = vega_visible(code);
- w.codes[count++] = code == 0x2028 || code == 0x2029 ? ' ' : code; /* one line: no breaks in it */
+ w.codes[count++] = vega_visible(code); /* one line: no breaks in it */
}
}
if (j < n && grid->spans[w.order[j]].row == s->row && grid->spans[w.order[j]].column < end)
diff --git a/surfaces/vega.h b/surfaces/vega.h
index 0ab87dd..c172d31 100644
--- a/surfaces/vega.h
+++ b/surfaces/vega.h
@@ -91,8 +91,16 @@ unsigned long vega_evaluations(void);
unsigned vega_width(uint32_t codepoint);
/* The code point at text[at] (valid UTF-8) and the bytes it takes. */
size_t vega_decode(const char *text, size_t length, size_t at, uint32_t *codepoint);
-/* A control character shown as its control picture (U+2400 block), else itself. */
+/* What a value's code point is shown as, on every surface: a control
+ * character as its control picture (the U+2400 block, DEL too), a C1 control
+ * as U+FFFD, a line or paragraph separator as U+2424 (a value never breaks
+ * its line), else itself. */
uint32_t vega_visible(uint32_t codepoint);
+/* In a terminal's cell (the TUI, the CLI) also the controls that would turn
+ * the rest of the terminal's line around (embeddings, overrides, isolates),
+ * as U+FFFD: pixels lay a value's text out themselves, within its place. */
+uint32_t vega_cell(uint32_t codepoint);
+/* The cells text takes, as vega_cell shows it. */
unsigned vega_text_width(const char *text, size_t length);
/* CLI: everything, as plain lines for pipes; color only when asked. */
diff --git a/surfaces/web.c b/surfaces/web.c
index a4759c4..dfa9470 100644
--- a/surfaces/web.c
+++ b/surfaces/web.c
@@ -193,6 +193,8 @@ static const char *const page_parts[] = {
"ul{list-style:none;margin:0;padding:0;border:1px solid var(--line);border-radius:8px;overflow:hidden}"
"li,tbody tr{cursor:pointer}li{display:flex;gap:10px;padding:7px 12px;border-top:1px solid var(--line)}"
"li:first-child{border-top:0}.label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:pre}"
+/* Each value its own paragraph as bidi goes: what turns text around stays in it. */
+".label,.detail,.badge,td,th,dd,dt,h1,h2 span{unicode-bidi:isolate}"
".detail{color:var(--muted);white-space:pre}.badge{font-size:12px;padding:1px 8px;border:1px solid currentColor;"
"border-radius:10px;align-self:center}[aria-selected=true]{background:var(--focus);color:#fff}"
"table{width:100%;border-collapse:collapse;border:1px solid var(--line)}th{text-align:left;color:var(--muted);"
@@ -229,7 +231,10 @@ static const char *const page_parts[] = {
"finally{busy=false;if(again){again=false;refresh()}}}"
"function fit(parent,count,make){while(parent.children.length>count)parent.lastChild.remove();"
"while(parent.children.length<count)parent.append(make());return[...parent.children]}"
-"function text(node,value){if(node.textContent!==value)node.textContent=value}"
+/* A value shown as the other surfaces show it (vega_visible): controls as their pictures. */
+"function visible(value){return String(value).replace(/[\\x00-\\x1f\\x7f-\\x9f\\u2028\\u2029]/g,c=>{const n=c.charCodeAt(0);"
+"return String.fromCharCode(n<32?0x2400+n:n===127?0x2421:n<160?0xfffd:0x2424)})}"
+"function text(node,value){value=visible(value);if(node.textContent!==value)node.textContent=value}"
"function attr(node,name,value){if(value==null)node.removeAttribute(name);"
"else if(node.getAttribute(name)!==String(value))node.setAttribute(name,value)}"
"const views=new Map();"
diff --git a/surfaces/width.c b/surfaces/width.c
index 1e893f6..645e309 100644
--- a/surfaces/width.c
+++ b/surfaces/width.c
@@ -46,9 +46,15 @@ uint32_t vega_visible(uint32_t codepoint) {
if (codepoint < 0x20) return 0x2400 + codepoint;
if (codepoint == 0x7F) return 0x2421;
if (codepoint >= 0x80 && codepoint < 0xA0) return 0xFFFD;
+ if (codepoint == 0x2028 || codepoint == 0x2029) return 0x2424;
return codepoint;
}
+uint32_t vega_cell(uint32_t codepoint) {
+ if ((codepoint >= 0x202A && codepoint <= 0x202E) || (codepoint >= 0x2066 && codepoint <= 0x2069)) return 0xFFFD;
+ return vega_visible(codepoint);
+}
+
size_t vega_decode(const char *text, size_t length, size_t at, uint32_t *codepoint) {
const unsigned char *p = (const unsigned char *)text + at;
size_t left = length - at, need = p[0] < 0x80 ? 1 : p[0] < 0xE0 ? 2 : p[0] < 0xF0 ? 3 : 4;
@@ -64,7 +70,7 @@ unsigned vega_text_width(const char *text, size_t length) {
for (size_t at = 0; at < length;) {
uint32_t codepoint;
at += vega_decode(text, length, at, &codepoint);
- width += vega_width(vega_visible(codepoint));
+ width += vega_width(vega_cell(codepoint));
}
return width;
}