patch 8.0.1685: can't set ANSI colors of a terminal window
Problem: Can't set ANSI colors of a terminal window.
Solution: Add term_setansicolors(), term_getansicolors() and
g:term_ansi_colors. (Andy Massimino, closes #2747)
diff --git a/src/channel.c b/src/channel.c
index 88c53b7..a25e9fe 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -4802,6 +4802,50 @@
opt->jo_set2 |= JO2_TERM_KILL;
opt->jo_term_kill = get_tv_string_chk(item);
}
+# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ else if (STRCMP(hi->hi_key, "ansi_colors") == 0)
+ {
+ int n = 0;
+ listitem_T *li;
+ long_u rgb[16];
+
+ if (!(supported2 & JO2_ANSI_COLORS))
+ break;
+
+ if (item == NULL || item->v_type != VAR_LIST
+ || item->vval.v_list == NULL)
+ {
+ EMSG2(_(e_invargval), "ansi_colors");
+ return FAIL;
+ }
+
+ li = item->vval.v_list->lv_first;
+ for (; li != NULL && n < 16; li = li->li_next, n++)
+ {
+ char_u *color_name;
+ guicolor_T guicolor;
+
+ color_name = get_tv_string_chk(&li->li_tv);
+ if (color_name == NULL)
+ return FAIL;
+
+ guicolor = GUI_GET_COLOR(color_name);
+ if (guicolor == INVALCOLOR)
+ return FAIL;
+
+ rgb[n] = GUI_MCH_GET_RGB(guicolor);
+ }
+
+ if (n != 16 || li != NULL)
+ {
+ EMSG2(_(e_invargval), "ansi_colors");
+ return FAIL;
+ }
+
+ opt->jo_set2 |= JO2_ANSI_COLORS;
+ memcpy(opt->jo_ansi_colors, rgb, sizeof(rgb));
+ }
+# endif
#endif
else if (STRCMP(hi->hi_key, "env") == 0)
{
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 1ffbb21..65279a5 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -856,6 +856,9 @@
{"term_dumpload", 1, 2, f_term_dumpload},
{"term_dumpwrite", 2, 3, f_term_dumpwrite},
{"term_getaltscreen", 1, 1, f_term_getaltscreen},
+# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ {"term_getansicolors", 1, 1, f_term_getansicolors},
+# endif
{"term_getattr", 2, 2, f_term_getattr},
{"term_getcursor", 1, 1, f_term_getcursor},
{"term_getjob", 1, 1, f_term_getjob},
@@ -868,6 +871,9 @@
{"term_list", 0, 0, f_term_list},
{"term_scrape", 2, 2, f_term_scrape},
{"term_sendkeys", 2, 2, f_term_sendkeys},
+# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ {"term_setansicolors", 2, 2, f_term_setansicolors},
+# endif
{"term_setkill", 2, 2, f_term_setkill},
{"term_setrestore", 2, 2, f_term_setrestore},
{"term_start", 1, 2, f_term_start},
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 0ef7783..f7cea5a 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -32,6 +32,7 @@
void f_term_dumpdiff(typval_T *argvars, typval_T *rettv);
void f_term_dumpload(typval_T *argvars, typval_T *rettv);
void f_term_getaltscreen(typval_T *argvars, typval_T *rettv);
+void f_term_getansicolors(typval_T *argvars, typval_T *rettv);
void f_term_getattr(typval_T *argvars, typval_T *rettv);
void f_term_getcursor(typval_T *argvars, typval_T *rettv);
void f_term_getjob(typval_T *argvars, typval_T *rettv);
@@ -44,6 +45,7 @@
void f_term_list(typval_T *argvars, typval_T *rettv);
void f_term_scrape(typval_T *argvars, typval_T *rettv);
void f_term_sendkeys(typval_T *argvars, typval_T *rettv);
+void f_term_setansicolors(typval_T *argvars, typval_T *rettv);
void f_term_setrestore(typval_T *argvars, typval_T *rettv);
void f_term_setkill(typval_T *argvars, typval_T *rettv);
void f_term_start(typval_T *argvars, typval_T *rettv);
diff --git a/src/structs.h b/src/structs.h
index db2792b..d3f98ac 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1708,7 +1708,7 @@
#define JO2_EOF_CHARS 0x1000 /* "eof_chars" */
#define JO2_NORESTORE 0x2000 /* "norestore" */
#define JO2_TERM_KILL 0x4000 /* "term_kill" */
-#define JO2_ALL 0x7FFF
+#define JO2_ANSI_COLORS 0x8000 /* "ansi_colors" */
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
#define JO_CB_ALL \
@@ -1777,6 +1777,9 @@
int jo_term_finish;
char_u *jo_eof_chars;
char_u *jo_term_kill;
+# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ long_u jo_ansi_colors[16];
+# endif
#endif
} jobopt_T;
diff --git a/src/terminal.c b/src/terminal.c
index a65fc00..7859941 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -38,11 +38,11 @@
* in tl_scrollback are no longer used.
*
* TODO:
- * - Add a way to set the 16 ANSI colors, to be used for 'termguicolors' and in
- * the GUI. #2747
* - Win32: Make terminal used for :!cmd in the GUI work better. Allow for
* redirection. Probably in call to channel_set_pipes().
* - implement term_setsize()
+ * - add an optional limit for the scrollback size. When reaching it remove
+ * 10% at the start.
* - Copy text in the vterm to the Vim buffer once in a while, so that
* completion works.
* - in GUI vertical split causes problems. Cursor is flickering. (Hirohito
@@ -64,8 +64,6 @@
* http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
* - when 'encoding' is not utf-8, or the job is using another encoding, setup
* conversions.
- * - add an optional limit for the scrollback size. When reaching it remove
- * 10% at the start.
*/
#include "vim.h"
@@ -3141,6 +3139,75 @@
}
}
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+/*
+ * Set the 16 ANSI colors from array of RGB values
+ */
+ static void
+set_vterm_palette(VTerm *vterm, long_u *rgb)
+{
+ int index = 0;
+ VTermState *state = vterm_obtain_state(vterm);
+ for (; index < 16; index++)
+ {
+ VTermColor color;
+ color.red = (unsigned)(rgb[index] >> 16);
+ color.green = (unsigned)(rgb[index] >> 8) & 255;
+ color.blue = (unsigned)rgb[index] & 255;
+ vterm_state_set_palette_color(state, index, &color);
+ }
+}
+
+/*
+ * Set the ANSI color palette from a list of colors
+ */
+ static int
+set_ansi_colors_list(VTerm *vterm, list_T *list)
+{
+ int n = 0;
+ long_u rgb[16];
+ listitem_T *li = list->lv_first;
+
+ for (; li != NULL && n < 16; li = li->li_next, n++)
+ {
+ char_u *color_name;
+ guicolor_T guicolor;
+
+ color_name = get_tv_string_chk(&li->li_tv);
+ if (color_name == NULL)
+ return FAIL;
+
+ guicolor = GUI_GET_COLOR(color_name);
+ if (guicolor == INVALCOLOR)
+ return FAIL;
+
+ rgb[n] = GUI_MCH_GET_RGB(guicolor);
+ }
+
+ if (n != 16 || li != NULL)
+ return FAIL;
+
+ set_vterm_palette(vterm, rgb);
+
+ return OK;
+}
+
+/*
+ * Initialize the ANSI color palette from g:terminal_ansi_colors[0:15]
+ */
+ static void
+init_vterm_ansi_colors(VTerm *vterm)
+{
+ dictitem_T *var = find_var((char_u *)"g:terminal_ansi_colors", NULL, TRUE);
+
+ if (var != NULL
+ && (var->di_tv.v_type != VAR_LIST
+ || var->di_tv.vval.v_list == NULL
+ || set_ansi_colors_list(vterm, var->di_tv.vval.v_list) == FAIL))
+ EMSG2(_(e_invarg2), "g:terminal_ansi_colors");
+}
+#endif
+
/*
* Handles a "drop" command from the job in the terminal.
* "item" is the file name, "item->li_next" may have options.
@@ -3372,6 +3439,9 @@
&term->tl_default_color.fg,
&term->tl_default_color.bg);
+ if (t_colors >= 16)
+ vterm_state_set_bold_highbright(vterm_obtain_state(vterm), 1);
+
/* Required to initialize most things. */
vterm_screen_reset(screen, 1 /* hard */);
@@ -4762,6 +4832,68 @@
}
}
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
+/*
+ * "term_getansicolors(buf)" function
+ */
+ void
+f_term_getansicolors(typval_T *argvars, typval_T *rettv)
+{
+ buf_T *buf = term_get_buf(argvars, "term_getansicolors()");
+ term_T *term;
+ VTermState *state;
+ VTermColor color;
+ char_u hexbuf[10];
+ int index;
+ list_T *list;
+
+ if (rettv_list_alloc(rettv) == FAIL)
+ return;
+
+ if (buf == NULL)
+ return;
+ term = buf->b_term;
+ if (term->tl_vterm == NULL)
+ return;
+
+ list = rettv->vval.v_list;
+ state = vterm_obtain_state(term->tl_vterm);
+ for (index = 0; index < 16; index++)
+ {
+ vterm_state_get_palette_color(state, index, &color);
+ sprintf((char *)hexbuf, "#%02x%02x%02x",
+ color.red, color.green, color.blue);
+ if (list_append_string(list, hexbuf, 7) == FAIL)
+ return;
+ }
+}
+
+/*
+ * "term_setansicolors(buf, list)" function
+ */
+ void
+f_term_setansicolors(typval_T *argvars, typval_T *rettv UNUSED)
+{
+ buf_T *buf = term_get_buf(argvars, "term_setansicolors()");
+ term_T *term;
+
+ if (buf == NULL)
+ return;
+ term = buf->b_term;
+ if (term->tl_vterm == NULL)
+ return;
+
+ if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL)
+ {
+ EMSG(_(e_listreq));
+ return;
+ }
+
+ if (set_ansi_colors_list(term->tl_vterm, argvars[1].vval.v_list) == FAIL)
+ EMSG(_(e_invarg));
+}
+#endif
+
/*
* "term_setrestore(buf, command)" function
*/
@@ -4824,7 +4956,8 @@
JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD
+ JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
+ JO2_CWD + JO2_ENV + JO2_EOF_CHARS
- + JO2_NORESTORE + JO2_TERM_KILL) == FAIL)
+ + JO2_NORESTORE + JO2_TERM_KILL
+ + JO2_ANSI_COLORS) == FAIL)
return;
buf = term_start(&argvars[0], NULL, &opt, 0);
@@ -5152,6 +5285,13 @@
create_vterm(term, term->tl_rows, term->tl_cols);
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ if (opt->jo_set2 & JO2_ANSI_COLORS)
+ set_vterm_palette(term->tl_vterm, opt->jo_ansi_colors);
+ else
+ init_vterm_ansi_colors(term->tl_vterm);
+#endif
+
channel_set_job(channel, job, opt);
job_set_options(job, opt);
@@ -5324,6 +5464,13 @@
{
create_vterm(term, term->tl_rows, term->tl_cols);
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ if (opt->jo_set2 & JO2_ANSI_COLORS)
+ set_vterm_palette(term->tl_vterm, opt->jo_ansi_colors);
+ else
+ init_vterm_ansi_colors(term->tl_vterm);
+#endif
+
/* This may change a string in "argvar". */
term->tl_job = job_start(argvar, argv, opt);
if (term->tl_job != NULL)
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 5645625..3b95d39 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -1255,3 +1255,73 @@
call ch_logfile('', '')
call delete('Xlog')
endfunc
+
+func Test_terminal_ansicolors_default()
+ let colors = [
+ \ '#000000', '#e00000',
+ \ '#00e000', '#e0e000',
+ \ '#0000e0', '#e000e0',
+ \ '#00e0e0', '#e0e0e0',
+ \ '#808080', '#ff4040',
+ \ '#40ff40', '#ffff40',
+ \ '#4040ff', '#ff40ff',
+ \ '#40ffff', '#ffffff',
+ \]
+
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(colors, term_getansicolors(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ exe buf . 'bwipe'
+endfunc
+
+let s:test_colors = [
+ \ '#616e64', '#0d0a79',
+ \ '#6d610d', '#0a7373',
+ \ '#690d0a', '#6d696e',
+ \ '#0d0a6f', '#616e0d',
+ \ '#0a6479', '#6d0d0a',
+ \ '#617373', '#0d0a69',
+ \ '#6d690d', '#0a6e6f',
+ \ '#610d0a', '#6e6479',
+ \]
+
+func Test_terminal_ansicolors_global()
+ let g:terminal_ansi_colors = reverse(copy(s:test_colors))
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(g:terminal_ansi_colors, term_getansicolors(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ exe buf . 'bwipe'
+ unlet g:terminal_ansi_colors
+endfunc
+
+func Test_terminal_ansicolors_func()
+ let g:terminal_ansi_colors = reverse(copy(s:test_colors))
+ let buf = Run_shell_in_terminal({'ansi_colors': s:test_colors})
+ call assert_equal(s:test_colors, term_getansicolors(buf))
+
+ call term_setansicolors(buf, g:terminal_ansi_colors)
+ call assert_equal(g:terminal_ansi_colors, term_getansicolors(buf))
+
+ let colors = [
+ \ 'ivory', 'AliceBlue',
+ \ 'grey67', 'dark goldenrod',
+ \ 'SteelBlue3', 'PaleVioletRed4',
+ \ 'MediumPurple2', 'yellow2',
+ \ 'RosyBrown3', 'OrangeRed2',
+ \ 'white smoke', 'navy blue',
+ \ 'grey47', 'gray97',
+ \ 'MistyRose2', 'DodgerBlue4',
+ \]
+ call term_setansicolors(buf, colors)
+
+ let colors[4] = 'Invalid'
+ call assert_fails('call term_setansicolors(buf, colors)', 'E474:')
+
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+endfunc
diff --git a/src/version.c b/src/version.c
index 2890f71..ff8d0c4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1685,
+/**/
1684,
/**/
1683,