Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support delta along with ANSI support (continuation) #1298

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ TIG_OBJS = \
src/grep.o \
src/ui.o \
src/apps.o \
src/ansi.o \
$(GRAPH_OBJS) \
$(COMPAT_OBJS)

Expand Down
35 changes: 35 additions & 0 deletions include/tig/ansi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Copyright (c) 2006-2015 Jonas Fonseca <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#ifndef TIG_ANSI_H
#define TIG_ANSI_H

#include "tig/line.h"
#include "tig/tig.h"
#include "tig/view.h"

struct ansi_status {
short fg;
short bg;
unsigned int attr;
};

void split_ansi(const char *string, int *ansi_num, char **ansi_ptrs);
void draw_ansi(struct view *view, int *ansi_num, char **ansi_ptrs, int max_width, size_t skip);
void draw_ansi_line(struct view *view, char *ansi_end_ptr, int *after_ansi_len, size_t *skip, int *cur_width, int *widths_of_display);
void wattrset_by_ansi_status(struct view *view, struct ansi_status* cur_ansi_status);
short convert_ansi_into_256_color(char **save_ptr);

#endif

/* vim: set ts=8 sw=8 noexpandtab: */
4 changes: 4 additions & 0 deletions include/tig/line.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "tig/tig.h"
struct ref;

#if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20180127
extern short color_pairs_map[257][257];
#endif

/*
* Line-oriented content detection.
*/
Expand Down
233 changes: 233 additions & 0 deletions src/ansi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/* Copyright (c) 2006-2015 Jonas Fonseca <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "tig/ansi.h"
#include "tig/draw.h"
#include "tig/line.h"
#include "tig/tig.h"
#include "tig/view.h"
#include "compat/utf8proc.h"

void
split_ansi(const char *string, int *ansi_num, char **ansi_ptrs) {
char *head_of_ansi = "\033[";
int current_ansi_idx = 0;
char *next_ansi_ptr = strstr(string + current_ansi_idx, head_of_ansi);

if (next_ansi_ptr == NULL)
return;
while (next_ansi_ptr != NULL) {
if (strcmp(string, next_ansi_ptr) == 0) {
next_ansi_ptr = strstr(string + current_ansi_idx + strlen(head_of_ansi), head_of_ansi);
continue;
}
int current_ansi_length = strlen(string + current_ansi_idx) - strlen(next_ansi_ptr);
strncpy(ansi_ptrs[*ansi_num], string + current_ansi_idx, current_ansi_length);
ansi_ptrs[*ansi_num][current_ansi_length] = '\0';
*ansi_num += 1;
current_ansi_idx += current_ansi_length / sizeof(char);
next_ansi_ptr = strstr(string + current_ansi_idx + strlen(head_of_ansi), head_of_ansi);
}

strcpy(ansi_ptrs[*ansi_num], string + current_ansi_idx);
*ansi_num += 1;
}

void
draw_ansi(struct view *view, int *ansi_num, char **ansi_ptrs, int max_width, size_t skip) {
static struct ansi_status cur_ansi_status;
cur_ansi_status.fg = 256;
cur_ansi_status.bg = 256;
cur_ansi_status.attr = A_NORMAL;
int cur_width = 0;

for (int i = 0; i < *ansi_num; i++) {
if (cur_width >= view->width)
break;

int len = strlen(ansi_ptrs[i]);
char text[len + 1];
strcpy(text, ansi_ptrs[i]);

if (i == 0 && text[0] != '\033') {
waddnstr(view->win, text, len);
continue;
}

// ncurses can't handle J and K of ANSI code behavior.
if ((text[3] == 'J') || (text[3] == 'K'))
continue;

char *ansi_end_ptr = strchr(text, 'm');
int after_ansi_len = strlen(ansi_end_ptr);
int ansi_code_len = len - after_ansi_len - 2;
ansi_end_ptr += 1;

int widths_of_display = utf8_width_of(ansi_end_ptr, after_ansi_len, after_ansi_len);
if (skip > widths_of_display) {
skip -= widths_of_display;
continue;
}

if (view->curline->selected) {
draw_ansi_line(view, ansi_end_ptr, &after_ansi_len, &skip, &cur_width, &widths_of_display);
continue;
}

// ncurses can't handle multiple attribute such as BOLD & UNDERLINE.
// If input-ansi has "\033[1;4" we'll give priority to the latter one.
char *saveptr;
char *ansi_code = malloc(sizeof(char) * (ansi_code_len + 1));
strncpy(ansi_code, text + 2, ansi_code_len);
ansi_code[ansi_code_len] = '\0';
char *ansi_code_part = strtok_r(ansi_code, ";", &saveptr);
while (ansi_code_part != NULL) {
if (strcmp(ansi_code_part, "0") == 0) {
cur_ansi_status.fg = 256;
cur_ansi_status.bg = 256;
cur_ansi_status.attr = A_NORMAL;
}
if (strcmp(ansi_code_part, "1") == 0)
cur_ansi_status.attr = A_BOLD;
if (strcmp(ansi_code_part, "2") == 0)
cur_ansi_status.attr = A_DIM;
if (strcmp(ansi_code_part, "3") == 0)
cur_ansi_status.attr = A_ITALIC;
if (strcmp(ansi_code_part, "4") == 0)
cur_ansi_status.attr = A_UNDERLINE;
if (strcmp(ansi_code_part, "5") == 0)
cur_ansi_status.attr = A_BLINK;
if (strcmp(ansi_code_part, "6") == 0)
cur_ansi_status.attr = A_BLINK; // This is supposed to be faster than normal blink, but ncurses doesn't have any way to achieve.
if (strcmp(ansi_code_part, "7") == 0)
cur_ansi_status.attr = A_REVERSE;
if (strcmp(ansi_code_part, "8") == 0)
cur_ansi_status.attr = A_INVIS;
if (strcmp(ansi_code_part, "9") == 0)
// This is supposed to be strikethrough, but ncurses doesn't have any way to achieve.
if (strcmp(ansi_code_part, "30") == 0)
cur_ansi_status.fg = COLOR_BLACK;
if (strcmp(ansi_code_part, "31") == 0)
cur_ansi_status.fg = COLOR_RED;
if (strcmp(ansi_code_part, "32") == 0)
cur_ansi_status.fg = COLOR_GREEN;
if (strcmp(ansi_code_part, "33") == 0)
cur_ansi_status.fg = COLOR_YELLOW;
if (strcmp(ansi_code_part, "34") == 0)
cur_ansi_status.fg = COLOR_BLUE;
if (strcmp(ansi_code_part, "35") == 0)
cur_ansi_status.fg = COLOR_MAGENTA;
if (strcmp(ansi_code_part, "36") == 0)
cur_ansi_status.fg = COLOR_CYAN;
if (strcmp(ansi_code_part, "37") == 0)
cur_ansi_status.fg = COLOR_WHITE;
if (strcmp(ansi_code_part, "38") == 0) {
short c256 = convert_ansi_into_256_color(&saveptr);
if (c256 != -1)
cur_ansi_status.fg = c256;
}
if (strcmp(ansi_code_part, "40") == 0)
cur_ansi_status.bg = COLOR_BLACK;
if (strcmp(ansi_code_part, "41") == 0)
cur_ansi_status.bg = COLOR_RED;
if (strcmp(ansi_code_part, "42") == 0)
cur_ansi_status.bg = COLOR_GREEN;
if (strcmp(ansi_code_part, "43") == 0)
cur_ansi_status.bg = COLOR_YELLOW;
if (strcmp(ansi_code_part, "44") == 0)
cur_ansi_status.bg = COLOR_BLUE;
if (strcmp(ansi_code_part, "45") == 0)
cur_ansi_status.bg = COLOR_MAGENTA;
if (strcmp(ansi_code_part, "46") == 0)
cur_ansi_status.bg = COLOR_CYAN;
if (strcmp(ansi_code_part, "47") == 0)
cur_ansi_status.bg = COLOR_WHITE;
if (strcmp(ansi_code_part, "48") == 0) {
short c256 = convert_ansi_into_256_color(&saveptr);
if (c256 != -1)
cur_ansi_status.bg = c256;
}

ansi_code_part = strtok_r(NULL, ";", &saveptr);
}
wattrset_by_ansi_status(view, &cur_ansi_status);
draw_ansi_line(view, ansi_end_ptr, &after_ansi_len, &skip, &cur_width, &widths_of_display);

free(ansi_code);
ansi_code = NULL;
}
}

void
draw_ansi_line(struct view *view, char *ansi_end_ptr, int *after_ansi_len, size_t *skip, int *cur_width, int *widths_of_display) {
while (*skip > 0) {
utf8proc_int32_t unicode;
int bytes_to_skip = utf8proc_iterate((const utf8proc_uint8_t *) ansi_end_ptr, *after_ansi_len, &unicode);
ansi_end_ptr += bytes_to_skip;
*after_ansi_len -= bytes_to_skip;
*skip -= 1;
*widths_of_display -= 1;
}

if (*cur_width + *widths_of_display > view->width) {
int left_widths = view->width - *cur_width;
while (left_widths > 0) {
utf8proc_int32_t unicode;
int bytes_to_display = utf8proc_iterate((const utf8proc_uint8_t *) ansi_end_ptr, *after_ansi_len, &unicode);
waddnstr(view->win, ansi_end_ptr, bytes_to_display);
ansi_end_ptr += bytes_to_display;
*after_ansi_len -= bytes_to_display;
left_widths -= 1;
}
} else {
waddnstr(view->win, ansi_end_ptr, *after_ansi_len);
}

*cur_width += *widths_of_display;
}

void
wattrset_by_ansi_status(struct view *view, struct ansi_status* cur_ansi_status) {
// Because init_extended_pair can't accept more than 32768 pairs,
// we skip the colors with color codes odd numbered and greater than 15 currently.
if (cur_ansi_status->fg < 256 && cur_ansi_status->fg > 15 && cur_ansi_status->fg % 2 == 1)
cur_ansi_status->fg -= 1;
if (cur_ansi_status->bg < 256 && cur_ansi_status->bg > 15 && cur_ansi_status->bg % 2 == 1)
cur_ansi_status->bg -= 1;
short id = color_pairs_map[cur_ansi_status->fg][cur_ansi_status->bg];
wattr_set(view->win, cur_ansi_status->attr, id, NULL);
}

short
convert_ansi_into_256_color(char **save_ptr) {
char *color_method_mark = strtok_r(NULL, ";", save_ptr);
short c256 = -1;
if (strcmp(color_method_mark, "5") == 0) {
char *color_code = strtok_r(NULL, ";", save_ptr);
c256 = atoi(color_code);
}

// WONTFIX: You can't init_color with numerous RGB code in ncurses.
// Therefore, \e[(3 or 4)8;2;r;g;bm syntax is disabled currently.
// The below code is left for when it is someday implemented.
// if (strcmp(color_method_mark, "2") == 0) {
// char *r = strtok(NULL, ";");
// char *g = strtok(NULL, ";");
// char *b = strtok(NULL, ";");
// }
// Return a color pair ID that matches this rgb combination.

return c256;
}

/* vim: set ts=8 sw=8 noexpandtab: */
10 changes: 8 additions & 2 deletions src/apps.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,14 @@ struct app_external
&& app_diff_highlight_path_search(dhlt_path, sizeof(dhlt_path), query)
&& *dhlt_path) {
if (suffixcmp(dhlt_path, strlen(dhlt_path), "/diff-highlight.perl")) {
dhlt_app.argv[0] = dhlt_path;
dhlt_app.argv[1] = NULL;
if (strcmp(strrchr(dhlt_path, '/'), "/delta") == 0) {
dhlt_app.argv[0] = dhlt_path;
dhlt_app.argv[1] = "--true-color=never";
dhlt_app.argv[2] = NULL;
} else {
dhlt_app.argv[0] = dhlt_path;
dhlt_app.argv[1] = NULL;
}
} else if (path_search(perl_path, sizeof(perl_path), "perl", getenv("PATH"), X_OK)) {
/* if the package manager failed to "make install" within the contrib dir, rescue via */
/* perl -MDiffHighlight -I/path/containing /path/containing/diff-highlight.perl */
Expand Down
65 changes: 65 additions & 0 deletions src/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "tig/options.h"
#include "compat/hashtab.h"

#if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20180127
#include "tig/ansi.h"
#endif

static const enum line_type palette_colors[] = {
LINE_PALETTE_0,
LINE_PALETTE_1,
Expand Down Expand Up @@ -91,6 +95,56 @@ draw_chars(struct view *view, enum line_type type, const char *string, int lengt
return VIEW_MAX_LEN(view) <= 0;
}

static bool
draw_chars_with_ansi(struct view *view, enum line_type type, const char *string, int length,
int max_width, bool use_tilde)
{
int len = 0;
int col = 0;
int trimmed = false;
size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;

if (max_width <= 0)
return VIEW_MAX_LEN(view) <= 0;

if (opt_iconv_out != ICONV_NONE) {
string = encoding_iconv(opt_iconv_out, string, len);
if (!string)
return VIEW_MAX_LEN(view) <= 0;
}

set_view_attr(view, type);

int ansi_num = 0;
int len_with_ansi = strlen(string);
int max_num = (len_with_ansi / 4) + 1;
int max_len = (len_with_ansi - 4) + 1;
char **ansi_ptrs = (char **)malloc(sizeof(char *) * max_num);
char *ansi_ptrs_for_free = (char *)malloc(sizeof(char) * max_num * max_len);
for (int i = 0; i < max_num; i++)
ansi_ptrs[i] = ansi_ptrs_for_free + i * max_len;
split_ansi(string, &ansi_num, ansi_ptrs);

if (ansi_num > 0)
draw_ansi(view, &ansi_num, ansi_ptrs, max_width, skip);
else {
len = utf8_length(&string, length, skip, &col, max_width, &trimmed, use_tilde, opt_tab_size);
waddnstr(view->win, string, len);
}

free(ansi_ptrs_for_free);
free(ansi_ptrs);

if (trimmed && use_tilde) {
set_view_attr(view, LINE_DELIMITER);
waddstr(view->win, opt_truncation_delimiter ? opt_truncation_delimiter : "~");
col++;
}

view->col += col;
return VIEW_MAX_LEN(view) <= 0;
}

static bool
draw_space(struct view *view, enum line_type type, int max, int spaces)
{
Expand Down Expand Up @@ -121,8 +175,19 @@ draw_text_expanded(struct view *view, enum line_type type, const char *string, i
size_t pos = string_expand(text, sizeof(text), string, length, opt_tab_size);
size_t col = view->col;

#if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20180127
if (strstr(string, "\033[") != NULL) {
if (draw_chars_with_ansi(view, type, text, -1, max_width, use_tilde))
return true;
} else {
if (draw_chars(view, type, text, -1, max_width, use_tilde))
return true;
}
#else
if (draw_chars(view, type, text, -1, max_width, use_tilde))
return true;
#endif

string += pos;
length -= pos;
max_width -= view->col - col;
Expand Down
Loading