diff --git a/examples/demo/highlight.js b/examples/demo/highlight.js new file mode 100644 index 0000000..7651e3a --- /dev/null +++ b/examples/demo/highlight.js @@ -0,0 +1,36 @@ +const hl = Ant.highlight; +const render = hl.render; + +console.log(render('Red text and back to normal')); +console.log(render('Green, Blue, Yellow')); + +console.log(render('Bright Red vs Normal Red')); + +console.log(render('Red background with text')); +console.log(render('Blue bg with white text')); + +console.log(render('Bold text and dim text')); +console.log(render(' text')); +console.log(render('Italic or italic, strikethrough, inverted')); + +console.log(render('Bold red or bold blue')); +console.log(render('Dim cyan and italic green')); + +console.log(render('<#ff8800>Orange hex color')); +console.log(render('<#0f0>Bright green shorthand')); +console.log(render('Magenta background')); + +console.log(render('Number: 42, String: hello')); +console.log(render('Hex: 0xff, Float: 3.14')); + +console.log(render('red back to normal immediately')); + +console.log(render('Literal <<: less than, >>: greater than')); +console.log(render('Literal %%: percent sign')); + +const code = `\nconst x = 42; +function greet(name) { + console.log(\`Hello, \${name}!\`); +}`; + +console.log(hl(code)); diff --git a/examples/demo/kat.js b/examples/demo/kat.js new file mode 100755 index 0000000..ed4b17c --- /dev/null +++ b/examples/demo/kat.js @@ -0,0 +1,21 @@ +#!/usr/bin/env ant + +import fs from 'node:fs'; +import path from 'node:path'; + +function exit(error) { + console.log(error); + process.exit(1); +} + +const file = process.argv[2]; +if (!file) exit('usage: kat '); + +try { + const content = fs.readFileSync(file, 'utf8'); + if (/\.(c|m)?(j|t)s$/.test(path.extname(file))) { + console.log(Ant.highlight(content)); + } else console.log(content); +} catch (err) { + exit(err.message); +} diff --git a/src/highlight/iter.c b/src/highlight/iter.c index 0950d9a..404e4bb 100644 --- a/src/highlight/iter.c +++ b/src/highlight/iter.c @@ -356,6 +356,13 @@ bool hl_iter_next(hl_iter *it, hl_span *out) { return true; } + if (i == 0 && c == '#' && i + 1 < input_len && input[i + 1] == '!') { + while (i < input_len && input[i] != '\n') i++; + *out = (hl_span){ 0, i, HL_COMMENT }; + it->pos = i; + return true; + } + if (it->state.mode == HL_STATE_STRING_SINGLE || it->state.mode == HL_STATE_STRING_DOUBLE) { char quote = (it->state.mode == HL_STATE_STRING_SINGLE) ? '\'' : '"'; size_t start = i; diff --git a/src/modules/builtin.c b/src/modules/builtin.c index 6f557af..66b4737 100644 --- a/src/modules/builtin.c +++ b/src/modules/builtin.c @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef __APPLE__ #include @@ -18,6 +19,7 @@ #include "errors.h" #include "runtime.h" #include "internal.h" +#include "highlight.h" #include "descriptors.h" #include "silver/engine.h" @@ -358,6 +360,57 @@ static ant_value_t js_match(ant_t *js, ant_value_t *args, int nargs) { return js_mkundef(); } +static ant_value_t hl_get_tagged(ant_t *js, ant_value_t *args, int nargs) { + size_t input_len; + char *input = js_getstr(js, args[0], &input_len); + + size_t out_size = input_len * 8 + 1; + char *out = malloc(out_size); + if (!out) return js_mkerr(js, "out of memory"); + + int len = ant_highlight(input, input_len, out, out_size); + ant_value_t result = js_mkstr(js, out, (size_t)len); + free(out); + + return result; +} + +static ant_value_t hl_render_tagged(ant_t *js, ant_value_t tagged) { + size_t tagged_len; + char *tagged_str = js_getstr(js, tagged, &tagged_len); + + size_t ansi_size = tagged_len * 2 + 1; + char *ansi = malloc(ansi_size); + if (!ansi) return js_mkerr(js, "out of memory"); + + int len = crsprintf_stateful(ansi, ansi_size, NULL, tagged_str); + ant_value_t result = js_mkstr(js, ansi, len < 0 ? 0 : (size_t)len); + free(ansi); + + return result; +} + +static ant_value_t js_highlight(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "Ant.highlight() requires 1 argument"); + if (vtype(args[0]) != T_STR) return js_mkerr(js, "Ant.highlight() argument must be a string"); + + ant_value_t tagged = hl_get_tagged(js, args, nargs); + if (is_err(tagged)) return tagged; + return hl_render_tagged(js, tagged); +} + +static ant_value_t js_highlight_render(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "Ant.highlight.render() requires 1 argument"); + if (vtype(args[0]) != T_STR) return js_mkerr(js, "Ant.highlight.render() argument must be a string"); + return hl_render_tagged(js, args[0]); +} + +static ant_value_t js_highlight_tags(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "Ant.highlight.tags() requires 1 argument"); + if (vtype(args[0]) != T_STR) return js_mkerr(js, "Ant.highlight.tags() argument must be a string"); + return hl_get_tagged(js, args, nargs); +} + void init_builtin_module() { ant_t *js = rt->js; ant_value_t ant_obj = rt->ant_obj; @@ -368,6 +421,14 @@ void init_builtin_module() { js_set(js, ant_obj, "sleep", js_mkfun(js_sleep)); js_set(js, ant_obj, "msleep", js_mkfun(js_msleep)); js_set(js, ant_obj, "usleep", js_mkfun(js_usleep)); + + ant_value_t hl_obj = js_newobj(js); + ant_value_t hl_fn = js_obj_to_func(hl_obj); + + js_set_slot(hl_obj, SLOT_CFUNC, js_mkfun(js_highlight)); + js_set(js, hl_fn, "render", js_mkfun(js_highlight_render)); + js_set(js, hl_fn, "tags", js_mkfun(js_highlight_tags)); + js_set(js, ant_obj, "highlight", hl_fn); ant_value_t raw_obj = js_newobj(js); js_set_getter_desc(js, js_as_obj(raw_obj), "stack", 5, js_mkfun(js_raw_stack), JS_DESC_C); diff --git a/src/readline.c b/src/readline.c index e2015e4..f071055 100644 --- a/src/readline.c +++ b/src/readline.c @@ -308,24 +308,20 @@ static void refresh_line(const char *line, int len, int pos, const char *prompt) repl_clear_to_end(); if (crprintf_get_color() && len > 0) { if (len <= 2048) { - char tagged[8192]; - char rendered[8192]; - - highlight_state state = hl_line_state; - ant_highlight_stateful(line, (size_t)len, tagged, sizeof(tagged), &state); - - hl_prog = crprintf_recompile(hl_prog, tagged); - crprintf_state *rs = crprintf_state_new(); - crsprintf_compiled(rendered, sizeof(rendered), rs, hl_prog); - crprintf_state_free(rs); - - fputs(rendered, stdout); - } else { - fwrite(line, 1, (size_t)len, stdout); - } - } else if (len > 0) { - fwrite(line, 1, (size_t)len, stdout); - } + char tagged[8192]; + char rendered[8192]; + + highlight_state state = hl_line_state; + ant_highlight_stateful(line, (size_t)len, tagged, sizeof(tagged), &state); + + hl_prog = crprintf_recompile(hl_prog, tagged); + crprintf_state *rs = crprintf_state_new(); + crsprintf_compiled(rendered, sizeof(rendered), rs, hl_prog); + crprintf_state_free(rs); + + fputs(rendered, stdout); + } else fwrite(line, 1, (size_t)len, stdout); + } else if (len > 0) fwrite(line, 1, (size_t)len, stdout); #ifndef _WIN32 if ((x_end == 0) && (y_end > 0) && (len > 0) && (line[len - 1] != '\n')) {