mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-24 01:40:12 -05:00
feat: ansi for pipe rows
This commit is contained in:
parent
5587604469
commit
312b22300d
10 changed files with 566 additions and 23 deletions
4
config.c
4
config.c
|
|
@ -132,6 +132,7 @@ static const char *const binding_action_map[] = {
|
|||
[BIND_ACTION_PIPE_VIEW] = "pipe-visible",
|
||||
[BIND_ACTION_PIPE_SELECTED] = "pipe-selected",
|
||||
[BIND_ACTION_PIPE_COMMAND_OUTPUT] = "pipe-command-output",
|
||||
[BIND_ACTION_TOGGLE_ANSI_SELECTION] = "toggle-ansi-selection",
|
||||
[BIND_ACTION_SHOW_URLS_COPY] = "show-urls-copy",
|
||||
[BIND_ACTION_SHOW_URLS_LAUNCH] = "show-urls-launch",
|
||||
[BIND_ACTION_SHOW_URLS_PERSISTENT] = "show-urls-persistent",
|
||||
|
|
@ -923,6 +924,9 @@ parse_section_main(struct context *ctx)
|
|||
else if (streq(key, "app-id"))
|
||||
return value_to_str(ctx, &conf->app_id);
|
||||
|
||||
else if (streq(key, "ansi-pipe"))
|
||||
return value_to_bool(ctx, &conf->ansi_pipe);
|
||||
|
||||
else if (streq(key, "initial-window-size-pixels")) {
|
||||
if (!value_to_dimensions(ctx, &conf->size.width, &conf->size.height))
|
||||
return false;
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -416,6 +416,8 @@ struct config {
|
|||
|
||||
char *utmp_helper_path;
|
||||
|
||||
bool ansi_pipe;
|
||||
|
||||
struct {
|
||||
enum fcft_scaling_filter fcft_filter;
|
||||
bool overflowing_glyphs;
|
||||
|
|
|
|||
552
extract.c
552
extract.c
|
|
@ -1,4 +1,5 @@
|
|||
#include "extract.h"
|
||||
#include "terminal.h"
|
||||
#include <string.h>
|
||||
|
||||
#define LOG_MODULE "extract"
|
||||
|
|
@ -18,27 +19,122 @@ struct extraction_context {
|
|||
const struct row *last_row;
|
||||
const struct cell *last_cell;
|
||||
enum selection_kind selection_kind;
|
||||
|
||||
bool rich;
|
||||
bool bold; // 0
|
||||
bool dim; // 1
|
||||
bool italic; // 2
|
||||
bool underline; // 3
|
||||
bool blink; // 4
|
||||
bool reverse; // 5
|
||||
bool conceal; // 6
|
||||
bool strikethrough; // 7
|
||||
uint32_t fg; // 8
|
||||
uint32_t bg; // 9
|
||||
uint32_t un; // 3
|
||||
enum color_source fg_src; // 8
|
||||
enum color_source bg_src; // 9
|
||||
enum color_source un_src; // 3
|
||||
enum underline_style underline_style; // 3
|
||||
uint64_t url_id; // 10
|
||||
};
|
||||
|
||||
struct extraction_context *
|
||||
extract_begin(enum selection_kind kind, bool strip_trailing_empty)
|
||||
{
|
||||
struct extraction_context *ctx = malloc(sizeof(*ctx));
|
||||
if (unlikely(ctx == NULL)) {
|
||||
LOG_ERRNO("malloc() failed");
|
||||
return NULL;
|
||||
uint16_t
|
||||
compare_attrs(struct extraction_context *ctx, struct attributes attrs, const struct row *row, int col) {
|
||||
uint16_t diff = 0;
|
||||
uint8_t idx;
|
||||
|
||||
const struct row_data *extra = row->extra;
|
||||
|
||||
idx = 0;
|
||||
if (ctx->bold != attrs.bold)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 1;
|
||||
if (ctx->dim != attrs.dim)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 2;
|
||||
if (ctx->italic != attrs.italic)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 3;
|
||||
if (ctx->underline != attrs.underline)
|
||||
diff |= 1 << idx;
|
||||
if (extra != NULL){
|
||||
for (size_t i = 0; i < extra->underline_ranges.count; i++){
|
||||
const struct row_range *range = &extra->underline_ranges.v[i];
|
||||
if (range->start <= col && col <= range->end){
|
||||
if (ctx->underline_style != range->underline.style){
|
||||
diff |= 1 << idx;
|
||||
break;
|
||||
}
|
||||
if (ctx->un_src != range->underline.color_src){
|
||||
diff |= 1 << idx;
|
||||
break;
|
||||
}
|
||||
if ((range->underline.color_src != COLOR_DEFAULT) && ctx->un != range->underline.color){
|
||||
diff |= 1 << idx;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ctx = (struct extraction_context){
|
||||
.selection_kind = kind,
|
||||
.strip_trailing_empty = strip_trailing_empty,
|
||||
};
|
||||
return ctx;
|
||||
idx = 4;
|
||||
if (ctx->blink != attrs.blink)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 5;
|
||||
if (ctx->reverse != attrs.reverse)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 6;
|
||||
if (ctx->conceal != attrs.conceal)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 7;
|
||||
if (ctx->strikethrough != attrs.strikethrough)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 8;
|
||||
if (ctx->fg_src != attrs.fg_src)
|
||||
diff |= 1 << idx;
|
||||
if ((attrs.fg_src != COLOR_DEFAULT) && ctx->fg != attrs.fg)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 9;
|
||||
if (ctx->bg_src != attrs.bg_src)
|
||||
diff |= 1 << idx;
|
||||
if ((attrs.bg_src != COLOR_DEFAULT) && ctx->bg != attrs.bg)
|
||||
diff |= 1 << idx;
|
||||
|
||||
idx = 10;
|
||||
|
||||
if (extra != NULL){
|
||||
bool found_one = false;
|
||||
for (size_t i = 0; i < extra->uri_ranges.count; i++){
|
||||
const struct row_range *range = &extra->uri_ranges.v[i];
|
||||
if (range->start <= col && col <= range->end){
|
||||
found_one = true;
|
||||
if (ctx->url_id != range->uri.id)
|
||||
diff |= 1 << idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_one && ctx->url_id)
|
||||
diff |= 1 << idx;
|
||||
} else if (ctx->url_id) {
|
||||
diff |= 1 << idx;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ensure_size(struct extraction_context *ctx, size_t additional_chars)
|
||||
{
|
||||
ensure_size(struct extraction_context *ctx, size_t additional_chars) {
|
||||
while (ctx->size < ctx->idx + additional_chars) {
|
||||
size_t new_size = ctx->size == 0 ? 512 : ctx->size * 2;
|
||||
char32_t *new_buf = realloc(ctx->buf, new_size * sizeof(new_buf[0]));
|
||||
|
|
@ -54,6 +150,413 @@ ensure_size(struct extraction_context *ctx, size_t additional_chars)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
clear_rich_ctx(struct extraction_context *ctx) {
|
||||
if (ctx->url_id){
|
||||
if (!ensure_size(ctx, 7))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U']';
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'\\';
|
||||
}
|
||||
|
||||
if (ctx->bold +
|
||||
ctx->dim +
|
||||
ctx->italic +
|
||||
ctx->underline +
|
||||
ctx->blink +
|
||||
ctx->reverse +
|
||||
ctx->conceal +
|
||||
ctx->strikethrough +
|
||||
ctx->fg_src +
|
||||
ctx->bg_src +
|
||||
ctx->un_src +
|
||||
ctx->underline_style
|
||||
)
|
||||
{
|
||||
if (!ensure_size(ctx, 4))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'[';
|
||||
ctx->buf[ctx->idx++] = U'0';
|
||||
ctx->buf[ctx->idx++] = U'm';
|
||||
}
|
||||
|
||||
ctx->bold = false;
|
||||
ctx->dim = false;
|
||||
ctx->italic = false;
|
||||
ctx->underline = false;
|
||||
ctx->blink = false;
|
||||
ctx->reverse = false;
|
||||
ctx->conceal = false;
|
||||
ctx->strikethrough = false;
|
||||
ctx->fg = 0;
|
||||
ctx->bg = 0;
|
||||
ctx->un = 0;
|
||||
ctx->fg_src = 0;
|
||||
ctx->bg_src = 0;
|
||||
ctx->un_src = 0;
|
||||
ctx->underline_style = 0;
|
||||
ctx->url_id = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
init_x1b(bool *x1b, struct extraction_context *ctx) {
|
||||
if (!(*x1b)) {
|
||||
if (!ensure_size(ctx, 2))
|
||||
return false;
|
||||
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'[';
|
||||
} else {
|
||||
if (!ensure_size(ctx, 1))
|
||||
return false;
|
||||
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
}
|
||||
*x1b = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
change_color_rich(enum color_source colour_src, uint32_t colour, struct extraction_context *ctx, uint8_t domain)
|
||||
{
|
||||
switch (colour_src) {
|
||||
case COLOR_DEFAULT:
|
||||
if (!ensure_size(ctx, 2))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'0' + domain;
|
||||
ctx->buf[ctx->idx++] = U'9';
|
||||
break;
|
||||
case COLOR_BASE16:
|
||||
xassert(domain != 0);
|
||||
if (!ensure_size(ctx, 2 + (((domain + (6 * (colour > 7)))) > 9)))
|
||||
return false;
|
||||
if (((domain + (6 * (colour > 7)))) > 9)
|
||||
ctx->buf[ctx->idx++] = U'1';
|
||||
ctx->buf[ctx->idx++] = U'0' + domain + (6 * (colour > 7)) - 10 * (((domain + (6 * (colour > 7)))) > 9);
|
||||
ctx->buf[ctx->idx++] = U'0' + colour - (8 * (colour > 7));
|
||||
break;
|
||||
case COLOR_BASE256:
|
||||
if (!ensure_size(ctx, 6 + (colour > 9) + (colour > 99)))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'0' + domain;
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U'5';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
if (colour > 99)
|
||||
ctx->buf[ctx->idx++] = U'0' + (colour / 100);
|
||||
if (colour > 9)
|
||||
ctx->buf[ctx->idx++] = U'0' + ((colour % 100) / 10);
|
||||
ctx->buf[ctx->idx++] = U'0' + (colour % 10);
|
||||
break;
|
||||
case COLOR_RGB:;
|
||||
uint8_t r = (colour >> 16) & 0xff;
|
||||
uint8_t g = (colour >> 8) & 0xff;
|
||||
uint8_t b = colour & 0xff;
|
||||
if (!ensure_size(ctx, 8 + (r > 9) + (r > 99) + (g > 9) + (g > 99) + (b > 9) + (b > 99)))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'0' + domain;
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U'2';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
if (r > 99)
|
||||
ctx->buf[ctx->idx++] = U'0' + (r / 100);
|
||||
if (r > 9)
|
||||
ctx->buf[ctx->idx++] = U'0' + ((r % 100) / 10);
|
||||
ctx->buf[ctx->idx++] = U'0' + (r % 10);
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
if (g > 99)
|
||||
ctx->buf[ctx->idx++] = U'0' + (g / 100);
|
||||
if (g > 9)
|
||||
ctx->buf[ctx->idx++] = U'0' + ((g % 100) / 10);
|
||||
ctx->buf[ctx->idx++] = U'0' + (g % 10);
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
if (b > 99)
|
||||
ctx->buf[ctx->idx++] = U'0' + (b / 100);
|
||||
if (b > 9)
|
||||
ctx->buf[ctx->idx++] = U'0' + ((b % 100) / 10);
|
||||
ctx->buf[ctx->idx++] = U'0' + (b % 10);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
style_flip_rich(bool attr, uint8_t attr_idx, struct extraction_context *ctx) {
|
||||
if (attr) {
|
||||
if (!ensure_size(ctx, 1))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'0' + attr_idx;
|
||||
} else {
|
||||
if (!ensure_size(ctx, 2))
|
||||
return false;
|
||||
ctx->buf[ctx->idx++] = U'2';
|
||||
ctx->buf[ctx->idx++] = U'0' + attr_idx;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
add_rich_diff(struct extraction_context *ctx, struct attributes attrs, const struct row *row, int col, uint16_t diff) {
|
||||
char idx;
|
||||
bool x1b = false;
|
||||
|
||||
/* dim and bod */
|
||||
idx = 0;
|
||||
if (diff & 1 << idx || diff & 1 << (idx + 1)) {
|
||||
x1b = true;
|
||||
if (!ensure_size(ctx, 2))
|
||||
goto err;
|
||||
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'[';
|
||||
|
||||
if ((!attrs.bold && !attrs.dim) || (attrs.bold ^ attrs.dim)) {
|
||||
if (!ensure_size(ctx, 2 + (2 * (attrs.bold ^ attrs.dim))))
|
||||
goto err;
|
||||
|
||||
ctx->buf[ctx->idx++] = U'2';
|
||||
ctx->buf[ctx->idx++] = U'2';
|
||||
if (attrs.bold ^ attrs.dim) {
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
if (attrs.dim) {
|
||||
ctx->buf[ctx->idx++] = U'2';
|
||||
}
|
||||
else if (attrs.bold) {
|
||||
ctx->buf[ctx->idx++] = U'1';
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx->dim = attrs.dim;
|
||||
ctx->bold = attrs.bold;
|
||||
}
|
||||
|
||||
/* italic */
|
||||
idx = 2;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!style_flip_rich(attrs.italic, 3, ctx))
|
||||
goto err;
|
||||
ctx->italic = attrs.italic;
|
||||
}
|
||||
|
||||
/* underline */
|
||||
idx = 3;
|
||||
if (diff & 1 << idx) {
|
||||
if (attrs.underline) {
|
||||
const struct row_data *extra = row->extra;
|
||||
if (extra != NULL) {
|
||||
for (size_t i = 0; i < extra->underline_ranges.count; i++) {
|
||||
const struct row_range *range = &extra->underline_ranges.v[i];
|
||||
if (range->start <= col && col <= range->end) {
|
||||
if (ctx->underline_style != range->underline.style) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!ensure_size(ctx, 3))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'4';
|
||||
ctx->buf[ctx->idx++] = U':';
|
||||
ctx->buf[ctx->idx++] = U'0' + range->underline.style;
|
||||
}
|
||||
|
||||
if ((ctx->un_src != range->underline.color_src) || ((range->underline.color_src != COLOR_DEFAULT) && ctx->un != range->underline.color)) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!change_color_rich(range->underline.color_src, range->underline.color, ctx, 5))
|
||||
goto err;
|
||||
}
|
||||
ctx->underline_style = range->underline.style;
|
||||
ctx->un = range->underline.color;
|
||||
ctx->un_src = range->underline.color_src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!ensure_size(ctx, 3))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'4';
|
||||
ctx->buf[ctx->idx++] = U':';
|
||||
ctx->buf[ctx->idx++] = U'1';
|
||||
}
|
||||
} else {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!ensure_size(ctx, 3))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'4';
|
||||
ctx->buf[ctx->idx++] = U':';
|
||||
ctx->buf[ctx->idx++] = U'0';
|
||||
}
|
||||
ctx->underline = attrs.underline;
|
||||
}
|
||||
|
||||
/* blink */
|
||||
idx = 4;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!style_flip_rich(attrs.blink, 5, ctx))
|
||||
goto err;
|
||||
ctx->blink = attrs.blink;
|
||||
}
|
||||
|
||||
/* reverse */
|
||||
idx = 5;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!style_flip_rich(attrs.reverse, 7, ctx))
|
||||
goto err;
|
||||
ctx->reverse = attrs.reverse;
|
||||
}
|
||||
|
||||
/* conceal */
|
||||
idx = 6;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!style_flip_rich(attrs.conceal, 8, ctx))
|
||||
goto err;
|
||||
ctx->conceal = attrs.conceal;
|
||||
}
|
||||
|
||||
/* strikethrough */
|
||||
idx = 7;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
if (!style_flip_rich(attrs.strikethrough, 9, ctx))
|
||||
goto err;
|
||||
ctx->strikethrough = attrs.strikethrough;
|
||||
}
|
||||
|
||||
/* foreground colour */
|
||||
idx = 8;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
|
||||
if (!change_color_rich(attrs.fg_src, attrs.fg, ctx, 3))
|
||||
goto err;
|
||||
|
||||
ctx->fg = attrs.fg;
|
||||
ctx->fg_src = attrs.fg_src;
|
||||
}
|
||||
|
||||
/* background colour */
|
||||
idx = 9;
|
||||
if (diff & 1 << idx) {
|
||||
if (!init_x1b(&x1b, ctx))
|
||||
goto err;
|
||||
|
||||
if (!change_color_rich(attrs.bg_src, attrs.bg, ctx, 4))
|
||||
goto err;
|
||||
|
||||
ctx->bg = attrs.bg;
|
||||
ctx->bg_src = attrs.bg_src;
|
||||
}
|
||||
|
||||
if (x1b) {
|
||||
if (!ensure_size(ctx, 1))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'm';
|
||||
}
|
||||
|
||||
idx = 10;
|
||||
if (diff & 1 << idx) {
|
||||
const struct row_data *extra = row->extra;
|
||||
if (extra != NULL) {
|
||||
char32_t *text;
|
||||
bool found_one = false;
|
||||
for (size_t i = 0; i < extra->uri_ranges.count; i++) {
|
||||
const struct row_range *range = &extra->uri_ranges.v[i];
|
||||
if (range->start <= col && col <= range->end) {
|
||||
found_one = true;
|
||||
ctx->url_id = range->uri.id;
|
||||
text = ambstoc32(range->uri.uri);
|
||||
|
||||
if (!ensure_size(ctx, 7 + c32len(text)))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U']';
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
|
||||
for (size_t j = 0; j < c32len(text); j++)
|
||||
ctx->buf[ctx->idx++] = text[j];
|
||||
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'\\';
|
||||
|
||||
free(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_one) {
|
||||
if (!ensure_size(ctx, 7))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U']';
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'\\';
|
||||
|
||||
ctx->url_id = 0;
|
||||
}
|
||||
} else {
|
||||
if (!ensure_size(ctx, 7))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U']';
|
||||
ctx->buf[ctx->idx++] = U'8';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U';';
|
||||
ctx->buf[ctx->idx++] = U'\x1b';
|
||||
ctx->buf[ctx->idx++] = U'\\';
|
||||
|
||||
ctx->url_id = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
err:
|
||||
free(ctx->buf);
|
||||
free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct extraction_context *
|
||||
extract_begin(enum selection_kind kind, bool strip_trailing_empty, bool rich) {
|
||||
struct extraction_context *ctx = malloc(sizeof(*ctx));
|
||||
if (unlikely(ctx == NULL)){
|
||||
LOG_ERRNO("malloc() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ctx = (struct extraction_context){
|
||||
.selection_kind = kind,
|
||||
.strip_trailing_empty = strip_trailing_empty,
|
||||
.rich = rich,
|
||||
};
|
||||
return ctx;
|
||||
}
|
||||
|
||||
bool
|
||||
extract_finish_wide(struct extraction_context *ctx, char32_t **text, size_t *len)
|
||||
{
|
||||
|
|
@ -111,6 +614,15 @@ extract_finish_wide(struct extraction_context *ctx, char32_t **text, size_t *len
|
|||
}
|
||||
}
|
||||
|
||||
if (ctx->rich){
|
||||
ctx->idx = ctx->idx - 1;
|
||||
if (!clear_rich_ctx(ctx))
|
||||
goto err;
|
||||
if (!ensure_size(ctx, 1))
|
||||
goto err;
|
||||
ctx->buf[ctx->idx++] = U'\0';
|
||||
}
|
||||
|
||||
*text = ctx->buf;
|
||||
if (len != NULL)
|
||||
*len = ctx->idx - 1;
|
||||
|
|
@ -157,12 +669,17 @@ extract_one(const struct terminal *term, const struct row *row,
|
|||
const struct cell *cell, int col, void *context)
|
||||
{
|
||||
struct extraction_context *ctx = context;
|
||||
struct attributes attrs = cell->attrs;
|
||||
|
||||
if (cell->wc >= CELL_SPACER)
|
||||
return true;
|
||||
|
||||
if (ctx->last_row != NULL && row != ctx->last_row) {
|
||||
/* New row - determine if we should insert a newline or not */
|
||||
if (ctx->rich){
|
||||
if (!clear_rich_ctx(ctx))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ctx->selection_kind != SELECTION_BLOCK) {
|
||||
if (ctx->last_row->linebreak ||
|
||||
|
|
@ -230,6 +747,13 @@ extract_one(const struct terminal *term, const struct row *row,
|
|||
ctx->newline_count = 0;
|
||||
ctx->empty_count = 0;
|
||||
|
||||
if (ctx->rich)
|
||||
{
|
||||
uint16_t rich_diff = compare_attrs(ctx, attrs, row, col);
|
||||
if (rich_diff)
|
||||
add_rich_diff(ctx, attrs, row, col, rich_diff);
|
||||
}
|
||||
|
||||
if (cell->wc >= CELL_COMB_CHARS_LO && cell->wc <= CELL_COMB_CHARS_HI)
|
||||
{
|
||||
const struct composed *composed = composed_lookup(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
struct extraction_context;
|
||||
|
||||
struct extraction_context *extract_begin(
|
||||
enum selection_kind kind, bool strip_trailing_empty);
|
||||
enum selection_kind kind, bool strip_trailing_empty, bool rich);
|
||||
|
||||
bool extract_one(
|
||||
const struct terminal *term, const struct row *row, const struct cell *cell,
|
||||
|
|
@ -19,3 +19,4 @@ bool extract_finish(
|
|||
struct extraction_context *context, char **text, size_t *len);
|
||||
bool extract_finish_wide(
|
||||
struct extraction_context *context, char32_t **text, size_t *len);
|
||||
|
||||
|
|
|
|||
5
input.c
5
input.c
|
|
@ -1,4 +1,5 @@
|
|||
#include "input.h"
|
||||
#include "key-binding.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -340,6 +341,10 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
return true;
|
||||
}
|
||||
|
||||
case BIND_ACTION_TOGGLE_ANSI_SELECTION:
|
||||
term->ansi_selection = !(term->ansi_selection);
|
||||
break;
|
||||
|
||||
case BIND_ACTION_SHOW_URLS_COPY:
|
||||
case BIND_ACTION_SHOW_URLS_LAUNCH:
|
||||
case BIND_ACTION_SHOW_URLS_PERSISTENT: {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ enum bind_action_normal {
|
|||
BIND_ACTION_PIPE_VIEW,
|
||||
BIND_ACTION_PIPE_SELECTED,
|
||||
BIND_ACTION_PIPE_COMMAND_OUTPUT,
|
||||
BIND_ACTION_TOGGLE_ANSI_SELECTION,
|
||||
BIND_ACTION_SHOW_URLS_COPY,
|
||||
BIND_ACTION_SHOW_URLS_LAUNCH,
|
||||
BIND_ACTION_SHOW_URLS_PERSISTENT,
|
||||
|
|
|
|||
4
search.c
4
search.c
|
|
@ -874,7 +874,7 @@ search_extend_left(struct terminal *term, const struct coord *target)
|
|||
|
||||
const bool move_cursor = term->search.cursor != 0;
|
||||
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, false);
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, false, false);
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -938,7 +938,7 @@ search_extend_right(struct terminal *term, const struct coord *target)
|
|||
|
||||
const bool move_cursor = term->search.cursor == term->search.len;
|
||||
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, false);
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, false, false);
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ selection_to_text(const struct terminal *term)
|
|||
if (term->selection.coords.end.row == -1)
|
||||
return NULL;
|
||||
|
||||
struct extraction_context *ctx = extract_begin(term->selection.kind, true);
|
||||
struct extraction_context *ctx = extract_begin(term->selection.kind, true, term->ansi_selection);
|
||||
if (ctx == NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
|
|||
13
terminal.c
13
terminal.c
|
|
@ -1400,6 +1400,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.cb = shutdown_cb,
|
||||
.cb_data = shutdown_data,
|
||||
},
|
||||
.ansi_pipe = conf->ansi_pipe,
|
||||
.ansi_selection = false,
|
||||
|
||||
.foot_exe = xstrdup(foot_exe),
|
||||
.cwd = xstrdup(cwd),
|
||||
.grapheme_shaping = conf->tweak.grapheme_shaping,
|
||||
|
|
@ -4418,9 +4421,9 @@ term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
|
|||
|
||||
static bool
|
||||
rows_to_text(const struct terminal *term, int start, int end,
|
||||
int col_start, int col_end, char **text, size_t *len)
|
||||
int col_start, int col_end, char **text, size_t *len, bool rich)
|
||||
{
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, true);
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, true, term->ansi_pipe);
|
||||
if (ctx == NULL)
|
||||
return false;
|
||||
|
||||
|
|
@ -4476,7 +4479,7 @@ term_scrollback_to_text(const struct terminal *term, char **text, size_t *len)
|
|||
end += term->grid->num_rows;
|
||||
}
|
||||
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len);
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len, true);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -4484,7 +4487,7 @@ term_view_to_text(const struct terminal *term, char **text, size_t *len)
|
|||
{
|
||||
int start = grid_row_absolute_in_view(term->grid, 0);
|
||||
int end = grid_row_absolute_in_view(term->grid, term->rows - 1);
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len);
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len, true);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -4524,7 +4527,7 @@ term_command_output_to_text(const struct terminal *term, char **text, size_t *le
|
|||
if (start_row < 0)
|
||||
return false;
|
||||
|
||||
bool ret = rows_to_text(term, start_row, end_row, start_col, end_col, text, len);
|
||||
bool ret = rows_to_text(term, start_row, end_row, start_col, end_col, text, len, true);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -807,6 +807,9 @@ struct terminal {
|
|||
bool ime_reenable_after_url_mode;
|
||||
const struct config_spawn_template *url_launch;
|
||||
|
||||
bool ansi_selection;
|
||||
bool ansi_pipe;
|
||||
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
bool ime_enabled;
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue