commit a519945d8fdbb3248dd0a886a257f552dc4866e6
parent de641c02cf3534abc80eef4f44e9924b1dccc208
Author: Remy Noulin <loader2x@gmail.com>
Date: Wed, 2 Jan 2019 12:04:48 +0100
add utf8 support, hex colors, support for ansi sequences in input
boxen.c | 203 +++++++++++++++++++++++++++++++++++++++---------------------
boxen.h | 12 ++--
main.c | 6 +-
package.yml | 2 +-
4 files changed, 145 insertions(+), 78 deletions(-)
Diffstat:
| M | boxen.c | | | 203 | ++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------- |
| M | boxen.h | | | 12 | +++++++----- |
| M | main.c | | | 6 | +++--- |
| M | package.yml | | | 2 | +- |
4 files changed, 145 insertions(+), 78 deletions(-)
diff --git a/boxen.c b/boxen.c
@@ -93,20 +93,22 @@ void initiateBoxen(boxent *self) {
}
self->f = boxenF;
- self->borderColor = "";
- self->borderStyle = singleBorderStyle;
- self->dimBorder = "";
- self->padding.top = 0;
- self->padding.left = 0;
- self->padding.right = 0;
- self->padding.bottom = 0;
- self->margin.top = 0;
- self->margin.left = 0;
- self->margin.right = 0;
- self->margin.bottom = 0;
- self->boxFloat = "left";
- self->backgroundColor = "";
- self->align = "left";
+ self->borderColor = "";
+ self->borderColorHex = 0;
+ self->borderStyle = singleBorderStyle;
+ self->dimBorder = "";
+ self->padding.top = 0;
+ self->padding.left = 0;
+ self->padding.right = 0;
+ self->padding.bottom = 0;
+ self->margin.top = 0;
+ self->margin.left = 0;
+ self->margin.right = 0;
+ self->margin.bottom = 0;
+ self->boxFloat = "left";
+ self->backgroundColor = "";
+ self->backgroundColorHex = 0;
+ self->align = "left";
}
void registerMethodsBoxen(boxenFunctionst *f) {
@@ -255,10 +257,59 @@ internal const char* helpBoxen(boxent UNUSED *self) {
ret "TODO - boxen help";
}
+// https://en.wikipedia.org/wiki/ANSI_escape_code#Escape_sequences
+// [\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)
+// (?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))
+internal void stripAnsiSequences(char *string) {
+ size_t i = 0,j = 0;
+ enum {SEARCH_SEQ, CTRL_CODE};
+ u8 status = SEARCH_SEQ;
+ while(string[i]) {
+ if (status == SEARCH_SEQ) {
+ if (string[i] == '\x1b' || string[i] == '\x9b') {
+ // dont copy sequence
+ status = CTRL_CODE;
+ }
+ else {
+ // copy other char
+ string[j++] = string[i];
+ }
+ }
+ if (status == CTRL_CODE and string[i] == 'm') {
+ // all libsheepy codes end with 'm'
+ status = SEARCH_SEQ;
+ }
+ i++;
+ }
+ string[j] = 0;
+}
+
#define border(bord) self->borderStyle.bord
#define oborderColor(color, value)\
if (icEqG(getG(&j, rtChar, "borderColor"), color)) {\
- self->borderColor = value;\
+ self->borderColor = value;\
+ self->borderColorHex = 0;\
+ }
+#define oHexColor(color, opt, optHex)\
+ if (getG(&j, rtChar, color) && getG(&j, rtChar, color)[0] == '#') {\
+ /* hex color */\
+ char *c = getG(&j, rtChar, color);\
+ /* check if color is 0 */\
+ bool is0 = true;\
+ size_t i = 1;\
+ while(c[i]) if (c[i++] != '0') {is0 = false; break;}\
+ \
+ self->opt = BLK;\
+ if (!is0) {\
+ /* convert color string to int */\
+ char *s = catS("0x", &c[1]);\
+ self->optHex = parseHex(s);\
+ free(s);\
+ if (!self->optHex) {\
+ /* invalid hex number */\
+ self->opt = "";\
+ }\
+ }\
}
#define oborderStyle(style, value)\
if (icEqG(getG(&j, rtChar, "borderStyle"), style)) {\
@@ -274,7 +325,8 @@ internal const char* helpBoxen(boxent UNUSED *self) {
}
#define obackgroundColor(color, value)\
if (icEqG(getG(&j, rtChar, "backgroundColor"), color)) {\
- self->backgroundColor = value;\
+ self->backgroundColor = value;\
+ self->backgroundColorHex = 0;\
}
#define oalign(fl, value)\
if (icEqG(getG(&j, rtChar, "align"), fl)) {\
@@ -286,11 +338,6 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
char *r = NULL;
- // TODO add utf8 support
- // TODO
- // hex colors
- // remove control codes
-
// options
if (opts) {
@@ -305,6 +352,7 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
else oborderColor ( "cyan" , CYN)
else oborderColor ( "white" , BLD WHT)
else oborderColor ( "gray" , WHT)
+ else oHexColor("borderColor", borderColor, borderColorHex)
elif (isEStringG(&j, "borderColor")) {
// invalid value: reset
self->borderColor = "";
@@ -360,6 +408,7 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
else obackgroundColor ( "cyan" , BGCYN)
else obackgroundColor ( "white" , BGWHT) // TODO RGB?
else obackgroundColor ( "gray" , BGWHT)
+ else oHexColor("backgroundColor", backgroundColor, backgroundColorHex)
elif (isEStringG(&j, "backgroundColor")) {
// invalid value: reset
self->backgroundColor = "";
@@ -374,23 +423,26 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
var lines = splitG(input, '\n');
- var widest = widestLine(lines);
+ var noansi = dupG(lines);
+ forEachS(noansi, l) {
+ stripAnsiSequences(l);
+ }
+ //var widest = widestLine(lines);
+ var widest = widestLine(noansi);
// align text
if (eqG(self->align, "center")) {
- forEachCharP(lines, l) {
- var algWidth = (widest - lenG(*l)) / 2;
- range(i, algWidth) {
- prependG(l, " ");
- }
+ enumerateCharP(lines, l, i) {
+ var algWidth = (widest - lenUTF8(noansi[i])) / 2;
+ prependNFreeG(l, repeatS(" ", algWidth));
+ prependNFreeG(&noansi[i], repeatS(" ", algWidth));
}
}
elif (eqG(self->align, "right")) {
- forEachCharP(lines, l) {
- var algWidth = widest - lenG(*l);
- range(i, algWidth) {
- prependG(l, " ");
- }
+ enumerateCharP(lines, l, i) {
+ var algWidth = widest - lenUTF8(noansi[i]);
+ prependNFreeG(l, repeatS(" ", algWidth));
+ prependNFreeG(&noansi[i], repeatS(" ", algWidth));
}
}
// align left - no-op
@@ -406,9 +458,7 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
var contentWidth = widest + self->padding.left + self->padding.right;
char *paddingLeft = NULL;
- range(i, self->padding.left) {
- pushG(&paddingLeft, " ");
- }
+ pushNFreeG(&paddingLeft, repeatS(" ", self->padding.left));
winSizet sz = wsize(); // window cols
@@ -417,55 +467,74 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
// float
if (eqG(self->boxFloat, "center")) {
var padWidth = maxV((sz.cols - contentWidth) / 2, 0);
- range(i, padWidth) {
- pushG(&marginLeft, " ");
- }
+ pushNFreeG(&marginLeft, repeatS(" ", padWidth));
}
elif (eqG(self->boxFloat, "right")) {
var padWidth = maxV(sz.cols - contentWidth - self->margin.right - 2, 0);
- range(i, padWidth) {
- pushG(&marginLeft, " ");
- }
+ pushNFreeG(&marginLeft, repeatS(" ", padWidth));
}
else {
// left
- range(i, self->margin.left) {
- pushG(&marginLeft, " ");
- }
+ pushNFreeG(&marginLeft, repeatS(" ", self->margin.left));
}
- // top
- range(i, self->margin.top) {
- pushG(&r, '\n');
+ // colors
+ char *borderColorS = NULL;
+ if (!self->borderColor[0]) {
+ emptyS(borderColorS);
+ }
+ elif (self->borderColorHex) {
+ u32 c = self->borderColorHex;
+ borderColorS = formatS("\x1b[38;2;%d;%d;%dm", c>>16, (c&0xFF00)>>8, c&0xFF);
+ }
+ else {
+ borderColorS = dupG(self->borderColor);
+ }
+ char *backgroundColorS = NULL;
+ if (!self->backgroundColor[0]) {
+ emptyS(backgroundColorS);
+ }
+ elif (self->backgroundColorHex) {
+ u32 c = self->backgroundColorHex;
+ backgroundColorS = formatS("\x1b[48;2;%d;%d;%dm", c>>16, (c&0xFF00)>>8, c&0xFF);
+ }
+ else {
+ backgroundColorS = dupG(self->backgroundColor);
}
+
+ // top
+ pushNFreeG(&r, repeatS("\n", self->margin.top));
pushG(&r, marginLeft);
pushG(&r, self->dimBorder);
- pushG(&r, self->borderColor);
+ pushG(&r, borderColorS);
pushG(&r, border(topLeft));
- range(i, contentWidth) {
- pushG(&r, border(horizontal));
- }
+ pushNFreeG(&r, repeatS(border(horizontal), contentWidth));
pushG(&r, border(topRight));
pushG(&r, RST"\n");
// middle
- forEachS(lines, l) {
+ enumerateS(lines, l, i) {
pushG(&r, marginLeft);
pushG(&r, self->dimBorder);
- pushG(&r, self->borderColor);
+ pushG(&r, borderColorS);
pushG(&r, border(vertical));
pushG(&r, RST);
- pushG(&r, self->backgroundColor);
+ pushG(&r, backgroundColorS);
pushG(&r, paddingLeft);
pushG(&r, l);
- char *paddingRight = NULL;
- range(i, contentWidth - lenG(l) - self->padding.left) {
- pushG(&paddingRight, " ");
+ // restore background color after text
+ pushG(&r, backgroundColorS);
+ // paddingRight
+ i64 idx = i - self->padding.top;
+ if (idx >= 0 and idx < lenG(noansi)) {
+ pushNFreeG(&r, repeatS(" ", contentWidth - lenUTF8(noansi[idx]) - self->padding.left));
+ }
+ else {
+ pushNFreeG(&r, repeatS(" ", contentWidth - lenUTF8(l) - self->padding.left));
}
- pushNFreeG(&r, paddingRight);
pushG(&r, RST);
pushG(&r, self->dimBorder);
- pushG(&r, self->borderColor);
+ pushG(&r, borderColorS);
pushG(&r, border(vertical));
pushG(&r, RST);
pushG(&r, '\n');
@@ -474,21 +543,17 @@ internal char *boxBoxen(boxent *self, char *input, char *opts) {
// bottom
pushG(&r, marginLeft);
pushG(&r, self->dimBorder);
- pushG(&r, self->borderColor);
+ pushG(&r, borderColorS);
pushG(&r, border(bottomLeft));
- range(i, contentWidth) {
- pushG(&r, border(horizontal));
- }
+ pushNFreeG(&r, repeatS(border(horizontal), contentWidth));
pushG(&r, border(bottomRight));
pushG(&r, RST);
- range(i, self->margin.bottom) {
- pushG(&r, '\n');
- }
+ pushNFreeG(&r, repeatS("\n", self->margin.bottom));
// free
+ freeG(noansi);
freeG(lines);
- free(paddingLeft);
- free(marginLeft);
+ freeManyS(paddingLeft, marginLeft, borderColorS, backgroundColorS);
ret r;
}
@@ -497,7 +562,7 @@ internal size_t widestLine(char **text) {
size_t r = 0;
if (!text) ret 0;
forEachS(text, l) {
- r = MAX(r, lenG(l));
+ r = MAX(r, lenUTF8(l));
}
ret r;
}
diff --git a/boxen.h b/boxen.h
@@ -157,14 +157,16 @@ struct boxen {
const char *type;
boxenFunctionst *f;
- char *borderColor;
+ char *borderColor;
+ u32 borderColorHex;
borderStyleBoxent borderStyle;
- char *dimBorder;
+ char *dimBorder;
struct {u32 top; u32 left; u32 right; u32 bottom;} padding;
struct {u32 top; u32 left; u32 right; u32 bottom;} margin;
- char *boxFloat;
- char *backgroundColor;
- char *align;
+ char *boxFloat;
+ char *backgroundColor;
+ u32 backgroundColorHex;
+ char *align;
};
/* boxen */
diff --git a/main.c b/main.c
@@ -22,14 +22,14 @@ int main(int ARGC, char** ARGV) {
createBoxen(box);
logNFree(boxO(&box, "unicorn", NULL));
- logNFree(boxO(&box, "unicorn\nand\nthe sheepy",
- "{borderColor: 'Yellow',\n\
+ logNFree(boxO(&box, BLD RED"unicornâś”"RST"\nand\nthe sheepy",
+ "{borderColor: '#FFFF00',\n\
borderStyle: 'double',\n\
dimBorder: true,\n\
padding: {top: 1, left: 2, right: 2, bottom: 2},\n\
margin: {top: 1, left: 1, right: 1, bottom: 1},\n\
float: 'right',\n\
- backgroundColor: 'green',\n\
+ backgroundColor: '#0000FF',\n\
align: 'center'}"));
// options are optional
logNFree(boxO(&box, "unicorn\nand\nthe sheepy",
diff --git a/package.yml b/package.yml
@@ -1,6 +1,6 @@
---
name: boxen
- version: 0.0.1
+ version: 0.0.2
description: "Create boxes in the terminal"
bin: ./boxen.c
#cflags: -DA -ggdb -std=gnu11 -fPIC -pipe