Compare commits

...

16 Commits

Author SHA1 Message Date
Bram Moolenaar
5a80f8ad5d patch 8.2.0809: build failure with small features
Problem:    Build failure with small features. (Tony Mechelynck)
Solution:   Move "expr" inside #ifdef.
2020-05-22 13:38:18 +02:00
Bram Moolenaar
98f1671cc0 patch 8.2.0808: not enough testing for the terminal window
Problem:    Not enough testing for the terminal window.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #6069)  Fix memory
            leak.
2020-05-22 13:34:01 +02:00
Bram Moolenaar
4c9243f9fb patch 8.2.0807: cannot easily restore a mapping
Problem:    Cannot easily restore a mapping.
Solution:   Add mapset().
2020-05-22 13:10:44 +02:00
Bram Moolenaar
74fae513f8 patch 8.2.0806: using "func!" after vim9script gives confusing error
Problem:    using "func!" after vim9script gives confusing error.
Solution:   Give E477. (closes #6107)
2020-05-21 21:50:58 +02:00
Bram Moolenaar
fe81389d60 patch 8.2.0805: terminal key codes test fails on some systems
Problem:    Terminal key codes test fails on some systems.
Solution:   Skip keypad 3 and 9. (Yegappan Lakshmanan, closes #6070)
2020-05-21 20:38:31 +02:00
Bram Moolenaar
e5886ccb51 patch 8.2.0804: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revision 727, but add the index instead of switching
            between RGB and indexed.
2020-05-21 20:10:04 +02:00
Bram Moolenaar
83a52533b2 patch 8.2.0803: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revisions 764 - 767
2020-05-20 19:30:19 +02:00
Bram Moolenaar
d863728913 patch 8.2.0802: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revisions 759 - 762.
2020-05-20 18:41:41 +02:00
Bram Moolenaar
eaa3e0dae5 patch 8.2.0801: terminal test fails on Mac
Problem:    Terminal test fails on Mac.
Solution:   Concatenate OSC pieces.
2020-05-19 23:11:00 +02:00
Bram Moolenaar
a09195f29e patch 8.2.0800: errors from failing test are unclear
Problem:    Errors from failing test are unclear.
Solution:   Include text where parsing failed.
2020-05-19 22:38:59 +02:00
Bram Moolenaar
74c6963656 patch 8.2.0799: build fails if snprintf is not available
Problem:    Build fails if snprintf is not available.
Solution:   Use vim_snprintf().
2020-05-19 21:43:47 +02:00
Bram Moolenaar
be593bf135 patch 8.2.0798: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revisions 755 - 758.
2020-05-19 21:20:04 +02:00
Bram Moolenaar
904e48a22b patch 8.2.0797: MS-Windows: compiler still can't handle C99 construct
Problem:    MS-Windows: compiler still can't handle C99 construct.
Solution:   Change to C90 construct. (Dominique Pelle, closes #6106)
2020-05-19 10:33:02 +02:00
Bram Moolenaar
37ebd42f16 patch 8.2.0796: MS-Windows: compiler can't handle C99 construct in libvterm
Problem:    MS-Windows: compiler can't handle C99 construct in libvterm.
Solution:   Change to C90 construct.
2020-05-18 23:27:50 +02:00
Bram Moolenaar
88d68de95d patch 8.2.0795: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revisions 748 - 754.
2020-05-18 21:51:01 +02:00
Bram Moolenaar
d098b824c1 patch 8.2.0794: libvterm code lags behind the upstream version
Problem:    Libvterm code lags behind the upstream version.
Solution:   Include revisions 743 - 747.
2020-05-18 21:12:59 +02:00
40 changed files with 1535 additions and 767 deletions

View File

@@ -2586,6 +2586,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]])
rhs of mapping {name} in mode {mode}
mapcheck({name} [, {mode} [, {abbr}]])
String check for mappings matching {name}
mapset({name}, {mode}, {abbr}, {dict}
none restore mapping from |maparg()| result
match({expr}, {pat} [, {start} [, {count}]])
Number position where {pat} matches in {expr}
matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
@@ -6794,6 +6796,7 @@ map({expr1}, {expr2}) *map()*
Can also be used as a |method|: >
mylist->map(expr2)
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
When {dict} is omitted or zero: Return the rhs of mapping
{name} in mode {mode}. The returned String has special
@@ -6844,6 +6847,10 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
"lnum" The line number in "sid", zero if unknown.
"nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|).
"simplified"
The dictionary can be used to restore a mapping with
|mapset()|.
The mappings local to the current buffer are checked first,
then the global mappings.
@@ -6890,6 +6897,18 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
Can also be used as a |method|: >
GetKey()->mapcheck('n')
mapset({mode}, {abbr}, {dict}) *mapset()*
Restore a mapping from a dictionary returned by |maparg()|.
{name}, {mode} and {abbr} should be the same as for the call
to |maparg()|.
{mode} is used to define the mode in which the mapping is set,
not the "mode" entry in {dict}.
Example for saving and restoring a mapping: >
let save_map = maparg('K', 'n', 0, 1)
nnoremap K somethingelse
...
call mapset('n', 0, save_map)
<
match({expr}, {pat} [, {start} [, {count}]]) *match()*
When {expr} is a |List| then this returns the index of the
first item where {pat} matches. Each item is used as a

View File

@@ -3562,7 +3562,7 @@ objects/vterm_screen.o: libvterm/src/screen.c $(TERM_DEPS)
objects/vterm_state.o: libvterm/src/state.c $(TERM_DEPS)
$(CCCTERM) -o $@ libvterm/src/state.c
objects/vterm_unicode.o: libvterm/src/unicode.c $(TERM_DEPS)
objects/vterm_unicode.o: libvterm/src/unicode.c $(TERM_DEPS) libvterm/src/fullwidth.inc
$(CCCTERM) -o $@ libvterm/src/unicode.c
objects/vterm_vterm.o: libvterm/src/vterm.c $(TERM_DEPS)

View File

@@ -664,6 +664,7 @@ static funcentry_T global_functions[] =
{"map", 2, 2, FEARG_1, ret_any, f_map},
{"maparg", 1, 4, FEARG_1, ret_string, f_maparg},
{"mapcheck", 1, 3, FEARG_1, ret_string, f_mapcheck},
{"mapset", 3, 3, FEARG_1, ret_void, f_mapset},
{"match", 2, 4, FEARG_1, ret_any, f_match},
{"matchadd", 2, 5, FEARG_1, ret_number, f_matchadd},
{"matchaddpos", 2, 5, FEARG_1, ret_number, f_matchaddpos},

View File

@@ -20,6 +20,8 @@
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
static char e_json_error[] = N_("E491: json decode error at '%s'");
/*
* Encode "val" into a JSON format string.
* The result is added to "gap"
@@ -740,7 +742,7 @@ json_decode_item(js_read_T *reader, typval_T *res, int options)
retval = json_decode_string(reader, cur_item, *p);
else
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
}
break;
@@ -748,7 +750,7 @@ json_decode_item(js_read_T *reader, typval_T *res, int options)
case ',': // comma: empty item
if ((options & JSON_JS) == 0)
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
break;
}
@@ -778,7 +780,7 @@ json_decode_item(js_read_T *reader, typval_T *res, int options)
}
if (!VIM_ISDIGIT(*sp))
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
break;
}
@@ -809,7 +811,7 @@ json_decode_item(js_read_T *reader, typval_T *res, int options)
&nr, NULL, 0, TRUE);
if (len == 0)
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
goto theend;
}
@@ -962,7 +964,7 @@ item_end:
retval = MAYBE;
else
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
}
goto theend;
@@ -980,7 +982,7 @@ item_end:
retval = MAYBE;
else
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
}
goto theend;
@@ -1036,7 +1038,7 @@ item_end:
retval = MAYBE;
else
{
emsg(_(e_invarg));
semsg(_(e_json_error), p);
retval = FAIL;
}
goto theend;
@@ -1055,7 +1057,7 @@ item_end:
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
}
emsg(_(e_invarg));
semsg(_(e_json_error), p);
theend:
ga_clear(&stack);
@@ -1079,7 +1081,7 @@ json_decode_all(js_read_T *reader, typval_T *res, int options)
if (ret != OK)
{
if (ret == MAYBE)
emsg(_(e_invarg));
semsg(_(e_json_error), reader->js_buf);
return FAIL;
}
json_skip_white(reader);

View File

@@ -170,6 +170,7 @@ between states.
SGR 4:x = Underline style
123 SGR 5 = Blink on
123 SGR 7 = Reverse on
SGR 8 = Conceal on
SGR 9 = Strikethrough on
SGR 10-19 = Select font
SGR 21 = Underline double
@@ -178,6 +179,7 @@ between states.
23 SGR 24 = Underline off
23 SGR 25 = Blink off
23 SGR 27 = Reverse off
SGR 28 = Conceal off
SGR 29 = Strikethrough off
SGR 30-37 = Foreground ANSI
SGR 38 = Foreground alternative palette

View File

@@ -3,16 +3,12 @@
use strict;
use warnings;
use Unicode::UCD qw( charprop );
STDOUT->autoflush(1);
sub iswide
{
my ( $cp ) = @_;
my $width = charprop( $cp, "East_Asian_Width" ) or return;
return $width eq "Wide" || $width eq "Fullwidth";
return chr($cp) =~ m/\p{East_Asian_Width=Wide}|\p{East_Asian_Width=Fullwidth}/;
}
my ( $start, $end );

View File

@@ -85,18 +85,107 @@ INLINE void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
}
#endif
// The ansi_index is used for the lower 16 colors, which can be set to any
// color.
#define VTERM_ANSI_INDEX_DEFAULT 0 // color cleared
#define VTERM_ANSI_INDEX_MIN 1
#define VTERM_ANSI_INDEX_MAX 16
#define VTERM_ANSI_INDEX_NONE 255 // non-ANSI color, use red/green/blue
/**
* Bit-field describing the value of VTermColor.type
*/
typedef enum {
/**
* If the lower bit of `type` is not set, the colour is 24-bit RGB.
*/
VTERM_COLOR_RGB = 0x00,
/**
* The colour is an index into a palette of 256 colours.
*/
VTERM_COLOR_INDEXED = 0x01,
/**
* Mask that can be used to extract the RGB/Indexed bit.
*/
VTERM_COLOR_TYPE_MASK = 0x01,
/**
* If set, indicates that this colour should be the default foreground
* color, i.e. there was no SGR request for another colour. When
* rendering this colour it is possible to ignore "idx" and just use a
* colour that is not in the palette.
*/
VTERM_COLOR_DEFAULT_FG = 0x02,
/**
* If set, indicates that this colour should be the default background
* color, i.e. there was no SGR request for another colour. A common
* option when rendering this colour is to not render a background at
* all, for example by rendering the window transparently at this spot.
*/
VTERM_COLOR_DEFAULT_BG = 0x04,
/**
* Mask that can be used to extract the default foreground/background bit.
*/
VTERM_COLOR_DEFAULT_MASK = 0x06
} VTermColorType;
/**
* Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
* given VTermColor instance is an indexed colour.
*/
#define VTERM_COLOR_IS_INDEXED(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
/**
* Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
* the given VTermColor instance is an rgb colour.
*/
#define VTERM_COLOR_IS_RGB(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
/**
* Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default foreground
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_FG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_FG))
/**
* Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default background
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_BG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_BG))
typedef struct {
/**
* Tag indicating which member is actually valid.
* Please use the `VTERM_COLOR_IS_*` test macros to check whether a
* particular type flag is set.
*/
uint8_t type;
uint8_t red, green, blue;
uint8_t ansi_index;
uint8_t index;
} VTermColor;
/**
* Constructs a new VTermColor instance representing the given RGB values.
*/
void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue);
/**
* Construct a new VTermColor instance representing an indexed color with the
* given index.
*/
void vterm_color_indexed(VTermColor *col, uint8_t idx);
/**
* Compares two colours. Returns true if the colors are equal, false otherwise.
*/
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
typedef enum {
// VTERM_VALUETYPE_NONE = 0
VTERM_VALUETYPE_BOOL = 1,
@@ -107,10 +196,17 @@ typedef enum {
VTERM_N_VALUETYPES
} VTermValueType;
typedef struct {
const char *str;
size_t len : 30;
unsigned int initial : 1;
unsigned int final : 1;
} VTermStringFragment;
typedef union {
int boolean;
int number;
char *string;
VTermStringFragment string;
VTermColor color;
} VTermValue;
@@ -121,6 +217,7 @@ typedef enum {
VTERM_ATTR_ITALIC, // bool: 3, 23
VTERM_ATTR_BLINK, // bool: 5, 25
VTERM_ATTR_REVERSE, // bool: 7, 27
VTERM_ATTR_CONCEAL, // bool: 8, 28
VTERM_ATTR_STRIKE, // bool: 9, 29
VTERM_ATTR_FONT, // number: 10-19
VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
@@ -170,8 +267,9 @@ typedef struct {
} VTermGlyphInfo;
typedef struct {
unsigned int doublewidth:1; // DECDWL or DECDHL line
unsigned int doubleheight:2; // DECDHL line (1=top 2=bottom)
unsigned int doublewidth:1; /* DECDWL or DECDHL line */
unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
unsigned int continuation:1; /* Line is a flow continuation of the previous */
} VTermLineInfo;
typedef struct {
@@ -256,8 +354,8 @@ typedef struct {
int (*control)(unsigned char control, void *user);
int (*escape)(const char *bytes, size_t len, void *user);
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
int (*osc)(const char *command, size_t cmdlen, void *user);
int (*dcs)(const char *command, size_t cmdlen, void *user);
int (*osc)(int command, VTermStringFragment frag, void *user);
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
int (*resize)(int rows, int cols, void *user);
} VTermParserCallbacks;
@@ -268,6 +366,15 @@ void *vterm_parser_get_cbdata(VTerm *vt);
// State layer
// -----------
/* Copies of VTermState fields that the 'resize' callback might have reason to
* edit. 'resize' callback gets total control of these fields and may
* free-and-reallocate them if required. They will be copied back from the
* struct after the callback has returned.
*/
typedef struct {
VTermPos pos; /* current cursor position */
} VTermStateFields;
typedef struct {
int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
@@ -280,7 +387,7 @@ typedef struct {
// was accepted, 0 otherwise.
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
int (*bell)(void *user);
int (*resize)(int rows, int cols, VTermPos *delta, void *user);
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
} VTermStateCallbacks;
@@ -297,13 +404,19 @@ typedef struct {
// useful to add protocol?
} VTermMouseState;
typedef struct {
int (*control)(unsigned char control, void *user);
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
int (*osc)(int command, VTermStringFragment frag, void *user);
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
} VTermStateFallbacks;
VTermState *vterm_obtain_state(VTerm *vt);
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
void *vterm_state_get_cbdata(VTermState *state);
// Only invokes control, csi, osc, dcs
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *fallbacks, void *user);
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
// Initialize the state.
@@ -322,6 +435,18 @@ void vterm_state_focus_in(VTermState *state);
void vterm_state_focus_out(VTermState *state);
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
/**
* Makes sure that the given color `col` is indeed an RGB colour. After this
* function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
* flags stored in `col->type` will have been reset.
*
* @param state is the VTermState instance from which the colour palette should
* be extracted.
* @param col is a pointer at the VTermColor instance that should be converted
* to an RGB colour.
*/
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
// ------------
// Screen layer
// ------------
@@ -332,6 +457,7 @@ typedef struct {
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int conceal : 1;
unsigned int strike : 1;
unsigned int font : 4; // 0 to 9
unsigned int dwl : 1; // On a DECDWL or DECDHL line
@@ -378,8 +504,7 @@ VTermScreen *vterm_obtain_screen(VTerm *vt);
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
void *vterm_screen_get_cbdata(VTermScreen *screen);
// Only invokes control, csi, osc, dcs
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user);
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
// Enable support for using the alternate screen if "altscreen" is non-zero.
@@ -421,8 +546,9 @@ typedef enum {
VTERM_ATTR_FONT_MASK = 1 << 6,
VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
VTERM_ATTR_CONCEAL_MASK = 1 << 9,
VTERM_ALL_ATTRS_MASK = (1 << 9) - 1
VTERM_ALL_ATTRS_MASK = (1 << 10) - 1
} VTermAttrMask;
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
@@ -431,6 +557,12 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
/**
* Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
* instance.
*/
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
// ---------
// Utilities
// ---------

View File

@@ -40,14 +40,13 @@
{ 0x3000, 0x303e },
{ 0x3041, 0x3096 },
{ 0x3099, 0x30ff },
{ 0x3105, 0x312d },
{ 0x3105, 0x312f },
{ 0x3131, 0x318e },
{ 0x3190, 0x31ba },
{ 0x31c0, 0x31e3 },
{ 0x31f0, 0x321e },
{ 0x3220, 0x3247 },
{ 0x3250, 0x32fe },
{ 0x3300, 0x4dbf },
{ 0x3250, 0x4dbf },
{ 0x4e00, 0xa48c },
{ 0xa490, 0xa4c6 },
{ 0xa960, 0xa97c },
@@ -59,10 +58,13 @@
{ 0xfe68, 0xfe6b },
{ 0xff01, 0xff60 },
{ 0xffe0, 0xffe6 },
{ 0x16fe0, 0x16fe0 },
{ 0x17000, 0x187ec },
{ 0x16fe0, 0x16fe3 },
{ 0x17000, 0x187f7 },
{ 0x18800, 0x18af2 },
{ 0x1b000, 0x1b001 },
{ 0x1b000, 0x1b11e },
{ 0x1b150, 0x1b152 },
{ 0x1b164, 0x1b167 },
{ 0x1b170, 0x1b2fb },
{ 0x1f004, 0x1f004 },
{ 0x1f0cf, 0x1f0cf },
{ 0x1f18e, 0x1f18e },
@@ -71,6 +73,7 @@
{ 0x1f210, 0x1f23b },
{ 0x1f240, 0x1f248 },
{ 0x1f250, 0x1f251 },
{ 0x1f260, 0x1f265 },
{ 0x1f300, 0x1f320 },
{ 0x1f32d, 0x1f335 },
{ 0x1f337, 0x1f37c },
@@ -92,13 +95,17 @@
{ 0x1f680, 0x1f6c5 },
{ 0x1f6cc, 0x1f6cc },
{ 0x1f6d0, 0x1f6d2 },
{ 0x1f6d5, 0x1f6d5 },
{ 0x1f6eb, 0x1f6ec },
{ 0x1f6f4, 0x1f6f6 },
{ 0x1f910, 0x1f91e },
{ 0x1f920, 0x1f927 },
{ 0x1f930, 0x1f930 },
{ 0x1f933, 0x1f93e },
{ 0x1f940, 0x1f94b },
{ 0x1f950, 0x1f95e },
{ 0x1f980, 0x1f991 },
{ 0x1f9c0, 0x1f9c0 },
{ 0x1f6f4, 0x1f6fa },
{ 0x1f7e0, 0x1f7eb },
{ 0x1f90d, 0x1f971 },
{ 0x1f973, 0x1f976 },
{ 0x1f97a, 0x1f9a2 },
{ 0x1f9a5, 0x1f9aa },
{ 0x1f9ae, 0x1f9ca },
{ 0x1f9cd, 0x1f9ff },
{ 0x1fa70, 0x1fa73 },
{ 0x1fa78, 0x1fa7a },
{ 0x1fa80, 0x1fa82 },
{ 0x1fa90, 0x1fa95 },

View File

@@ -23,10 +23,10 @@ static void do_csi(VTerm *vt, char command)
{
#ifdef DEBUG_PARSER
printf("Parsed CSI args as:\n", arglen, args);
printf(" leader: %s\n", vt->parser.csi_leader);
for(int argi = 0; argi < vt->parser.csi_argi; argi++) {
printf(" %lu", CSI_ARG(vt->parser.csi_args[argi]));
if(!CSI_ARG_HAS_MORE(vt->parser.csi_args[argi]))
printf(" leader: %s\n", vt->parser.v.csi.leader);
for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
printf("\n");
printf(" intermed: %s\n", vt->parser.intermed);
}
@@ -34,9 +34,9 @@ static void do_csi(VTerm *vt, char command)
if(vt->parser.callbacks && vt->parser.callbacks->csi)
if((*vt->parser.callbacks->csi)(
vt->parser.csi_leaderlen ? vt->parser.csi_leader : NULL,
vt->parser.csi_args,
vt->parser.csi_argi,
vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
vt->parser.v.csi.args,
vt->parser.v.csi.argi,
vt->parser.intermedlen ? vt->parser.intermed : NULL,
command,
vt->parser.cbdata))
@@ -61,65 +61,36 @@ static void do_escape(VTerm *vt, char command)
DEBUG_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", command);
}
static void append_strbuffer(VTerm *vt, const char *str, size_t len)
static void string_fragment(VTerm *vt, const char *str, size_t len, int final)
{
if(len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) {
len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur;
DEBUG_LOG1("Truncating strbuffer preserve to %zu bytes\n", len);
VTermStringFragment frag;
frag.str = str;
frag.len = len;
frag.initial = vt->parser.string_initial;
frag.final = final;
switch(vt->parser.state) {
case OSC:
if(vt->parser.callbacks && vt->parser.callbacks->osc)
(*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
break;
case DCS:
if(len && vt->parser.callbacks && vt->parser.callbacks->dcs)
(*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
break;
case NORMAL:
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
case OSC_COMMAND:
case DCS_COMMAND:
break;
}
if(len > 0) {
strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len);
vt->parser.strbuffer_cur += len;
}
}
static void start_string(VTerm *vt, VTermParserStringType type)
{
vt->parser.stringtype = type;
vt->parser.strbuffer_cur = 0;
}
static void more_string(VTerm *vt, const char *str, size_t len)
{
append_strbuffer(vt, str, len);
}
static void done_string(VTerm *vt, const char *str, size_t len)
{
if(vt->parser.strbuffer_cur) {
if(str)
append_strbuffer(vt, str, len);
str = vt->parser.strbuffer;
len = vt->parser.strbuffer_cur;
}
else if(!str) {
DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n");
len = 0;
}
switch(vt->parser.stringtype) {
case VTERM_PARSER_OSC:
if(vt->parser.callbacks && vt->parser.callbacks->osc)
if((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata))
return;
DEBUG_LOG2("libvterm: Unhandled OSC %.*s\n", (int)len, str);
return;
case VTERM_PARSER_DCS:
if(vt->parser.callbacks && vt->parser.callbacks->dcs)
if((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata))
return;
DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str);
return;
case VTERM_N_PARSER_TYPES:
return;
}
vt->parser.string_initial = FALSE;
}
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
@@ -135,43 +106,47 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
case ESC:
case OSC_COMMAND:
case DCS_COMMAND:
string_start = NULL;
break;
case STRING:
case ESC_IN_STRING:
case OSC:
case DCS:
string_start = bytes;
break;
}
#define ENTER_STRING_STATE() do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0)
#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
for( ; pos < len; pos++) {
unsigned char c = bytes[pos];
int c1_allowed = !vt->mode.utf8;
size_t string_len;
if(c == 0x00 || c == 0x7f) { // NUL, DEL
if(vt->parser.state >= STRING) {
more_string(vt, string_start, bytes + pos - string_start);
if(IS_STRING_STATE()) {
string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
string_start = bytes + pos + 1;
}
continue;
}
if(c == 0x18 || c == 0x1a) { // CAN, SUB
vt->parser.in_esc = FALSE;
ENTER_NORMAL_STATE();
continue;
}
else if(c == 0x1b) { // ESC
vt->parser.intermedlen = 0;
if(vt->parser.state == STRING)
vt->parser.state = ESC_IN_STRING;
else
ENTER_STATE(ESC);
if(!IS_STRING_STATE())
vt->parser.state = NORMAL;
vt->parser.in_esc = TRUE;
continue;
}
else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
vt->parser.state == STRING) {
IS_STRING_STATE()) {
// fallthrough
}
else if(c < 0x20) { // other C0
@@ -182,96 +157,72 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
if(pos + 2 < len && bytes[pos + 1] == 0x20 && bytes[pos + 2] == 0x08)
vt->in_backspace = 2; // Trigger when count down to 1
}
if(vt->parser.state >= STRING)
more_string(vt, string_start, bytes + pos - string_start);
if(IS_STRING_STATE())
string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
do_control(vt, c);
if(vt->parser.state >= STRING)
if(IS_STRING_STATE())
string_start = bytes + pos + 1;
continue;
}
// else fallthrough
string_len = bytes + pos - string_start;
if(vt->parser.in_esc) {
// Hoist an ESC letter into a C1 if we're not in a string mode
// Always accept ESC \ == ST even in string mode
if(!vt->parser.intermedlen &&
c >= 0x40 && c < 0x60 &&
((!IS_STRING_STATE() || c == 0x5c))) {
c += 0x40;
c1_allowed = TRUE;
string_len -= 1;
vt->parser.in_esc = FALSE;
}
else {
string_start = NULL;
vt->parser.state = NORMAL;
}
}
switch(vt->parser.state) {
case ESC_IN_STRING:
if(c == 0x5c) { // ST
vt->parser.state = STRING;
done_string(vt, string_start, bytes + pos - string_start - 1);
ENTER_NORMAL_STATE();
break;
}
vt->parser.state = ESC;
// else fallthrough
case ESC:
switch(c) {
case 0x50: // DCS
start_string(vt, VTERM_PARSER_DCS);
ENTER_STRING_STATE();
break;
case 0x5b: // CSI
vt->parser.csi_leaderlen = 0;
ENTER_STATE(CSI_LEADER);
break;
case 0x5d: // OSC
start_string(vt, VTERM_PARSER_OSC);
ENTER_STRING_STATE();
break;
default:
if(is_intermed(c)) {
if(vt->parser.intermedlen < INTERMED_MAX-1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
}
else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) {
do_control(vt, c + 0x40);
ENTER_NORMAL_STATE();
}
else if(c >= 0x30 && c < 0x7f) {
do_escape(vt, c);
ENTER_NORMAL_STATE();
}
else {
DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
}
}
break;
case CSI_LEADER:
// Extract leader bytes 0x3c to 0x3f
if(c >= 0x3c && c <= 0x3f) {
if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1)
vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c;
if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
break;
}
// else fallthrough
vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0;
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
vt->parser.csi_argi = 0;
vt->parser.csi_args[0] = CSI_ARG_MISSING;
vt->parser.v.csi.argi = 0;
vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
vt->parser.state = CSI_ARGS;
// fallthrough
case CSI_ARGS:
// Numerical value of argument
if(c >= '0' && c <= '9') {
if(vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING)
vt->parser.csi_args[vt->parser.csi_argi] = 0;
vt->parser.csi_args[vt->parser.csi_argi] *= 10;
vt->parser.csi_args[vt->parser.csi_argi] += c - '0';
if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
break;
}
if(c == ':') {
vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE;
vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
c = ';';
}
if(c == ';') {
vt->parser.csi_argi++;
vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING;
vt->parser.v.csi.argi++;
vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
break;
}
// else fallthrough
vt->parser.csi_argi++;
vt->parser.v.csi.argi++;
vt->parser.intermedlen = 0;
vt->parser.state = CSI_INTERMED;
// fallthrough
@@ -293,31 +244,79 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
ENTER_NORMAL_STATE();
break;
case STRING:
if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
done_string(vt, string_start, bytes + pos - string_start);
ENTER_NORMAL_STATE();
case OSC_COMMAND:
/* Numerical value of command */
if(c >= '0' && c <= '9') {
if(vt->parser.v.osc.command == -1)
vt->parser.v.osc.command = 0;
else
vt->parser.v.osc.command *= 10;
vt->parser.v.osc.command += c - '0';
break;
}
else if (pos + 1 == len) {
// end of input but OSC string isn't finished yet, copy it to
// vt->parser.strbuffer to continue it later
more_string(vt, string_start, bytes + pos + 1 - string_start);
if(c == ';') {
vt->parser.state = OSC;
string_start = bytes + pos + 1;
break;
}
/* else fallthrough */
string_start = bytes + pos;
string_len = 0;
vt->parser.state = OSC;
goto string_state;
case DCS_COMMAND:
if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
if(c >= 0x40 && c<= 0x7e) {
string_start = bytes + pos + 1;
vt->parser.state = DCS;
}
break;
string_state:
case OSC:
case DCS:
if(c == 0x07 || (c1_allowed && c == 0x9c)) {
string_fragment(vt, string_start, string_len, TRUE);
ENTER_NORMAL_STATE();
}
break;
case NORMAL:
if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
if(vt->parser.in_esc) {
if(is_intermed(c)) {
if(vt->parser.intermedlen < INTERMED_MAX-1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
}
else if(c >= 0x30 && c < 0x7f) {
do_escape(vt, c);
vt->parser.in_esc = 0;
ENTER_NORMAL_STATE();
}
else {
DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
}
break;
}
if(c1_allowed && c >= 0x80 && c < 0xa0) {
switch(c) {
case 0x90: // DCS
start_string(vt, VTERM_PARSER_DCS);
ENTER_STRING_STATE();
vt->parser.string_initial = TRUE;
vt->parser.v.dcs.commandlen = 0;
ENTER_STATE(DCS_COMMAND);
break;
case 0x9b: // CSI
vt->parser.v.csi.leaderlen = 0;
ENTER_STATE(CSI_LEADER);
break;
case 0x9d: // OSC
start_string(vt, VTERM_PARSER_OSC);
ENTER_STRING_STATE();
vt->parser.v.osc.command = -1;
vt->parser.string_initial = TRUE;
string_start = bytes + pos + 1;
ENTER_STATE(OSC_COMMAND);
break;
default:
do_control(vt, c);
@@ -341,6 +340,9 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
}
}
if(string_start)
string_fragment(vt, string_start, bytes + pos - string_start, FALSE);
return len;
}

View File

@@ -2,26 +2,34 @@
#include <stdio.h>
static const VTermColor ansi_colors[] = {
// R G B index
{ 0, 0, 0, 1 }, // black
{ 224, 0, 0, 2 }, // red
{ 0, 224, 0, 3 }, // green
{ 224, 224, 0, 4 }, // yellow
{ 0, 0, 224, 5 }, // blue
{ 224, 0, 224, 6 }, // magenta
{ 0, 224, 224, 7 }, // cyan
{ 224, 224, 224, 8 }, // white == light grey
/**
* Structure used to store RGB triples without the additional metadata stored in
* VTermColor.
*/
typedef struct {
uint8_t red, green, blue;
} VTermRGB;
static const VTermRGB ansi_colors[] = {
// R G B
{ 0, 0, 0 }, // black
{ 224, 0, 0 }, // red
{ 0, 224, 0 }, // green
{ 224, 224, 0 }, // yellow
{ 0, 0, 224 }, // blue
{ 224, 0, 224 }, // magenta
{ 0, 224, 224 }, // cyan
{ 224, 224, 224 }, // white == light grey
// high intensity
{ 128, 128, 128, 9 }, // black
{ 255, 64, 64, 10 }, // red
{ 64, 255, 64, 11 }, // green
{ 255, 255, 64, 12 }, // yellow
{ 64, 64, 255, 13 }, // blue
{ 255, 64, 255, 14 }, // magenta
{ 64, 255, 255, 15 }, // cyan
{ 255, 255, 255, 16 }, // white for real
{ 128, 128, 128 }, // black
{ 255, 64, 64 }, // red
{ 64, 255, 64 }, // green
{ 255, 255, 64 }, // yellow
{ 64, 64, 255 }, // blue
{ 255, 64, 255 }, // magenta
{ 64, 255, 255 }, // cyan
{ 255, 255, 255 }, // white for real
};
static int ramp6[] = {
@@ -34,6 +42,15 @@ static int ramp24[] = {
0x81, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE,
};
static void lookup_default_colour_ansi(long idx, VTermColor *col)
{
vterm_color_rgb(
col,
ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
col->index = idx;
col->type = VTERM_COLOR_INDEXED;
}
static int lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
{
if(index >= 0 && index < 16) {
@@ -54,10 +71,9 @@ static int lookup_colour_palette(const VTermState *state, long index, VTermColor
// 216-colour cube
index -= 16;
col->blue = ramp6[index % 6];
col->green = ramp6[index/6 % 6];
col->red = ramp6[index/6/6 % 6];
col->ansi_index = VTERM_ANSI_INDEX_NONE;
vterm_color_rgb(col, ramp6[index/6/6 % 6],
ramp6[index/6 % 6],
ramp6[index % 6]);
return TRUE;
}
@@ -65,10 +81,7 @@ static int lookup_colour_palette(const VTermState *state, long index, VTermColor
// 24 greyscales
index -= 232;
col->blue = ramp24[index];
col->green = ramp24[index];
col->red = ramp24[index];
col->ansi_index = VTERM_ANSI_INDEX_NONE;
vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
return TRUE;
}
@@ -76,25 +89,23 @@ static int lookup_colour_palette(const VTermState *state, long index, VTermColor
return FALSE;
}
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col, int *index)
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
{
switch(palette) {
case 2: // RGB mode - 3 args contain colour values directly
if(argcount < 3)
return argcount;
col->red = (uint8_t)CSI_ARG(args[0]);
col->green = (uint8_t)CSI_ARG(args[1]);
col->blue = (uint8_t)CSI_ARG(args[2]);
col->ansi_index = VTERM_ANSI_INDEX_NONE;
vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
return 3;
case 5: // XTerm 256-colour mode
if(index)
*index = CSI_ARG_OR(args[0], -1);
if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
return argcount ? 1 : 0;
}
lookup_colour_palette(state, argcount ? CSI_ARG_OR(args[0], -1) : -1, col);
lookup_colour_palette(state, args[0], col);
return argcount ? 1 : 0;
@@ -154,13 +165,12 @@ INTERNAL void vterm_state_newpen(VTermState *state)
int col;
// 90% grey so that pure white is brighter
state->default_fg.red = state->default_fg.green = state->default_fg.blue = 240;
state->default_fg.ansi_index = VTERM_ANSI_INDEX_DEFAULT;
state->default_bg.red = state->default_bg.green = state->default_bg.blue = 0;
state->default_bg.ansi_index = VTERM_ANSI_INDEX_DEFAULT;
vterm_color_rgb(&state->default_fg, 240, 240, 240);
vterm_color_rgb(&state->default_bg, 0, 0, 0);
vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
for(col = 0; col < 16; col++)
state->colors[col] = ansi_colors[col];
lookup_default_colour_ansi(col, &state->colors[col]);
}
INTERNAL void vterm_state_resetpen(VTermState *state)
@@ -170,11 +180,10 @@ INTERNAL void vterm_state_resetpen(VTermState *state)
state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 0);
state->fg_index = -1;
state->bg_index = -1;
state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
}
@@ -192,6 +201,7 @@ INTERNAL void vterm_state_savepen(VTermState *state, int save)
setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
setpenattr_int( state, VTERM_ATTR_FONT, state->pen.font);
setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
@@ -199,6 +209,40 @@ INTERNAL void vterm_state_savepen(VTermState *state, int save)
}
}
void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue)
{
col->type = VTERM_COLOR_RGB;
col->red = red;
col->green = green;
col->blue = blue;
}
void vterm_color_indexed(VTermColor *col, uint8_t idx)
{
col->type = VTERM_COLOR_INDEXED;
col->index = idx;
}
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
{
/* First make sure that the two colours are of the same type (RGB/Indexed) */
if (a->type != b->type) {
return FALSE;
}
/* Depending on the type inspect the corresponding members */
if (VTERM_COLOR_IS_INDEXED(a)) {
return a->index == b->index;
}
else if (VTERM_COLOR_IS_RGB(a)) {
return (a->red == b->red)
&& (a->green == b->green)
&& (a->blue == b->blue);
}
return 0;
}
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
{
*default_fg = state->default_fg;
@@ -212,8 +256,15 @@ void vterm_state_get_palette_color(const VTermState *state, int index, VTermColo
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
{
/* Copy the given colors */
state->default_fg = *default_fg;
state->default_bg = *default_bg;
/* Make sure the correct type flags are set */
state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
| VTERM_COLOR_DEFAULT_FG;
state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
| VTERM_COLOR_DEFAULT_BG;
}
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
@@ -221,10 +272,18 @@ void vterm_state_set_palette_color(VTermState *state, int index, const VTermColo
if(index >= 0 && index < 16)
{
state->colors[index] = *col;
state->colors[index].ansi_index = index + VTERM_ANSI_INDEX_MIN;
state->colors[index].index = index + 1;
}
}
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
{
if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
lookup_colour_palette(state, col->index, col);
}
col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
}
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
{
state->bold_is_highbright = bold_is_highbright;
@@ -249,12 +308,14 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
vterm_state_resetpen(state);
break;
case 1: // Bold on
case 1: { // Bold on
const VTermColor *fg = &state->pen.fg;
state->pen.bold = 1;
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
if(state->fg_index > -1 && state->fg_index < 8 && state->bold_is_highbright)
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_index + (state->pen.bold ? 8 : 0));
if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->index < 8 && state->bold_is_highbright)
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->index + (state->pen.bold ? 8 : 0));
break;
}
case 3: // Italic on
state->pen.italic = 1;
@@ -293,6 +354,11 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
break;
case 8: // Conceal on
state->pen.conceal = 1;
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
break;
case 9: // Strikethrough on
state->pen.strike = 1;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
@@ -334,6 +400,11 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
break;
case 28: // Conceal off (Reveal)
state->pen.conceal = 0;
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
break;
case 29: // Strikethrough off
state->pen.strike = 0;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
@@ -342,22 +413,19 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37: // Foreground colour palette
value = CSI_ARG(args[argi]) - 30;
state->fg_index = value;
if(state->pen.bold && state->bold_is_highbright)
value += 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 38: // Foreground colour alternative palette
state->fg_index = -1;
if(argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg, &state->fg_index);
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 39: // Foreground colour default
state->fg_index = -1;
state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
@@ -365,20 +433,17 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
case 40: case 41: case 42: case 43:
case 44: case 45: case 46: case 47: // Background colour palette
value = CSI_ARG(args[argi]) - 40;
state->bg_index = value;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
case 48: // Background colour alternative palette
state->bg_index = -1;
if(argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg, &state->bg_index);
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 49: // Default background
state->bg_index = -1;
state->pen.bg = state->default_bg;
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
@@ -386,14 +451,12 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
case 90: case 91: case 92: case 93:
case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
value = CSI_ARG(args[argi]) - 90 + 8;
state->fg_index = value;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 100: case 101: case 102: case 103:
case 104: case 105: case 106: case 107: // Background colour high-intensity palette
value = CSI_ARG(args[argi]) - 100 + 8;
state->bg_index = value;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
@@ -412,6 +475,39 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
}
}
static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
{
/* Do nothing if the given color is the default color */
if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
(!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
return argi;
}
/* Decide whether to send an indexed color or an RGB color */
if (VTERM_COLOR_IS_INDEXED(col)) {
const uint8_t idx = col->index;
if (idx < 8) {
args[argi++] = (idx + (fg ? 30 : 40));
}
else if (idx < 16) {
args[argi++] = (idx - 8 + (fg ? 90 : 100));
}
else {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 5;
args[argi++] = idx;
}
}
else if (VTERM_COLOR_IS_RGB(col)) {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 2;
args[argi++] = CSI_ARG_FLAG_MORE | col->red;
args[argi++] = CSI_ARG_FLAG_MORE | col->green;
args[argi++] = col->blue;
}
return argi;
}
INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount UNUSED)
{
int argi = 0;
@@ -433,6 +529,9 @@ INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount UNU
if(state->pen.reverse)
args[argi++] = 7;
if(state->pen.conceal)
args[argi++] = 8;
if(state->pen.strike)
args[argi++] = 9;
@@ -442,49 +541,9 @@ INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount UNU
if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
args[argi++] = 21;
if(state->fg_index >= 0 && state->fg_index < 8)
args[argi++] = 30 + state->fg_index;
else if(state->fg_index >= 8 && state->fg_index < 16)
args[argi++] = 90 + state->fg_index - 8;
else if(state->fg_index >= 16 && state->fg_index < 256) {
args[argi++] = CSI_ARG_FLAG_MORE|38;
args[argi++] = CSI_ARG_FLAG_MORE|5;
args[argi++] = state->fg_index;
}
else if(state->fg_index == -1) {
// Send palette 2 if the actual FG colour is not default
if(state->pen.fg.red != state->default_fg.red ||
state->pen.fg.green != state->default_fg.green ||
state->pen.fg.blue != state->default_fg.blue ) {
args[argi++] = CSI_ARG_FLAG_MORE|38;
args[argi++] = CSI_ARG_FLAG_MORE|2;
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.fg.red;
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.fg.green;
args[argi++] = state->pen.fg.blue;
}
}
argi = vterm_state_getpen_color(&state->pen.fg, argi, args, TRUE);
if(state->bg_index >= 0 && state->bg_index < 8)
args[argi++] = 40 + state->bg_index;
else if(state->bg_index >= 8 && state->bg_index < 16)
args[argi++] = 100 + state->bg_index - 8;
else if(state->bg_index >= 16 && state->bg_index < 256) {
args[argi++] = CSI_ARG_FLAG_MORE|48;
args[argi++] = CSI_ARG_FLAG_MORE|5;
args[argi++] = state->bg_index;
}
else if(state->bg_index == -1) {
// Send palette 2 if the actual BG colour is not default
if(state->pen.bg.red != state->default_bg.red ||
state->pen.bg.green != state->default_bg.green ||
state->pen.bg.blue != state->default_bg.blue ) {
args[argi++] = CSI_ARG_FLAG_MORE|48;
args[argi++] = CSI_ARG_FLAG_MORE|2;
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.bg.red;
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.bg.green;
args[argi++] = state->pen.bg.blue;
}
}
argi = vterm_state_getpen_color(&state->pen.bg, argi, args, FALSE);
return argi;
}
@@ -512,6 +571,10 @@ int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue
val->boolean = state->pen.reverse;
return 1;
case VTERM_ATTR_CONCEAL:
val->boolean = state->pen.conceal;
return 1;
case VTERM_ATTR_STRIKE:
val->boolean = state->pen.strike;
return 1;

View File

@@ -21,6 +21,7 @@ typedef struct
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int conceal : 1;
unsigned int strike : 1;
unsigned int font : 4; // 0 to 9
@@ -56,8 +57,6 @@ struct VTermScreen
int global_reverse;
// Primary and Altscreen. buffers[1] is lazily allocated as needed
#define BUFIDX_PRIMARY 0
#define BUFIDX_ALTSCREEN 1
ScreenCell *buffers[2];
// buffer will == buffers[0] or buffers[1], depending on altscreen
@@ -422,6 +421,9 @@ static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
case VTERM_ATTR_REVERSE:
screen->pen.reverse = val->boolean;
return 1;
case VTERM_ATTR_CONCEAL:
screen->pen.conceal = val->boolean;
return 1;
case VTERM_ATTR_STRIKE:
screen->pen.strike = val->boolean;
return 1;
@@ -481,7 +483,7 @@ static int bell(void *user)
return 0;
}
static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, int active, VTermPos *delta)
static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, int active, VTermStateFields *statefields)
{
int old_rows = screen->rows;
int old_cols = screen->cols;
@@ -490,24 +492,9 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
/* Find the final row of old buffer content */
int old_row;
int old_row = old_rows - 1;
int new_row = new_rows - 1;
int col;
int new_row;
for(old_row = old_rows - 1; old_row > 0; old_row--) {
if(active && (old_row == screen->state->pos.row))
/* The line with the active cursor is never "blank" */
goto found_oldrow;
for(col = 0; col < old_cols; col++)
if(old_buffer[old_row * old_cols + col].chars[0])
/* This row is not blank */
goto found_oldrow;
}
old_row = 0;
found_oldrow:
;
new_row = new_rows - 1;
while(new_row >= 0 && old_row >= 0) {
for(col = 0; col < old_cols && col < new_cols; col++)
@@ -517,6 +504,15 @@ found_oldrow:
old_row--;
new_row--;
if(new_row < 0 && old_row >= 0 &&
new_buffer[(new_rows - 1) * new_cols].chars[0] == 0 &&
(!active || statefields->pos.row < (new_rows - 1))) {
int moverows = new_rows - 1;
memmove(&new_buffer[1 * new_cols], &new_buffer[0], moverows * new_cols * sizeof(ScreenCell));
new_row++;
}
}
if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
@@ -524,8 +520,8 @@ found_oldrow:
int row;
for(row = 0; row <= old_row; row++)
sb_pushline_from_row(screen, row);
if(delta)
delta->row -= (old_row + 1);
if(active)
statefields->pos.row -= (old_row + 1);
}
if(new_row >= 0 && bufidx == BUFIDX_PRIMARY &&
screen->callbacks && screen->callbacks->sb_popline) {
@@ -541,7 +537,7 @@ found_oldrow:
ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
int i;
for(i = 0; ; i++) {
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
dst->chars[i] = src->chars[i];
if(!src->chars[i])
break;
@@ -552,6 +548,7 @@ found_oldrow:
dst->pen.italic = src->attrs.italic;
dst->pen.blink = src->attrs.blink;
dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
dst->pen.conceal = src->attrs.conceal;
dst->pen.strike = src->attrs.strike;
dst->pen.font = src->attrs.font;
@@ -561,10 +558,12 @@ found_oldrow:
if(src->width == 2 && pos.col < (new_cols-1))
(dst + 1)->chars[0] = (uint32_t) -1;
}
for( ; pos.col < new_cols; pos.col++)
clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
new_row--;
if(delta)
delta->row++;
if(active)
statefields->pos.row++;
}
}
@@ -590,7 +589,7 @@ found_oldrow:
*/
}
static int resize(int new_rows, int new_cols, VTermPos *delta, void *user)
static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
{
VTermScreen *screen = user;
@@ -606,9 +605,9 @@ static int resize(int new_rows, int new_cols, VTermPos *delta, void *user)
screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
}
resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, altscreen_active ? NULL : delta);
resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
if(screen->buffers[BUFIDX_ALTSCREEN])
resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, altscreen_active ? delta : NULL);
resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
screen->buffer = altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
@@ -812,7 +811,7 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
if(!intcell)
return 0;
for(i = 0; ; i++) {
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
cell->chars[i] = intcell->chars[i];
if(!intcell->chars[i])
break;
@@ -823,6 +822,7 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
cell->attrs.italic = intcell->pen.italic;
cell->attrs.blink = intcell->pen.blink;
cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
cell->attrs.conceal = intcell->pen.conceal;
cell->attrs.strike = intcell->pen.strike;
cell->attrs.font = intcell->pen.font;
@@ -879,7 +879,6 @@ VTermScreen *vterm_obtain_screen(VTerm *vt)
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
{
if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
int rows, cols;
vterm_get_size(screen->vt, &rows, &cols);
@@ -899,7 +898,7 @@ void *vterm_screen_get_cbdata(VTermScreen *screen)
return screen->cbdata;
}
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user)
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
{
vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
}
@@ -944,13 +943,15 @@ static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
return 1;
if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
return 1;
if((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal))
return 1;
if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
return 1;
if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
return 1;
if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg))
if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
return 1;
if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg))
if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
return 1;
return 0;
@@ -983,3 +984,8 @@ int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent,
return 1;
}
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
{
vterm_state_convert_color_to_rgb(screen->state, col);
}

View File

@@ -44,6 +44,15 @@ static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom
static void erase(VTermState *state, VTermRect rect, int selective)
{
if(rect.end_col == state->cols) {
int row;
/* If we're erasing the final cells of any lines, cancel the continuation
* marker on the subsequent line
*/
for(row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
state->lineinfo[row].continuation = 0;
}
if(state->callbacks && state->callbacks->erase)
if((*state->callbacks->erase)(rect, selective, state->cbdata))
return;
@@ -73,13 +82,29 @@ static VTermState *vterm_state_new(VTerm *vt)
state->bold_is_highbright = 0;
state->combine_chars_size = 16;
state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
/* TODO: Make an 'enable' function */
state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
if(state->encoding_utf8.enc->init)
(*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
return state;
}
INTERNAL void vterm_state_free(VTermState *state)
{
vterm_allocator_free(state->vt, state->tabstops);
vterm_allocator_free(state->vt, state->lineinfo);
vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
if(state->lineinfos[BUFIDX_ALTSCREEN])
vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
vterm_allocator_free(state->vt, state->combine_chars);
vterm_allocator_free(state->vt, state);
}
@@ -106,15 +131,23 @@ static void scroll(VTermState *state, VTermRect rect, int downward, int rightwar
// Update lineinfo if full line
if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
int height = rect.end_row - rect.start_row - abs(downward);
int row;
VTermLineInfo zeroLineInfo = { 0 };
if(downward > 0)
if(downward > 0) {
memmove(state->lineinfo + rect.start_row,
state->lineinfo + rect.start_row + downward,
height * sizeof(state->lineinfo[0]));
else
for(row = rect.end_row - downward; row < rect.end_row; row++)
state->lineinfo[row] = zeroLineInfo;
}
else {
memmove(state->lineinfo + rect.start_row - downward,
state->lineinfo + rect.start_row,
height * sizeof(state->lineinfo[0]));
for(row = rect.start_row; row < rect.start_row - downward; row++)
state->lineinfo[row] = zeroLineInfo;
}
}
if(state->callbacks && state->callbacks->scrollrect)
@@ -367,6 +400,7 @@ static int on_text(const char bytes[], size_t len, void *user)
linefeed(state);
state->pos.col = 0;
state->at_phantom = 0;
state->lineinfo[state->pos.row].continuation = 1;
}
if(state->mode.insert) {
@@ -548,19 +582,12 @@ static int settermprop_int(VTermState *state, VTermProp prop, int v)
return vterm_state_set_termprop(state, prop, &val);
}
static int settermprop_string(VTermState *state, VTermProp prop, const char *str, size_t len)
static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
{
char *strvalue;
int r;
VTermValue val;
strvalue = vterm_allocator_malloc(state->vt, (len+1) * sizeof(char));
strncpy(strvalue, str, len);
strvalue[len] = 0;
val.string = strvalue;
r = vterm_state_set_termprop(state, prop, &val);
vterm_allocator_free(state->vt, strvalue);
return r;
val.string = frag;
return vterm_state_set_termprop(state, prop, &val);
}
static void savecursor(VTermState *state, int save)
@@ -1451,6 +1478,14 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
state->scrollregion_bottom = -1;
}
// Setting the scrolling region restores the cursor to the home position
state->pos.row = 0;
state->pos.col = 0;
if(state->mode.origin) {
state->pos.row += state->scrollregion_top;
state->pos.col += SCROLLREGION_LEFT(state);
}
break;
case 0x73: // DECSLRM - DEC custom
@@ -1472,6 +1507,14 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
state->scrollregion_right = -1;
}
// Setting the scrolling region restores the cursor to the home position
state->pos.row = 0;
state->pos.col = 0;
if(state->mode.origin) {
state->pos.row += state->scrollregion_top;
state->pos.col += SCROLLREGION_LEFT(state);
}
break;
case 0x74:
@@ -1568,100 +1611,119 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
return 1;
}
static int on_osc(const char *command, size_t cmdlen, void *user)
static int on_osc(int command, VTermStringFragment frag, void *user)
{
VTermState *state = user;
if(cmdlen < 2)
return 0;
if(strneq(command, "0;", 2)) {
settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2);
settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2);
return 1;
}
else if(strneq(command, "1;", 2)) {
settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2);
return 1;
}
else if(strneq(command, "2;", 2)) {
settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2);
return 1;
}
else if(strneq(command, "10;", 3)) {
// request foreground color: <Esc>]10;?<0x07>
int red = state->default_fg.red;
int blue = state->default_fg.blue;
int green = state->default_fg.green;
vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "10;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
return 1;
}
else if(strneq(command, "11;", 3)) {
// request background color: <Esc>]11;?<0x07>
int red = state->default_bg.red;
int blue = state->default_bg.blue;
int green = state->default_bg.green;
vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "11;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
return 1;
}
else if(strneq(command, "12;", 3)) {
settermprop_string(state, VTERM_PROP_CURSORCOLOR, command + 3, cmdlen - 3);
return 1;
}
else if(state->fallbacks && state->fallbacks->osc)
if((*state->fallbacks->osc)(command, cmdlen, state->fbdata))
switch(command) {
case 0:
settermprop_string(state, VTERM_PROP_ICONNAME, frag);
settermprop_string(state, VTERM_PROP_TITLE, frag);
return 1;
case 1:
settermprop_string(state, VTERM_PROP_ICONNAME, frag);
return 1;
case 2:
settermprop_string(state, VTERM_PROP_TITLE, frag);
return 1;
case 10:
{
// request foreground color: <Esc>]10;?<0x07>
int red = state->default_fg.red;
int blue = state->default_fg.blue;
int green = state->default_fg.green;
vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "10;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
return 1;
}
case 11:
{
// request background color: <Esc>]11;?<0x07>
int red = state->default_bg.red;
int blue = state->default_bg.blue;
int green = state->default_bg.green;
vterm_push_output_sprintf_ctrl(state->vt, C1_OSC, "11;rgb:%02x%02x/%02x%02x/%02x%02x\x07", red, red, green, green, blue, blue);
return 1;
}
case 12:
settermprop_string(state, VTERM_PROP_CURSORCOLOR, frag);
return 1;
default:
if(state->fallbacks && state->fallbacks->osc)
if((*state->fallbacks->osc)(command, frag, state->fbdata))
return 1;
}
return 0;
}
static void request_status_string(VTermState *state, const char *command, size_t cmdlen)
static void request_status_string(VTermState *state, VTermStringFragment frag)
{
VTerm *vt = state->vt;
if(cmdlen == 1)
switch(command[0]) {
case 'm': // Query SGR
{
long args[20];
int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
int argi;
size_t cur = 0;
char *tmp = state->tmp.decrqss;
size_t i = 0;
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
if(cur >= vt->tmpbuffer_len)
return;
if(frag.initial)
tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
for(argi = 0; argi < argc; argi++) {
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
argi == argc - 1 ? "%ld" :
CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
"%ld;",
CSI_ARG(args[argi]));
while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
i++;
while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
tmp[i++] = (frag.str++)[0];
tmp[i] = 0;
if(cur >= vt->tmpbuffer_len)
return;
}
if(!frag.final)
return;
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
if(cur >= vt->tmpbuffer_len)
return;
switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
case 'm': {
// Query SGR
long args[20];
int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
size_t cur = 0;
int argi;
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
}
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
if(cur >= vt->tmpbuffer_len)
return;
case 'r': // Query DECSTBM
vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
return;
case 's': // Query DECSLRM
vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
for(argi = 0; argi < argc; argi++) {
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
argi == argc - 1 ? "%ld" :
CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
"%ld;",
CSI_ARG(args[argi]));
if(cur >= vt->tmpbuffer_len)
return;
}
cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
if(cur >= vt->tmpbuffer_len)
return;
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
return;
}
if(cmdlen == 2) {
if(strneq(command, " q", 2)) {
case 'r':
// Query DECSTBM
vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
return;
case 's':
// Query DECSLRM
vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
return;
case ' '|('q'<<8): {
// Query DECSCUSR
int reply;
switch(state->mode.cursor_shape) {
case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
@@ -1673,27 +1735,29 @@ static void request_status_string(VTermState *state, const char *command, size_t
vterm_push_output_sprintf_dcs(vt, "1$r%d q", reply);
return;
}
else if(strneq(command, "\"q", 2)) {
case '\"'|('q'<<8):
// Query DECSCA
vterm_push_output_sprintf_dcs(vt, "1$r%d\"q", state->protected_cell ? 1 : 2);
return;
}
}
vterm_push_output_sprintf_dcs(state->vt, "0$r%.s", (int)cmdlen, command);
vterm_push_output_sprintf_dcs(state->vt, "0$r%s", tmp);
}
static int on_dcs(const char *command, size_t cmdlen, void *user)
static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
{
VTermState *state = user;
if(cmdlen >= 2 && strneq(command, "$q", 2)) {
request_status_string(state, command+2, cmdlen-2);
if(commandlen == 2 && strneq(command, "$q", 2)) {
request_status_string(state, frag);
return 1;
}
else if(state->fallbacks && state->fallbacks->dcs)
if((*state->fallbacks->dcs)(command, cmdlen, state->fbdata))
if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
return 1;
DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
return 0;
}
@@ -1701,7 +1765,7 @@ static int on_resize(int rows, int cols, void *user)
{
VTermState *state = user;
VTermPos oldpos = state->pos;
VTermPos delta = { 0, 0 };
VTermStateFields fields;
if(cols != state->cols) {
int col;
@@ -1731,22 +1795,30 @@ static int on_resize(int rows, int cols, void *user)
}
if(rows != state->rows) {
int row;
VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
if (newlineinfo == NULL)
return 0;
int bufidx;
for(bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
int row;
VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
VTermLineInfo *newlineinfo;
if(!oldlineinfo)
continue;
for(row = 0; row < state->rows && row < rows; row++) {
newlineinfo[row] = state->lineinfo[row];
newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
for(row = 0; row < state->rows && row < rows; row++) {
newlineinfo[row] = oldlineinfo[row];
}
for( ; row < rows; row++) {
VTermLineInfo lineInfo = {0};
newlineinfo[row] = lineInfo;
}
vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
state->lineinfos[bufidx] = newlineinfo;
}
for( ; row < rows; row++) {
newlineinfo[row].doublewidth = 0;
newlineinfo[row].doubleheight = 0;
}
vterm_allocator_free(state->vt, state->lineinfo);
state->lineinfo = newlineinfo;
state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
}
state->rows = rows;
@@ -1757,17 +1829,18 @@ static int on_resize(int rows, int cols, void *user)
if(state->scrollregion_right > -1)
UBOUND(state->scrollregion_right, state->cols);
fields.pos = state->pos;
if(state->callbacks && state->callbacks->resize)
(*state->callbacks->resize)(rows, cols, &delta, state->cbdata);
(*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
state->pos = fields.pos;
if(state->at_phantom && state->pos.col < cols-1) {
state->at_phantom = 0;
state->pos.col++;
}
state->pos.row += delta.row;
state->pos.col += delta.col;
if(state->pos.row >= rows)
state->pos.row = rows - 1;
if(state->pos.col >= cols)
@@ -1803,17 +1876,6 @@ VTermState *vterm_obtain_state(VTerm *vt)
return NULL;
vt->state = state;
state->combine_chars_size = 16;
state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
state->lineinfo = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
if(*state->encoding_utf8.enc->init != NULL)
(*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
vterm_parser_set_callbacks(vt, &parser_callbacks, state);
return state;
@@ -1931,7 +1993,7 @@ void *vterm_state_get_cbdata(VTermState *state)
return state->cbdata;
}
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *fallbacks, void *user)
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
{
if(fallbacks) {
state->fallbacks = fallbacks;
@@ -1976,6 +2038,7 @@ int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
return 1;
case VTERM_PROP_ALTSCREEN:
state->mode.alt_screen = val->boolean;
state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
if(state->mode.alt_screen) {
VTermRect rect = {0, 0, 0, 0};
rect.end_row = state->rows;

View File

@@ -55,35 +55,23 @@ VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *fun
vt->parser.callbacks = NULL;
vt->parser.cbdata = NULL;
vt->parser.strbuffer_len = 500; // should be able to hold an OSC string
vt->parser.strbuffer_cur = 0;
vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len);
if (vt->parser.strbuffer == NULL)
{
vterm_allocator_free(vt, vt);
return NULL;
}
vt->outfunc = NULL;
vt->outdata = NULL;
vt->outbuffer_len = 200;
vt->outbuffer_cur = 0;
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
if (vt->outbuffer == NULL)
{
vterm_allocator_free(vt, vt->parser.strbuffer);
vterm_allocator_free(vt, vt);
return NULL;
}
vt->tmpbuffer_len = 64;
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
if (vt->tmpbuffer == NULL)
if (vt->tmpbuffer == NULL
|| vt->outbuffer == NULL
|| vt->tmpbuffer == NULL)
{
vterm_allocator_free(vt, vt->parser.strbuffer);
vterm_allocator_free(vt, vt);
vterm_allocator_free(vt, vt->outbuffer);
vterm_allocator_free(vt, vt->tmpbuffer);
vterm_allocator_free(vt, vt);
return NULL;
}
@@ -98,7 +86,6 @@ void vterm_free(VTerm *vt)
if(vt->state)
vterm_state_free(vt->state);
vterm_allocator_free(vt, vt->parser.strbuffer);
vterm_allocator_free(vt, vt->outbuffer);
vterm_allocator_free(vt, vt->tmpbuffer);
@@ -274,6 +261,7 @@ VTermValueType vterm_get_attr_type(VTermAttr attr)
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;

View File

@@ -32,6 +32,9 @@
#define CSI_ARGS_MAX 16
#define CSI_LEADER_MAX 16
#define BUFIDX_PRIMARY 0
#define BUFIDX_ALTSCREEN 1
typedef struct VTermEncoding VTermEncoding;
typedef struct {
@@ -50,19 +53,11 @@ struct VTermPen
unsigned int italic:1;
unsigned int blink:1;
unsigned int reverse:1;
unsigned int conceal:1;
unsigned int strike:1;
unsigned int font:4; // To store 0-9
};
int vterm_color_equal(VTermColor a, VTermColor b);
#if defined(DEFINE_INLINES) || USE_INLINE
INLINE int vterm_color_equal(VTermColor a, VTermColor b)
{
return a.red == b.red && a.green == b.green && a.blue == b.blue;
}
#endif
struct VTermState
{
VTerm *vt;
@@ -70,7 +65,7 @@ struct VTermState
const VTermStateCallbacks *callbacks;
void *cbdata;
const VTermParserCallbacks *fallbacks;
const VTermStateFallbacks *fallbacks;
void *fbdata;
int rows;
@@ -92,6 +87,10 @@ struct VTermState
// Bitvector of tab stops
unsigned char *tabstops;
/* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
VTermLineInfo *lineinfos[2];
/* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
VTermLineInfo *lineinfo;
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
@@ -136,8 +135,6 @@ struct VTermState
VTermColor default_bg;
VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
int fg_index;
int bg_index;
int bold_is_highbright;
unsigned int protected_cell : 1;
@@ -153,15 +150,13 @@ struct VTermState
unsigned int cursor_shape:2;
} mode;
} saved;
/* Temporary state for DECRQSS parsing */
union {
char decrqss[4];
} tmp;
};
typedef enum {
VTERM_PARSER_OSC,
VTERM_PARSER_DCS,
VTERM_N_PARSER_TYPES
} VTermParserStringType;
struct VTerm
{
VTermAllocatorFunctions *allocator;
@@ -181,28 +176,39 @@ struct VTerm
CSI_LEADER,
CSI_ARGS,
CSI_INTERMED,
ESC,
// below here are the "string states"
STRING,
ESC_IN_STRING,
DCS_COMMAND,
/* below here are the "string states" */
OSC_COMMAND,
OSC,
DCS,
} state;
unsigned int in_esc : 1;
int intermedlen;
char intermed[INTERMED_MAX];
int csi_leaderlen;
char csi_leader[CSI_LEADER_MAX];
union {
struct {
int leaderlen;
char leader[CSI_LEADER_MAX];
int csi_argi;
long csi_args[CSI_ARGS_MAX];
int argi;
long args[CSI_ARGS_MAX];
} csi;
struct {
int command;
} osc;
struct {
int commandlen;
char command[CSI_LEADER_MAX];
} dcs;
} v;
const VTermParserCallbacks *callbacks;
void *cbdata;
VTermParserStringType stringtype;
char *strbuffer;
size_t strbuffer_len;
size_t strbuffer_cur;
int string_initial;
} parser;
// len == malloc()ed size; cur == number of valid bytes

View File

@@ -132,15 +132,31 @@ PUSH "\e[12\n;3X"
!OSC BEL
PUSH "\e]1;Hello\x07"
osc "1;Hello"
osc [1 "Hello"]
!OSC ST (7bit)
PUSH "\e]1;Hello\e\\"
osc "1;Hello"
osc [1 "Hello"]
!OSC ST (8bit)
PUSH "\x{9d}1;Hello\x9c"
osc "1;Hello"
osc [1 "Hello"]
!OSC in parts
PUSH "\e]52;abc"
osc [52 "abc"
PUSH "def"
osc "def"
PUSH "ghi\e\\"
osc "ghi"]
!OSC BEL without semicolon
PUSH "\e]1234\x07"
osc [1234 ]
!OSC ST without semicolon
PUSH "\e]1234\e\\"
osc [1234 ]
!Escape cancels OSC, starts Escape
PUSH "\e]Something\e9"
@@ -152,20 +168,21 @@ PUSH "\e]12\x{18}AB"
!C0 in OSC interrupts and continues
PUSH "\e]2;\nBye\x07"
osc [2 ""
control 10
osc "2;Bye"
osc "Bye"]
!DCS BEL
PUSH "\ePHello\x07"
dcs "Hello"
dcs ["Hello"]
!DCS ST (7bit)
PUSH "\ePHello\e\\"
dcs "Hello"
dcs ["Hello"]
!DCS ST (8bit)
PUSH "\x{90}Hello\x9c"
dcs "Hello"
dcs ["Hello"]
!Escape cancels DCS, starts Escape
PUSH "\ePSomething\e9"
@@ -177,8 +194,9 @@ PUSH "\eP12\x{18}AB"
!C0 in OSC interrupts and continues
PUSH "\ePBy\ne\x07"
dcs ["By"
control 10
dcs "Bye"
dcs "e"]
!NUL ignored
PUSH "\x{00}"

View File

@@ -40,8 +40,8 @@ PUSH "\n"
?cursor = 20,0
!Index in DECSTBM
PUSH "\e[10H"
PUSH "\e[9;10r"
PUSH "\e[10H"
PUSH "\eM"
?cursor = 8,0
PUSH "\eM"
@@ -148,3 +148,9 @@ PUSH "\e[2T"
moverect 0..23,0..80 -> 2..25,0..80
erase 0..2,0..80
?cursor = 0,0
!DECSTBM resets cursor position
PUSH "\e[5;5H"
?cursor = 4,4
PUSH "\e[r"
?cursor = 0,0

View File

@@ -33,4 +33,10 @@ PUSH "\e[3 q"
!Title
PUSH "\e]2;Here is my title\a"
settermprop 4 "Here is my title"
settermprop 4 ["Here is my title"]
!Title split write
PUSH "\e]2;Here is"
settermprop 4 ["Here is"
PUSH " another title\a"
settermprop 4 " another title"]

View File

@@ -41,10 +41,10 @@ PUSH "\e[0;93;104m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r93;104m\e\\"
!DECRQSS on SGR 256-palette colours
PUSH "\e[0;38:5:56;48:5:78m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r38:5:56;48:5:78m\e\\"
##!DECRQSS on SGR 256-palette colours
#PUSH "\e[0;38:5:56;48:5:78m"
#PUSH "\eP\$qm\e\\"
# output "\eP1\$r38:5:56;48:5:78m\e\\"
!DECRQSS on SGR RGB8 colours
PUSH "\e[0;38:2:24:68:112;48:2:13:57:101m"

View File

@@ -12,8 +12,8 @@ PUSH "\e[?15;2z"
!Unrecognised OSC
PUSH "\e]27;Something\e\\"
osc "27;Something"
osc [27 "Something"]
!Unrecognised DCS
PUSH "\ePz123\e\\"
dcs "z123"
dcs ["z123"]

View File

@@ -10,8 +10,8 @@ PUSH "\e[m"
?pen blink = off
?pen reverse = off
?pen font = 0
?pen foreground = rgb(240,240,240)
?pen background = rgb(0,0,0)
?pen foreground = rgb(240,240,240,is_default_fg)
?pen background = rgb(0,0,0,is_default_bg)
!Bold
PUSH "\e[1m"
@@ -75,40 +75,40 @@ PUSH "\e[11m\e[m"
!Foreground
PUSH "\e[31m"
?pen foreground = rgb(224,0,0)
?pen foreground = idx(1)
PUSH "\e[32m"
?pen foreground = rgb(0,224,0)
?pen foreground = idx(2)
PUSH "\e[34m"
?pen foreground = rgb(0,0,224)
?pen foreground = idx(4)
PUSH "\e[91m"
?pen foreground = rgb(255,64,64)
?pen foreground = idx(9)
PUSH "\e[38:2:10:20:30m"
?pen foreground = rgb(10,20,30)
PUSH "\e[38:5:1m"
?pen foreground = rgb(224,0,0)
?pen foreground = idx(1)
PUSH "\e[39m"
?pen foreground = rgb(240,240,240)
?pen foreground = rgb(240,240,240,is_default_fg)
!Background
PUSH "\e[41m"
?pen background = rgb(224,0,0)
?pen background = idx(1)
PUSH "\e[42m"
?pen background = rgb(0,224,0)
?pen background = idx(2)
PUSH "\e[44m"
?pen background = rgb(0,0,224)
?pen background = idx(4)
PUSH "\e[101m"
?pen background = rgb(255,64,64)
?pen background = idx(9)
PUSH "\e[48:2:10:20:30m"
?pen background = rgb(10,20,30)
PUSH "\e[48:5:1m"
?pen background = rgb(224,0,0)
?pen background = idx(1)
PUSH "\e[49m"
?pen background = rgb(0,0,0)
?pen background = rgb(0,0,0,is_default_bg)
!Bold+ANSI colour == highbright
PUSH "\e[m\e[1;37m"
?pen bold = on
?pen foreground = rgb(255,255,255)
?pen foreground = idx(15)
PUSH "\e[m\e[37;1m"
?pen bold = on
?pen foreground = rgb(255,255,255)
?pen foreground = idx(15)

View File

@@ -0,0 +1,28 @@
INIT
WANTSTATE
# Many of these test cases inspired by
# https://blueprints.launchpad.net/libvterm/+spec/reflow-cases
!Spillover text marks continuation on second line
RESET
PUSH "A"x100
PUSH "\r\n"
?lineinfo 0 =
?lineinfo 1 = cont
!CRLF in column 80 does not mark continuation
RESET
PUSH "B"x80
PUSH "\r\n"
PUSH "B"x20
PUSH "\r\n"
?lineinfo 0 =
?lineinfo 1 =
!EL cancels continuation of following line
RESET
PUSH "D"x100
?lineinfo 1 = cont
PUSH "\eM\e[79G\e[K"
?lineinfo 1 =

View File

@@ -1,12 +1,12 @@
INIT
WANTSCREEN c
WANTSCREEN ac
!Get
RESET
PUSH "ABC"
movecursor 0,3
?screen_chars 0,0,1,3 = 0x41,0x42,0x43
?screen_chars 0,0,1,80 = 0x41,0x42,0x43
?screen_chars 0,0,1,3 = "ABC"
?screen_chars 0,0,1,80 = "ABC"
?screen_text 0,0,1,3 = 0x41,0x42,0x43
?screen_text 0,0,1,80 = 0x41,0x42,0x43
?screen_cell 0,0 = {0x41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
@@ -18,11 +18,11 @@ PUSH "ABC"
?screen_eol 0,3 = 1
PUSH "\e[H"
movecursor 0,0
?screen_chars 0,0,1,80 = 0x41,0x42,0x43
?screen_chars 0,0,1,80 = "ABC"
?screen_text 0,0,1,80 = 0x41,0x42,0x43
PUSH "E"
movecursor 0,1
?screen_chars 0,0,1,80 = 0x45,0x42,0x43
?screen_chars 0,0,1,80 = "EBC"
?screen_text 0,0,1,80 = 0x45,0x42,0x43
WANTSCREEN -c
@@ -37,33 +37,33 @@ PUSH "ABCDE\e[H\e[K"
RESET
PUSH "ABC\e[H\e[@"
PUSH "1"
?screen_chars 0,0,1,80 = 0x31,0x41,0x42,0x43
?screen_chars 0,0,1,80 = "1ABC"
RESET
PUSH "ABC\e[H\e[P"
?screen_chars 0,0,1,1 = 0x42
?screen_chars 0,1,1,2 = 0x43
?screen_chars 0,0,1,80 = 0x42,0x43
?screen_chars 0,0,1,1 = "B"
?screen_chars 0,1,1,2 = "C"
?screen_chars 0,0,1,80 = "BC"
!Space padding
RESET
PUSH "Hello\e[CWorld"
?screen_chars 0,0,1,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64
?screen_chars 0,0,1,80 = "Hello World"
?screen_text 0,0,1,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64
!Linefeed padding
RESET
PUSH "Hello\r\nWorld"
?screen_chars 0,0,2,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x0a,0x57,0x6f,0x72,0x6c,0x64
?screen_chars 0,0,2,80 = "Hello\nWorld"
?screen_text 0,0,2,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x0a,0x57,0x6f,0x72,0x6c,0x64
!Altscreen
RESET
PUSH "P"
?screen_chars 0,0,1,80 = 0x50
?screen_chars 0,0,1,80 = "P"
PUSH "\e[?1049h"
?screen_chars 0,0,1,80 =
PUSH "\e[2K\e[HA"
?screen_chars 0,0,1,80 = 0x41
?screen_chars 0,0,1,80 = "A"
PUSH "\e[?1049l"
?screen_chars 0,0,1,80 = 0x50
?screen_chars 0,0,1,80 = "P"

View File

@@ -1,5 +1,5 @@
INIT
WANTSCREEN Db
WANTSCREEN aDb
!Putglyph
RESET
@@ -152,4 +152,4 @@ PUSH "\e[25H\r\nABCDE\b\b\b\e[2P\r\n"
DAMAGEFLUSH
moverect 1..25,0..80 -> 0..24,0..80
damage 24..25,0..80
?screen_chars 23,0,24,5 = 0x41,0x42,0x45
?screen_chars 23,0,24,5 = "ABE"

View File

@@ -6,42 +6,42 @@ WANTSCREEN
RESET
RESIZE 25,80
PUSH "AB\r\nCD"
?screen_chars 0,0,1,80 = 0x41,0x42
?screen_chars 1,0,2,80 = 0x43,0x44
?screen_chars 0,0,1,80 = "AB"
?screen_chars 1,0,2,80 = "CD"
RESIZE 25,100
?screen_chars 0,0,1,100 = 0x41,0x42
?screen_chars 1,0,2,100 = 0x43,0x44
?screen_chars 0,0,1,100 = "AB"
?screen_chars 1,0,2,100 = "CD"
!Resize wider allows print in new area
RESET
RESIZE 25,80
PUSH "AB\e[79GCD"
?screen_chars 0,0,1,2 = 0x41,0x42
?screen_chars 0,78,1,80 = 0x43,0x44
?screen_chars 0,0,1,2 = "AB"
?screen_chars 0,78,1,80 = "CD"
RESIZE 25,100
?screen_chars 0,0,1,2 = 0x41,0x42
?screen_chars 0,78,1,80 = 0x43,0x44
?screen_chars 0,0,1,2 = "AB"
?screen_chars 0,78,1,80 = "CD"
PUSH "E"
?screen_chars 0,78,1,81 = 0x43,0x44,0x45
?screen_chars 0,78,1,81 = "CDE"
!Resize shorter with blanks just truncates
RESET
RESIZE 25,80
PUSH "Top\e[10HLine 10"
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
?screen_chars 9,0,10,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31,0x30
?screen_chars 0,0,1,80 = "Top"
?screen_chars 9,0,10,80 = "Line 10"
?cursor = 9,7
RESIZE 20,80
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
?screen_chars 9,0,10,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31,0x30
?screen_chars 0,0,1,80 = "Top"
?screen_chars 9,0,10,80 = "Line 10"
?cursor = 9,7
!Resize shorter with content must scroll
RESET
RESIZE 25,80
PUSH "Top\e[25HLine 25\e[15H"
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
?screen_chars 24,0,25,80 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
?screen_chars 0,0,1,80 = "Top"
?screen_chars 24,0,25,80 = "Line 25"
?cursor = 14,0
WANTSCREEN b
RESIZE 20,80
@@ -51,7 +51,7 @@ RESIZE 20,80
sb_pushline 80 =
sb_pushline 80 =
?screen_chars 0,0,1,80 =
?screen_chars 19,0,20,80 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
?screen_chars 19,0,20,80 = "Line 25"
?cursor = 9,0
!Resize shorter does not lose line with cursor
@@ -62,11 +62,11 @@ RESIZE 25,80
WANTSCREEN b
PUSH "\e[24HLine 24\r\nLine 25\r\n"
sb_pushline 80 =
?screen_chars 23,0,24,10 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
?screen_chars 23,0,24,10 = "Line 25"
?cursor = 24,0
RESIZE 24,80
sb_pushline 80 =
?screen_chars 22,0,23,10 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
?screen_chars 22,0,23,10 = "Line 25"
?cursor = 23,0
!Resize taller attempts to pop scrollback
@@ -74,8 +74,8 @@ RESET
WANTSCREEN -b
RESIZE 25,80
PUSH "Line 1\e[25HBottom\e[15H"
?screen_chars 0,0,1,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31
?screen_chars 24,0,25,80 = 0x42,0x6f,0x74,0x74,0x6f,0x6d
?screen_chars 0,0,1,80 = "Line 1"
?screen_chars 24,0,25,80 = "Bottom"
?cursor = 14,0
WANTSCREEN b
RESIZE 30,80
@@ -84,7 +84,18 @@ RESIZE 30,80
sb_popline 80
sb_popline 80
sb_popline 80
?screen_chars 0,0,1,80 = 0x41,0x42,0x43,0x44,0x45
?screen_chars 5,0,6,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31
?screen_chars 29,0,30,80 = 0x42,0x6f,0x74,0x74,0x6f,0x6d
?screen_chars 0,0,1,80 = "ABCDE"
?screen_chars 5,0,6,80 = "Line 1"
?screen_chars 29,0,30,80 = "Bottom"
?cursor = 19,0
WANTSCREEN -b
!Resize can operate on altscreen
RESET
WANTSCREEN a
RESIZE 25,80
PUSH "Main screen\e[?1049h\e[HAlt screen"
RESIZE 30,80
?screen_chars 0,0,1,3 = "Alt"
PUSH "\e[?1049l"
?screen_chars 0,0,1,3 = "Mai"

View File

@@ -29,27 +29,27 @@ PUSH "\e[11mF\e[m"
!Foreground
PUSH "\e[31mG\e[m"
?screen_cell 0,6 = {0x47} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)
?screen_cell 0,6 = {0x47} width=1 attrs={} fg=idx(1) bg=rgb(0,0,0)
!Background
PUSH "\e[42mH\e[m"
?screen_cell 0,7 = {0x48} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,224,0)
?screen_cell 0,7 = {0x48} width=1 attrs={} fg=rgb(240,240,240) bg=idx(2)
!EL sets reverse and colours to end of line
PUSH "\e[H\e[7;33;44m\e[K"
?screen_cell 0,0 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,79 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,0 = {} width=1 attrs={R} fg=idx(3) bg=idx(4)
?screen_cell 0,79 = {} width=1 attrs={R} fg=idx(3) bg=idx(4)
!DECSCNM xors reverse for entire screen
PUSH "\e[?5h"
?screen_cell 0,0 = {} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,79 = {} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,0 = {} width=1 attrs={} fg=idx(3) bg=idx(4)
?screen_cell 0,79 = {} width=1 attrs={} fg=idx(3) bg=idx(4)
?screen_cell 1,0 = {} width=1 attrs={R} fg=rgb(240,240,240) bg=rgb(0,0,0)
PUSH "\e[?5\$p"
output "\e[?5;1\$y"
PUSH "\e[?5l"
?screen_cell 0,0 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,79 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
?screen_cell 0,0 = {} width=1 attrs={R} fg=idx(3) bg=idx(4)
?screen_cell 0,79 = {} width=1 attrs={R} fg=idx(3) bg=idx(4)
?screen_cell 1,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
PUSH "\e[?5\$p"
output "\e[?5;2\$y"

View File

@@ -30,3 +30,9 @@ PUSH "abcde"
?screen_cell 0,0 = {0x61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
PUSH "\e#6"
?screen_cell 0,0 = {0x61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)
!DWL doesn't spill over on scroll
RESET
PUSH "\e[25H\e#6Final\r\n"
?screen_cell 23,0 = {0x46} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)
?screen_cell 24,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)

View File

@@ -14,4 +14,4 @@ PUSH "\e[?25l"
!Title
PUSH "\e]2;Here is my title\a"
settermprop 4 "Here is my title"
settermprop 4 ["Here is my title"]

View File

@@ -60,6 +60,26 @@ static VTermKey strp_key(char *str)
return VTERM_KEY_NONE;
}
static void print_color(const VTermColor *col)
{
if (VTERM_COLOR_IS_RGB(col)) {
printf("rgb(%d,%d,%d", col->red, col->green, col->blue);
}
else if (VTERM_COLOR_IS_INDEXED(col)) {
printf("idx(%d", col->index);
}
else {
printf("invalid(%d", col->type);
}
if (VTERM_COLOR_IS_DEFAULT_FG(col)) {
printf(",is_default_fg");
}
if (VTERM_COLOR_IS_DEFAULT_BG(col)) {
printf(",is_default_bg");
}
printf(")");
}
static VTerm *vt;
static VTermState *state;
static VTermScreen *screen;
@@ -153,21 +173,44 @@ static int parser_csi(const char *leader, const long args[], int argcount, const
return 1;
}
static int parser_osc(const char *command, size_t cmdlen, void *user UNUSED)
static int parser_osc(int command, VTermStringFragment frag, void *user UNUSED)
{
printf("osc ");
printhex(command, cmdlen);
if(frag.initial) {
if(command == -1)
printf("[");
else
printf("[%d;", command);
}
printhex(frag.str, frag.len);
if(frag.final)
printf("]");
printf("\n");
return 1;
}
static int parser_dcs(const char *command, size_t cmdlen, void *user UNUSED)
static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user UNUSED)
{
printf("dcs ");
printhex(command, cmdlen);
if(frag.initial) {
size_t i;
printf("[");
for(i = 0; i < commandlen; i++)
printf("%02x", command[i]);
}
printhex(frag.str, frag.len);
if(frag.final)
printf("]");
printf("\n");
return 1;
@@ -183,7 +226,14 @@ static VTermParserCallbacks parser_cbs = {
NULL // resize
};
// These callbacks are shared by State and Screen
static VTermStateFallbacks fallbacks = {
parser_control, // control
parser_csi, // csi
parser_osc, // osc
parser_dcs // dcs
};
/* These callbacks are shared by State and Screen */
static int want_movecursor = 0;
static VTermPos state_pos;
@@ -239,10 +289,13 @@ static int settermprop(VTermProp prop, VTermValue *val, void *user UNUSED)
printf("settermprop %d %d\n", prop, val->number);
return 1;
case VTERM_VALUETYPE_STRING:
printf("settermprop %d \"%s\"\n", prop, val->string);
printf("settermprop %d %s\"%.*s\"%s\n", prop,
val->string.initial ? "[" : "", val->string.len, val->string.str, val->string.final ? "]" : "");
return 1;
case VTERM_VALUETYPE_COLOR:
printf("settermprop %d rgb(%d,%d,%d)\n", prop, val->color.red, val->color.green, val->color.blue);
printf("settermprop %d ", prop);
print_color(&val->color);
printf("\n");
return 1;
case VTERM_N_VALUETYPES:
@@ -262,7 +315,7 @@ static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user UNUSED)
return 1;
printf("putglyph ");
for(i = 0; info->chars[i]; i++)
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++)
printf(i ? ",%x" : "%x", info->chars[i]);
printf(" %d %d,%d", info->width, pos.row, pos.col);
if(info->protected_cell)
@@ -295,6 +348,7 @@ static struct {
int italic;
int blink;
int reverse;
int conceal;
int strike;
int font;
VTermColor foreground;
@@ -318,6 +372,9 @@ static int state_setpenattr(VTermAttr attr, VTermValue *val, void *user UNUSED)
case VTERM_ATTR_REVERSE:
state_pen.reverse = val->boolean;
break;
case VTERM_ATTR_CONCEAL:
state_pen.conceal = val->boolean;
break;
case VTERM_ATTR_STRIKE:
state_pen.strike = val->boolean;
break;
@@ -528,7 +585,7 @@ int main(int argc UNUSED, char **argv UNUSED)
want_settermprop = sense;
break;
case 'f':
vterm_state_set_unrecognised_fallbacks(state, sense ? &parser_cbs : NULL, NULL);
vterm_state_set_unrecognised_fallbacks(state, sense ? &fallbacks : NULL, NULL);
break;
default:
fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]);
@@ -540,7 +597,6 @@ int main(int argc UNUSED, char **argv UNUSED)
int sense = 1;
if(!screen)
screen = vterm_obtain_screen(vt);
vterm_screen_enable_altscreen(screen, 1);
vterm_screen_set_callbacks(screen, &screen_cbs, NULL);
while(line[i] == ' ')
@@ -550,6 +606,9 @@ int main(int argc UNUSED, char **argv UNUSED)
case '-':
sense = 0;
break;
case 'a':
vterm_screen_enable_altscreen(screen, 1);
break;
case 'd':
want_screen_damage = sense;
break;
@@ -797,14 +856,35 @@ int main(int argc UNUSED, char **argv UNUSED)
printf("%d\n", state_pen.font);
}
else if(streq(linep, "foreground")) {
printf("rgb(%d,%d,%d)\n", state_pen.foreground.red, state_pen.foreground.green, state_pen.foreground.blue);
print_color(&state_pen.foreground);
printf("\n");
}
else if(streq(linep, "background")) {
printf("rgb(%d,%d,%d)\n", state_pen.background.red, state_pen.background.green, state_pen.background.blue);
print_color(&state_pen.background);
printf("\n");
}
else
printf("?\n");
}
else if(strstartswith(line, "?lineinfo ")) {
char *linep = line + 10;
int row;
const VTermLineInfo *info;
while(linep[0] == ' ')
linep++;
if(sscanf(linep, "%d", &row) < 1) {
printf("! lineinfo unrecognised input\n");
goto abort_line;
}
info = vterm_state_get_lineinfo(state, row);
if(info->doublewidth)
printf("dwl ");
if(info->doubleheight)
printf("dhl ");
if(info->continuation)
printf("cont ");
printf("\n");
}
else if(strstartswith(line, "?screen_chars ")) {
char *linep = line + 13;
VTermRect rect;
@@ -896,8 +976,13 @@ int main(int argc UNUSED, char **argv UNUSED)
printf("} ");
if(cell.attrs.dwl) printf("dwl ");
if(cell.attrs.dhl) printf("dhl-%s ", cell.attrs.dhl == 2 ? "bottom" : "top");
printf("fg=rgb(%d,%d,%d) ", cell.fg.red, cell.fg.green, cell.fg.blue);
printf("bg=rgb(%d,%d,%d)\n", cell.bg.red, cell.bg.green, cell.bg.blue);
printf("fg=");
vterm_screen_convert_color_to_rgb(screen, &cell.fg);
print_color(&cell.fg);
printf(" bg=");
vterm_screen_convert_color_to_rgb(screen, &cell.bg);
print_color(&cell.bg);
printf("\n");
}
else if(strstartswith(line, "?screen_eol ")) {
VTermPos pos;

View File

@@ -11,7 +11,8 @@ my $VALGRIND = 0;
my $EXECUTABLE = "t/.libs/harness";
GetOptions(
'valgrind|v+' => \$VALGRIND,
'executable|e=s' => \$EXECUTABLE
'executable|e=s' => \$EXECUTABLE,
'fail-early|F' => \(my $FAIL_EARLY),
) or exit 1;
my ( $hin, $hout, $hpid );
@@ -28,6 +29,8 @@ my $exitcode = 0;
my $command;
my @expect;
my $linenum = 0;
sub do_onetest
{
$hin->print( "$command\n" );
@@ -41,7 +44,7 @@ sub do_onetest
chomp $outline;
if( !@expect ) {
print "# Test failed\n" unless $fail_printed++;
print "# line $linenum: Test failed\n" unless $fail_printed++;
print "# expected nothing more\n" .
"# Actual: $outline\n";
next;
@@ -51,18 +54,19 @@ sub do_onetest
next if $expectation eq $outline;
print "# Test failed\n" unless $fail_printed++;
print "# line $linenum: Test failed\n" unless $fail_printed++;
print "# Expected: $expectation\n" .
"# Actual: $outline\n";
}
if( @expect ) {
print "# Test failed\n" unless $fail_printed++;
print "# line $linenum: Test failed\n" unless $fail_printed++;
print "# Expected: $_\n" .
"# didn't happen\n" for @expect;
}
$exitcode = 1 if $fail_printed;
exit $exitcode if $exitcode and $FAIL_EARLY;
}
sub do_line
@@ -103,8 +107,15 @@ sub do_line
elsif( $line =~ m/^csi (\S+) (.*)$/ ) {
$line = sprintf "csi %02x %s", eval($1), $2; # TODO
}
elsif( $line =~ m/^(escape|osc|dcs) (.*)$/ ) {
$line = "$1 " . join "", map sprintf("%02x", $_), unpack "C*", eval($2);
elsif( $line =~ m/^(osc) (\[\d+)? *(.*?)(\]?)$/ ) {
my ( $cmd, $initial, $data, $final ) = ( $1, $2, $3, $4 );
$initial //= "";
$initial .= ";" if $initial =~ m/\d+/;
$line = "$cmd $initial" . join( "", map sprintf("%02x", $_), unpack "C*", length $data ? eval($data) : "" ) . "$final";
}
elsif( $line =~ m/^(escape|dcs) (\[?)(.*?)(\]?)$/ ) {
$line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", eval($3) ) . "$4";
}
elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) {
$line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2";
@@ -133,27 +144,35 @@ sub do_line
$response = pack "C*", map hex, split m/,/, $response;
if( $response ne $want ) {
print "# Assert ?screen_row $row failed:\n" .
print "# line $linenum: Assert ?screen_row $row failed:\n" .
"# Expected: $want\n" .
"# Actual: $response\n";
$exitcode = 1;
exit $exitcode if $exitcode and $FAIL_EARLY;
}
}
# Assertions start with '?'
elsif( $line =~ s/^\?([a-z]+.*?=)\s+// ) {
elsif( $line =~ s/^\?([a-z]+.*?=)\s*// ) {
do_onetest if defined $command;
my ( $assertion ) = $1 =~ m/^(.*)\s+=/;
my $expectation = $line;
$hin->print( "\?$assertion\n" );
my $response = <$hout>; defined $response or wait, die "Test harness failed - $?\n";
chomp $response;
chomp $response; $response =~ s/^\s+|\s+$//g;
if( $response ne $line ) {
print "# Assert $assertion failed:\n" .
"# Expected: $line\n" .
# Some convenience formatting
if( $assertion =~ m/^screen_chars/ and $expectation =~ m/^"/ ) {
$expectation = join ",", map sprintf("0x%02x", ord $_), split m//, eval($expectation);
}
if( $response ne $expectation ) {
print "# line $linenum: Assert $assertion failed:\n" .
"# Expected: $expectation\n" .
"# Actual: $response\n";
$exitcode = 1;
exit $exitcode if $exitcode and $FAIL_EARLY;
}
}
# Test controls start with '$'
@@ -176,6 +195,7 @@ sub do_line
open my $test, "<", $ARGV[0] or die "Cannot open test script $ARGV[0] - $!";
while( my $line = <$test> ) {
$linenum++;
$line =~ s/^\s+//;
chomp $line;

221
src/map.c
View File

@@ -204,6 +204,86 @@ showmap(
out_flush(); // show one line at a time
}
static int
map_add(
mapblock_T **map_table,
mapblock_T **abbr_table,
char_u *keys,
char_u *rhs,
char_u *orig_rhs,
int noremap,
int nowait,
int silent,
int mode,
int is_abbr,
#ifdef FEAT_EVAL
int expr,
scid_T sid, // -1 to use current_sctx
linenr_T lnum,
#endif
int simplified)
{
mapblock_T *mp = ALLOC_ONE(mapblock_T);
if (mp == NULL)
return FAIL;
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*keys == Ctrl_C)
{
if (map_table == curbuf->b_maphash)
curbuf->b_mapped_ctrl_c |= mode;
else
mapped_ctrl_c |= mode;
}
mp->m_keys = vim_strsave(keys);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
if (mp->m_keys == NULL || mp->m_str == NULL)
{
vim_free(mp->m_keys);
vim_free(mp->m_str);
vim_free(mp->m_orig_str);
vim_free(mp);
return FAIL;
}
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = nowait;
mp->m_silent = silent;
mp->m_mode = mode;
mp->m_simplified = simplified;
#ifdef FEAT_EVAL
mp->m_expr = expr;
if (sid >= 0)
{
mp->m_script_ctx.sc_sid = sid;
mp->m_script_ctx.sc_lnum = lnum;
}
else
{
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
}
#endif
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbr)
{
mp->m_next = *abbr_table;
*abbr_table = mp;
}
else
{
int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
return OK;
}
/*
* map[!] : show all key mappings
* map[!] {lhs} : show key mapping for {lhs}
@@ -501,7 +581,8 @@ do_map(
msg_start();
// Check if a new local mapping wasn't already defined globally.
if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1)
if (unique && map_table == curbuf->b_maphash
&& haskey && hasarg && maptype != 1)
{
// need to loop over all global hash lists
for (hash = 0; hash < 256 && !got_int; ++hash)
@@ -519,7 +600,6 @@ do_map(
// check entries with the same mode
if ((mp->m_mode & mode) != 0
&& mp->m_keylen == len
&& unique
&& STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
{
if (abbrev)
@@ -759,58 +839,16 @@ do_map(
continue; // have added the new entry already
// Get here when adding a new entry to the maphash[] list or abbrlist.
mp = ALLOC_ONE(mapblock_T);
if (mp == NULL)
if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
noremap, nowait, silent, mode, abbrev,
#ifdef FEAT_EVAL
expr, /* sid */ -1, /* lnum */ 0,
#endif
did_simplify && keyround == 1) == FAIL)
{
retval = 4; // no mem
goto theend;
}
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*keys == Ctrl_C)
{
if (map_table == curbuf->b_maphash)
curbuf->b_mapped_ctrl_c |= mode;
else
mapped_ctrl_c |= mode;
}
mp->m_keys = vim_strsave(keys);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
if (mp->m_keys == NULL || mp->m_str == NULL)
{
vim_free(mp->m_keys);
vim_free(mp->m_str);
vim_free(mp->m_orig_str);
vim_free(mp);
retval = 4; // no mem
goto theend;
}
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = nowait;
mp->m_silent = silent;
mp->m_mode = mode;
mp->m_simplified = did_simplify && keyround == 1;
#ifdef FEAT_EVAL
mp->m_expr = expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
#endif
// add the new entry in front of the abbrlist or maphash[] list
if (abbrev)
{
mp->m_next = *abbr_table;
*abbr_table = mp;
}
else
{
n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
}
theend:
@@ -2209,13 +2247,96 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact)
dict_add_number(dict, "buffer", (long)buffer_local);
dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
dict_add_string(dict, "mode", mapmode);
dict_add_number(dict, "simplified", mp->m_simplified);
vim_free(lhs);
vim_free(mapmode);
}
}
/*
* "mapset()" function
*/
void
f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
{
char_u *keys;
char_u *keys_buf = NULL;
char_u *which;
int mode;
char_u buf[NUMBUFLEN];
int is_abbr;
dict_T *d;
char_u *lhs;
char_u *rhs;
int noremap;
int expr;
int silent;
scid_T sid;
linenr_T lnum;
mapblock_T **map_table = maphash;
mapblock_T **abbr_table = &first_abbr;
int nowait;
int simplified;
char_u *arg;
which = tv_get_string_buf_chk(&argvars[0], buf);
mode = get_map_mode(&which, 0);
is_abbr = (int)tv_get_number(&argvars[1]);
if (argvars[2].v_type != VAR_DICT)
{
emsg(_(e_dictkey));
return;
}
d = argvars[2].vval.v_dict;
// Get the values in the same order as above in get_maparg().
lhs = dict_get_string(d, (char_u *)"lhs", FALSE);
if (lhs == NULL)
{
emsg(_("E99: lhs entry missing in mapset() dict argument"));
return;
}
rhs = dict_get_string(d, (char_u *)"rhs", FALSE);
if (rhs == NULL)
{
emsg(_("E99: rhs entry missing in mapset() dict argument"));
return;
}
noremap = dict_get_number(d, (char_u *)"noremap") ? REMAP_NONE: 0;
if (dict_get_number(d, (char_u *)"script") != 0)
noremap = REMAP_SCRIPT;
expr = dict_get_number(d, (char_u *)"expr") != 0;
silent = dict_get_number(d, (char_u *)"silent") != 0;
sid = dict_get_number(d, (char_u *)"sid");
lnum = dict_get_number(d, (char_u *)"lnum");
if (dict_get_number(d, (char_u *)"buffer"))
{
map_table = curbuf->b_maphash;
abbr_table = &curbuf->b_first_abbr;
}
nowait = dict_get_number(d, (char_u *)"nowait") != 0;
// mode from the dict is not used
simplified = dict_get_number(d, (char_u *)"simplified") != 0;
// Delete any existing mapping for this lhs and mode.
arg = vim_strsave(lhs);
if (arg == NULL)
return;
do_map(1, arg, mode, is_abbr);
vim_free(arg);
keys = replace_termcodes(lhs, &keys_buf,
REPTERM_FROM_PART | REPTERM_DO_LT, NULL);
(void)map_add(map_table, abbr_table, keys, rhs, rhs, noremap,
nowait, silent, mode, is_abbr, expr, sid, lnum, simplified);
vim_free(keys_buf);
}
#endif
#if defined(MSWIN) || defined(MACOS_X)
# define VIS_SEL (VISUAL+SELECTMODE) // abbreviation

View File

@@ -18,6 +18,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what);
void check_map_keycodes(void);
char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, int *local_ptr);
void get_maparg(typval_T *argvars, typval_T *rettv, int exact);
void f_mapset(typval_T *argvars, typval_T *rettv);
void init_mappings(void);
void add_map(char_u *map, int mode);
int langmap_adjust_mb(int c);

View File

@@ -6357,12 +6357,6 @@ static int grey_ramp[] = {
0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
};
# ifdef FEAT_TERMINAL
# include "libvterm/include/vterm.h" // for VTERM_ANSI_INDEX_NONE
# else
# define VTERM_ANSI_INDEX_NONE 0
# endif
static char_u ansi_table[16][4] = {
// R G B idx
{ 0, 0, 0, 1}, // black
@@ -6384,6 +6378,8 @@ static char_u ansi_table[16][4] = {
{255, 255, 255, 16}, // white
};
#define ANSI_INDEX_NONE 0
void
cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx)
{
@@ -6403,7 +6399,7 @@ cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx)
*r = cube_value[idx / 36 % 6];
*g = cube_value[idx / 6 % 6];
*b = cube_value[idx % 6];
*ansi_idx = VTERM_ANSI_INDEX_NONE;
*ansi_idx = ANSI_INDEX_NONE;
}
else if (nr < 256)
{
@@ -6412,14 +6408,14 @@ cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx)
*r = grey_ramp[idx];
*g = grey_ramp[idx];
*b = grey_ramp[idx];
*ansi_idx = VTERM_ANSI_INDEX_NONE;
*ansi_idx = ANSI_INDEX_NONE;
}
else
{
*r = 0;
*g = 0;
*b = 0;
*ansi_idx = 0;
*ansi_idx = ANSI_INDEX_NONE;
}
}
#endif

View File

@@ -162,6 +162,7 @@ struct terminal_S {
char_u *tl_cursor_color; // NULL or allocated
int tl_using_altscreen;
garray_T tl_osc_buf; // incomplete OSC string
};
#define TMODE_ONCE 1 // CTRL-\ CTRL-N used
@@ -445,6 +446,7 @@ term_start(
#endif
ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
ga_init2(&term->tl_scrollback_postponed, sizeof(sb_line_T), 300);
ga_init2(&term->tl_osc_buf, sizeof(char), 300);
CLEAR_FIELD(split_ea);
if (opt->jo_curwin)
@@ -1015,6 +1017,7 @@ free_unused_terminals()
terminals_to_free = term->tl_next;
free_scrollback(term);
ga_clear(&term->tl_osc_buf);
term_free_vterm(term);
vim_free(term->tl_api);
@@ -1623,7 +1626,8 @@ cell2cellattr(const VTermScreenCell *cell, cellattr_T *attr)
static int
equal_celattr(cellattr_T *a, cellattr_T *b)
{
// Comparing the colors should be sufficient.
// We only compare the RGB colors, ignoring the ANSI index and type.
// Thus black set explicitly is equal the background black.
return a->fg.red == b->fg.red
&& a->fg.green == b->fg.green
&& a->fg.blue == b->fg.blue
@@ -2689,10 +2693,13 @@ color2index(VTermColor *color, int fg, int *boldp)
int blue = color->blue;
int green = color->green;
if (color->ansi_index != VTERM_ANSI_INDEX_NONE)
if (VTERM_COLOR_IS_DEFAULT_FG(color)
|| VTERM_COLOR_IS_DEFAULT_BG(color))
return 0;
if (VTERM_COLOR_IS_INDEXED(color))
{
// The first 16 colors and default: use the ANSI index.
switch (color->ansi_index)
switch (color->index + 1)
{
case 0: return 0;
case 1: return lookup_color( 0, fg, boldp) + 1; // black
@@ -2998,22 +3005,27 @@ handle_settermprop(
void *user)
{
term_T *term = (term_T *)user;
char_u *strval = NULL;
switch (prop)
{
case VTERM_PROP_TITLE:
strval = vim_strnsave((char_u *)value->string.str,
(int)value->string.len);
if (strval == NULL)
break;
vim_free(term->tl_title);
// a blank title isn't useful, make it empty, so that "running" is
// displayed
if (*skipwhite((char_u *)value->string) == NUL)
if (*skipwhite(strval) == NUL)
term->tl_title = NULL;
// Same as blank
else if (term->tl_arg0_cmd != NULL
&& STRNCMP(term->tl_arg0_cmd, (char_u *)value->string,
&& STRNCMP(term->tl_arg0_cmd, strval,
(int)STRLEN(term->tl_arg0_cmd)) == 0)
term->tl_title = NULL;
// Empty corrupted data of winpty
else if (STRNCMP(" - ", (char_u *)value->string, 4) == 0)
else if (STRNCMP(" - ", strval, 4) == 0)
term->tl_title = NULL;
#ifdef MSWIN
else if (!enc_utf8 && enc_codepage > 0)
@@ -3022,8 +3034,8 @@ handle_settermprop(
int length = 0;
MultiByteToWideChar_alloc(CP_UTF8, 0,
(char*)value->string, (int)STRLEN(value->string),
&ret, &length);
(char*)value->string.str,
(int)value->string.len, &ret, &length);
if (ret != NULL)
{
WideCharToMultiByte_alloc(enc_codepage, 0,
@@ -3034,7 +3046,10 @@ handle_settermprop(
}
#endif
else
term->tl_title = vim_strsave((char_u *)value->string);
{
term->tl_title = strval;
strval = NULL;
}
VIM_CLEAR(term->tl_status_text);
if (term == curbuf->b_term)
maketitle();
@@ -3057,7 +3072,11 @@ handle_settermprop(
break;
case VTERM_PROP_CURSORCOLOR:
cursor_color_copy(&term->tl_cursor_color, (char_u*)value->string);
strval = vim_strnsave((char_u *)value->string.str,
(int)value->string.len);
if (strval == NULL)
break;
cursor_color_copy(&term->tl_cursor_color, strval);
may_set_cursor_props(term);
break;
@@ -3069,6 +3088,8 @@ handle_settermprop(
default:
break;
}
vim_free(strval);
// Always return 1, otherwise vterm doesn't store the value internally.
return 1;
}
@@ -3815,7 +3836,14 @@ term_get_attr(win_T *wp, linenr_T lnum, int col)
static void
cterm_color2vterm(int nr, VTermColor *rgb)
{
cterm_color2rgb(nr, &rgb->red, &rgb->green, &rgb->blue, &rgb->ansi_index);
cterm_color2rgb(nr, &rgb->red, &rgb->green, &rgb->blue, &rgb->index);
if (rgb->index == 0)
rgb->type = VTERM_COLOR_RGB;
else
{
rgb->type = VTERM_COLOR_INDEXED;
--rgb->index;
}
}
/*
@@ -3847,7 +3875,8 @@ init_default_colors(term_T *term, win_T *wp)
}
fg->red = fg->green = fg->blue = fgval;
bg->red = bg->green = bg->blue = bgval;
fg->ansi_index = bg->ansi_index = VTERM_ANSI_INDEX_DEFAULT;
fg->type = VTERM_COLOR_RGB | VTERM_COLOR_DEFAULT_FG;
bg->type = VTERM_COLOR_RGB | VTERM_COLOR_DEFAULT_BG;
// The 'wincolor' or the highlight group overrules the defaults.
if (wp != NULL && *wp->w_p_wcr != NUL)
@@ -4181,21 +4210,32 @@ handle_call_command(term_T *term, channel_T *channel, listitem_T *item)
* We recognize a terminal API command.
*/
static int
parse_osc(const char *command, size_t cmdlen, void *user)
parse_osc(int command, VTermStringFragment frag, void *user)
{
term_T *term = (term_T *)user;
js_read_T reader;
typval_T tv;
channel_T *channel = term->tl_job == NULL ? NULL
: term->tl_job->jv_channel;
garray_T *gap = &term->tl_osc_buf;
// We recognize only OSC 5 1 ; {command}
if (cmdlen < 3 || STRNCMP(command, "51;", 3) != 0)
return 0; // not handled
if (command != 51)
return 0;
reader.js_buf = vim_strnsave((char_u *)command + 3, (int)(cmdlen - 3));
if (reader.js_buf == NULL)
// Concatenate what was received until the final piece is found.
if (ga_grow(gap, (int)frag.len + 1) == FAIL)
{
ga_clear(gap);
return 1;
}
mch_memmove((char *)gap->ga_data + gap->ga_len, frag.str, frag.len);
gap->ga_len += frag.len;
if (!frag.final)
return 1;
((char *)gap->ga_data)[gap->ga_len] = 0;
reader.js_buf = gap->ga_data;
reader.js_fill = NULL;
reader.js_used = 0;
if (json_decode(&reader, &tv, 0) == OK
@@ -4229,7 +4269,7 @@ parse_osc(const char *command, size_t cmdlen, void *user)
else
ch_log(channel, "Invalid JSON received");
vim_free(reader.js_buf);
ga_clear(gap);
clear_tv(&tv);
return 1;
}
@@ -4293,14 +4333,11 @@ parse_csi(
return 1;
}
static VTermParserCallbacks parser_fallbacks = {
NULL, // text
static VTermStateFallbacks state_fallbacks = {
NULL, // control
NULL, // escape
parse_csi, // csi
parse_osc, // osc
NULL, // dcs
NULL // resize
NULL // dcs
};
/*
@@ -4383,7 +4420,7 @@ create_vterm(term_T *term, int rows, int cols)
value.boolean = 0;
#endif
vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &value);
vterm_state_set_unrecognised_fallbacks(state, &parser_fallbacks, term);
vterm_state_set_unrecognised_fallbacks(state, &state_fallbacks, term);
return OK;
}
@@ -4484,21 +4521,30 @@ term_get_buf(typval_T *argvars, char *where)
return buf;
}
static int
same_color(VTermColor *a, VTermColor *b)
static void
clear_cell(VTermScreenCell *cell)
{
return a->red == b->red
&& a->green == b->green
&& a->blue == b->blue
&& a->ansi_index == b->ansi_index;
CLEAR_FIELD(*cell);
cell->fg.type = VTERM_COLOR_DEFAULT_FG;
cell->bg.type = VTERM_COLOR_DEFAULT_BG;
}
static void
dump_term_color(FILE *fd, VTermColor *color)
{
int index;
if (VTERM_COLOR_IS_INDEXED(color))
index = color->index + 1;
else if (color->type == 0)
// use RGB values
index = 255;
else
// default color
index = 0;
fprintf(fd, "%02x%02x%02x%d",
(int)color->red, (int)color->green, (int)color->blue,
(int)color->ansi_index);
index);
}
/*
@@ -4582,7 +4628,7 @@ f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
return;
}
CLEAR_FIELD(prev_cell);
clear_cell(&prev_cell);
screen = vterm_obtain_screen(term->tl_vterm);
state = vterm_obtain_state(term->tl_vterm);
@@ -4604,7 +4650,7 @@ f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
&& pos.row == cursor_pos.row);
if (vterm_screen_get_cell(screen, pos, &cell) == 0)
CLEAR_FIELD(cell);
clear_cell(&cell);
for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
{
@@ -4624,8 +4670,8 @@ f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
}
same_attr = vtermAttr2hl(cell.attrs)
== vtermAttr2hl(prev_cell.attrs)
&& same_color(&cell.fg, &prev_cell.fg)
&& same_color(&cell.bg, &prev_cell.bg);
&& vterm_color_is_equal(&cell.fg, &prev_cell.fg)
&& vterm_color_is_equal(&cell.bg, &prev_cell.bg);
if (same_chars && cell.width == prev_cell.width && same_attr
&& !is_cursor_pos)
{
@@ -4672,14 +4718,14 @@ f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
else
{
fprintf(fd, "%d", vtermAttr2hl(cell.attrs));
if (same_color(&cell.fg, &prev_cell.fg))
if (vterm_color_is_equal(&cell.fg, &prev_cell.fg))
fputs("&", fd);
else
{
fputs("#", fd);
dump_term_color(fd, &cell.fg);
}
if (same_color(&cell.bg, &prev_cell.bg))
if (vterm_color_is_equal(&cell.bg, &prev_cell.bg))
fputs("&", fd);
else
{
@@ -4722,6 +4768,14 @@ append_cell(garray_T *gap, cellattr_T *cell)
}
}
static void
clear_cellattr(cellattr_T *cell)
{
CLEAR_FIELD(*cell);
cell->fg.type = VTERM_COLOR_DEFAULT_FG;
cell->bg.type = VTERM_COLOR_DEFAULT_BG;
}
/*
* Read the dump file from "fd" and append lines to the current buffer.
* Return the cell width of the longest line.
@@ -4742,8 +4796,8 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos)
ga_init2(&ga_text, 1, 90);
ga_init2(&ga_cell, sizeof(cellattr_T), 90);
CLEAR_FIELD(cell);
CLEAR_FIELD(empty_cell);
clear_cellattr(&cell);
clear_cellattr(&empty_cell);
cursor_pos->row = -1;
cursor_pos->col = -1;
@@ -4853,7 +4907,7 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos)
}
else if (c == '#')
{
int red, green, blue, index = 0;
int red, green, blue, index = 0, type;
c = fgetc(fd);
red = hex2nr(c);
@@ -4875,20 +4929,37 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos)
index = index * 10 + (c - '0');
c = fgetc(fd);
}
if (is_bg)
if (index == 0 || index == 255)
{
cell.bg.red = red;
cell.bg.green = green;
cell.bg.blue = blue;
cell.bg.ansi_index = index;
type = VTERM_COLOR_RGB;
if (index == 0)
{
if (is_bg)
type |= VTERM_COLOR_DEFAULT_BG;
else
type |= VTERM_COLOR_DEFAULT_FG;
}
}
else
{
type = VTERM_COLOR_INDEXED;
index -= 1;
}
if (is_bg)
{
cell.bg.type = type;
cell.bg.red = red;
cell.bg.green = green;
cell.bg.blue = blue;
cell.bg.index = index;
}
else
{
cell.fg.type = type;
cell.fg.red = red;
cell.fg.green = green;
cell.fg.blue = blue;
cell.fg.ansi_index = index;
cell.fg.index = index;
}
}
else
@@ -5201,10 +5272,10 @@ term_load_dump(typval_T *argvars, typval_T *rettv, int do_diff)
if ((cellattr1 + col)->width
!= (cellattr2 + col)->width)
textline[col] = 'w';
else if (!same_color(&(cellattr1 + col)->fg,
else if (!vterm_color_is_equal(&(cellattr1 + col)->fg,
&(cellattr2 + col)->fg))
textline[col] = 'f';
else if (!same_color(&(cellattr1 + col)->bg,
else if (!vterm_color_is_equal(&(cellattr1 + col)->bg,
&(cellattr2 + col)->bg))
textline[col] = 'b';
else if (vtermAttr2hl((cellattr1 + col)->attrs)
@@ -5783,6 +5854,7 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
else
{
VTermScreenCell cell;
if (vterm_screen_get_cell(screen, pos, &cell) == 0)
break;
for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)

View File

@@ -850,8 +850,7 @@ func Test_gui_run_cmd_in_terminal()
" assume all the other systems have a cat command
let cmd = 'cat'
endif
let cmd = ':silent !' . cmd . " test_gui.vim\<CR>\<CR>"
call feedkeys(cmd, 'xt')
exe "silent !" . cmd . " test_gui.vim"
" TODO: how to check that the command ran in a separate terminal?
" Maybe check for $TERM (dumb vs xterm) in the spawned shell?
let &guioptions = save_guioptions

View File

@@ -148,36 +148,36 @@ func Test_json_decode()
call assert_fails("call json_decode('{\"\": \"ok\", \"\": \"bad\"}')", 'E938:')
call assert_equal({'n': 1}, json_decode('{"n":1,}'))
call assert_fails("call json_decode(\"{'n':'1',}\")", 'E474:')
call assert_fails("call json_decode(\"'n'\")", 'E474:')
call assert_fails("call json_decode(\"{'n':'1',}\")", 'E491:')
call assert_fails("call json_decode(\"'n'\")", 'E491:')
call assert_fails('call json_decode("\"")', "E474:")
call assert_fails('call json_decode("blah")', "E474:")
call assert_fails('call json_decode("\"")', "E491:")
call assert_fails('call json_decode("blah")', "E491:")
call assert_fails('call json_decode("true blah")', "E488:")
call assert_fails('call json_decode("<foobar>")', "E474:")
call assert_fails('call json_decode("<foobar>")', "E491:")
call assert_fails('call json_decode("{\"a\":1,\"a\":2}")', "E938:")
call assert_fails('call json_decode("{")', "E474:")
call assert_fails('call json_decode("{foobar}")', "E474:")
call assert_fails('call json_decode("{\"n\",")', "E474:")
call assert_fails('call json_decode("{\"n\":")', "E474:")
call assert_fails('call json_decode("{\"n\":1")', "E474:")
call assert_fails('call json_decode("{\"n\":1,")', "E474:")
call assert_fails('call json_decode("{\"n\",1}")', "E474:")
call assert_fails('call json_decode("{-}")', "E474:")
call assert_fails('call json_decode("{")', "E491:")
call assert_fails('call json_decode("{foobar}")', "E491:")
call assert_fails('call json_decode("{\"n\",")', "E491:")
call assert_fails('call json_decode("{\"n\":")', "E491:")
call assert_fails('call json_decode("{\"n\":1")', "E491:")
call assert_fails('call json_decode("{\"n\":1,")', "E491:")
call assert_fails('call json_decode("{\"n\",1}")', "E491:")
call assert_fails('call json_decode("{-}")', "E491:")
call assert_fails('call json_decode("[foobar]")', "E474:")
call assert_fails('call json_decode("[")', "E474:")
call assert_fails('call json_decode("[1")', "E474:")
call assert_fails('call json_decode("[1,")', "E474:")
call assert_fails('call json_decode("[1 2]")', "E474:")
call assert_fails('call json_decode("[foobar]")', "E491:")
call assert_fails('call json_decode("[")', "E491:")
call assert_fails('call json_decode("[1")', "E491:")
call assert_fails('call json_decode("[1,")', "E491:")
call assert_fails('call json_decode("[1 2]")', "E491:")
call assert_fails('call json_decode("[1,,2]")', "E474:")
call assert_fails('call json_decode("[1,,2]")', "E491:")
call assert_fails('call json_decode("{{}:42}")', "E474:")
call assert_fails('call json_decode("{[]:42}")', "E474:")
call assert_fails('call json_decode("{{}:42}")', "E491:")
call assert_fails('call json_decode("{[]:42}")', "E491:")
call assert_fails('call json_decode("\"\\u111Z\"")', 'E474:')
call assert_fails('call json_decode("\"\\u111Z\"")', 'E491:')
call assert_equal('[😂]', json_decode('"[\uD83D\uDE02]"'))
call assert_equal('a😂b', json_decode('"a\uD83D\uDE02b"'))
endfunc

View File

@@ -1,12 +1,12 @@
" Tests for maparg().
" Tests for maparg(), mapcheck() and mapset().
" Also test utf8 map with a 0x80 byte.
" Also test mapcheck()
function s:SID()
func s:SID()
return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$'))
endfun
endfunc
function Test_maparg()
funct Test_maparg()
new
set cpo-=<
set encoding=utf8
@@ -18,23 +18,23 @@ function Test_maparg()
call assert_equal("is<F4>foo", maparg('foo<C-V>'))
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>',
\ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1,
\ 'rhs': 'is<F4>foo', 'buffer': 0},
\ 'simplified': 1, 'rhs': 'is<F4>foo', 'buffer': 0},
\ maparg('foo<C-V>', '', 0, 1))
call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v',
\ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2,
\ 'rhs': 'isbar', 'buffer': 1},
\ 'simplified': 0, 'rhs': 'isbar', 'buffer': 1},
\ 'bar'->maparg('', 0, 1))
let lnum = expand('<sflnum>')
map <buffer> <nowait> foo bar
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ',
\ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar',
\ 'buffer': 1},
\ 'simplified': 0, 'buffer': 1},
\ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>')
tmap baz foo
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo',
\ 'buffer': 0},
\ 'simplified': 0, 'buffer': 0},
\ maparg('baz', 't', 0, 1))
map abc x<char-114>x
@@ -75,7 +75,7 @@ function Test_maparg()
let d = maparg('esc', 'i', 1, 1)
call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode])
abclear
endfunction
endfunc
func Test_mapcheck()
call assert_equal('', mapcheck('a'))
@@ -116,7 +116,7 @@ func Test_mapcheck()
unabbr ab
endfunc
function Test_range_map()
func Test_range_map()
new
" Outside of the range, minimum
inoremap <Char-0x1040> a
@@ -131,6 +131,31 @@ function Test_range_map()
inoremap <Char-0xf040> d
execute "normal a\uf040\<Esc>"
call assert_equal("abcd", getline(1))
endfunction
endfunc
func One_mapset_test(keys)
exe 'nnoremap ' .. a:keys .. ' original<CR>'
let orig = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, orig.lhs)
call assert_equal('original<CR>', orig.rhs)
call assert_equal('n', orig.mode)
exe 'nunmap ' .. a:keys
let d = maparg(a:keys, 'n', 0, 1)
call assert_equal({}, d)
call mapset('n', 0, orig)
let d = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, d.lhs)
call assert_equal('original<CR>', d.rhs)
call assert_equal('n', d.mode)
exe 'nunmap ' .. a:keys
endfunc
func Test_mapset()
call One_mapset_test('K')
call One_mapset_test('<F3>')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -1220,6 +1220,7 @@ func Test_terminal_dumpwrite_errors()
call delete('Xtest.dump')
call assert_fails("call term_dumpwrite(buf, '')", 'E482:')
call assert_fails("call term_dumpwrite(buf, test_null_string())", 'E482:')
call test_garbagecollect_now()
call StopVimInTerminal(buf)
call term_wait(buf)
call assert_fails("call term_dumpwrite(buf, 'Xtest.dump')", 'E958:')
@@ -1305,6 +1306,16 @@ func Test_terminal_dumpdiff_swap()
call assert_match('three four five', getline(3))
call assert_match('Undo', getline(45))
quit
" Diff two terminal dump files with different number of rows
" Swap the diffs
call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_winline_rnu.dump')
call assert_match('Test_popup_command_01.dump', getline(21))
call assert_match('Test_winline_rnu.dump', getline(42))
normal s
call assert_match('Test_winline_rnu.dump', getline(6))
call assert_match('Test_popup_command_01.dump', getline(27))
quit
endfunc
func Test_terminal_dumpdiff_options()
@@ -1619,6 +1630,7 @@ func Test_terminal_ansicolors_default()
call assert_equal(colors, term_getansicolors(buf))
call StopShellInTerminal(buf)
call TermWait(buf)
call assert_equal([], term_getansicolors(buf))
exe buf . 'bwipe'
endfunc
@@ -1673,9 +1685,11 @@ func Test_terminal_ansicolors_func()
let colors[4] = 'Invalid'
call assert_fails('call term_setansicolors(buf, colors)', 'E474:')
call assert_fails('call term_setansicolors(buf, {})', 'E714:')
call StopShellInTerminal(buf)
call TermWait(buf)
call assert_equal(0, term_setansicolors(buf, []))
exe buf . 'bwipe'
endfunc
@@ -2029,7 +2043,7 @@ func Test_terminal_switch_mode()
call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))})
call feedkeys("A", 'xt')
call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))})
call feedkeys("\<C-W>N", 'xt')
call feedkeys("\<C-\>\<C-N>", 'xt')
call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))})
call feedkeys("I", 'xt')
call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))})
@@ -2066,6 +2080,7 @@ func Test_terminal_normal_mode()
call term_sendkeys(buf, ":set culopt=line\r")
call VerifyScreenDump(buf, 'Test_terminal_normal_3', {})
call assert_fails('call term_sendkeys(buf, [])', 'E730:')
call term_sendkeys(buf, "a:q!\<CR>:q\<CR>:q\<CR>")
call StopVimInTerminal(buf)
call delete('XtermNormal')
@@ -2178,17 +2193,18 @@ func Test_term_getcursor()
call StopShellInTerminal(buf)
endfunc
" Test for term_gettitle()
func Test_term_gettitle()
" term_gettitle() returns an empty string for a non-terminal buffer
" and for a non-existing buffer.
call assert_equal('', bufnr('%')->term_gettitle())
call assert_equal('', term_gettitle(bufnr('$') + 1))
if !has('title') || &title == 0 || empty(&t_ts)
if !has('title') || empty(&t_ts)
throw "Skipped: can't get/set title"
endif
let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'])
let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', '-c', 'set title'])
if has('autoservername')
call WaitForAssert({-> assert_match('^\[No Name\] - VIM\d\+$', term_gettitle(term)) })
call term_sendkeys(term, ":e Xfoo\r")
@@ -2683,22 +2699,36 @@ func Test_term_keycode_translation()
\ '<F8>', '<F9>', '<F10>', '<F11>', '<F12>', '<Home>', '<S-Home>',
\ '<C-Home>', '<End>', '<S-End>', '<C-End>', '<Insert>', '<Del>',
\ '<Left>', '<S-Left>', '<C-Left>', '<Right>', '<S-Right>',
\ '<C-Right>', '<Up>', '<S-Up>', '<Down>', '<S-Down>',
\ '0123456789', "\t\t.+-*/"]
\ '<C-Right>', '<Up>', '<S-Up>', '<Down>', '<S-Down>']
for k in keys
call term_sendkeys(buf, "i\<C-K>" .. k .. "\<CR>\<C-\>\<C-N>")
call term_sendkeys(buf, "i")
for i in range(len(keys))
call term_sendkeys(buf, "\<C-U>\<C-K>" .. keys[i])
call term_wait(buf)
call assert_equal(output[i], term_getline(buf, 1))
endfor
call term_sendkeys(buf, "i\<K0>\<K1>\<K2>\<K3>\<K4>\<K5>\<K6>\<K7>")
call term_sendkeys(buf, "\<K8>\<K9>\<kEnter>\<kPoint>\<kPlus>")
call term_sendkeys(buf, "\<kMinus>\<kMultiply>\<kDivide>\<C-\>\<C-N>")
call term_sendkeys(buf, "\<Home>\<Ins>\<Tab>\<S-Tab>\<C-\>\<C-N>")
call term_sendkeys(buf, ":write Xkeycodes\<CR>")
let keypad_keys = ["\<k0>", "\<k1>", "\<k2>", "\<k3>", "\<k4>", "\<k5>",
\ "\<k6>", "\<k7>", "\<k8>", "\<k9>", "\<kPoint>", "\<kPlus>",
\ "\<kMinus>", "\<kMultiply>", "\<kDivide>"]
let keypad_output = ['0', '1', '2', '3', '4', '5',
\ '6', '7', '8', '9', '.', '+',
\ '-', '*', '/']
for i in range(len(keypad_keys))
" TODO: Mysteriously keypad 3 and 9 do not work on some systems.
if keypad_output[i] == '3' || keypad_output[i] == '9'
continue
endif
call term_sendkeys(buf, "\<C-U>" .. keypad_keys[i])
call term_wait(buf)
call assert_equal(keypad_output[i], term_getline(buf, 1))
endfor
call feedkeys("\<C-U>\<kEnter>\<BS>one\<C-W>.two", 'xt')
call term_wait(buf)
call assert_equal('two', term_getline(buf, 1))
call StopVimInTerminal(buf)
call assert_equal(output, readfile('Xkeycodes'))
call delete('Xkeycodes')
endfunc
" Test for using the mouse in a terminal
@@ -2710,7 +2740,6 @@ func Test_term_mouse()
let save_term = &term
let save_ttymouse = &ttymouse
let save_clipboard = &clipboard
call test_override('no_query_mouse', 1)
set mouse=a term=xterm ttymouse=sgr mousetime=200 clipboard=
let lines =<< trim END
@@ -2720,6 +2749,8 @@ func Test_term_mouse()
END
call writefile(lines, 'Xtest_mouse')
" Create a terminal window running Vim for the test with mouse enabled
let prev_win = win_getid()
let buf = RunVimInTerminal('Xtest_mouse -n', {})
call term_sendkeys(buf, ":set nocompatible\<CR>")
call term_sendkeys(buf, ":set mouse=a term=xterm ttymouse=sgr\<CR>")
@@ -2728,6 +2759,12 @@ func Test_term_mouse()
call term_wait(buf)
redraw!
" Use the mouse to enter the terminal window
call win_gotoid(prev_win)
call feedkeys(MouseLeftClickCode(1, 1), 'x')
call feedkeys(MouseLeftReleaseCode(1, 1), 'x')
call assert_equal(1, getwininfo(win_getid())[0].terminal)
" Test for <LeftMouse> click/release
call test_setmouse(2, 5)
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
@@ -2810,7 +2847,6 @@ func Test_term_mouse()
let &ttymouse = save_ttymouse
let &clipboard = save_clipboard
set mousetime&
call test_override('no_query_mouse', 0)
call delete('Xtest_mouse')
call delete('Xbuf')
endfunc
@@ -2825,7 +2861,6 @@ func Test_term_modeless_selection()
let save_mouse = &mouse
let save_term = &term
let save_ttymouse = &ttymouse
call test_override('no_query_mouse', 1)
set mouse=a term=xterm ttymouse=sgr mousetime=200
set clipboard=autoselectml
@@ -2836,12 +2871,21 @@ func Test_term_modeless_selection()
END
call writefile(lines, 'Xtest_modeless')
" Create a terminal window running Vim for the test with mouse disabled
let prev_win = win_getid()
let buf = RunVimInTerminal('Xtest_modeless -n', {})
call term_sendkeys(buf, ":set nocompatible\<CR>")
call term_sendkeys(buf, ":set mouse=\<CR>")
call term_wait(buf)
redraw!
" Use the mouse to enter the terminal window
call win_gotoid(prev_win)
call feedkeys(MouseLeftClickCode(1, 1), 'x')
call feedkeys(MouseLeftReleaseCode(1, 1), 'x')
call term_wait(buf)
call assert_equal(1, getwininfo(win_getid())[0].terminal)
" Test for copying a modeless selection to clipboard
let @* = 'clean'
" communicating with X server may take a little time
@@ -2858,8 +2902,8 @@ func Test_term_modeless_selection()
let &term = save_term
let &ttymouse = save_ttymouse
set mousetime& clipboard&
call test_override('no_query_mouse', 0)
call delete('Xtest_modeless')
new | only!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -136,7 +136,7 @@ def Test_assignment_dict()
let dict2: dict<number> = #{one: 1, two: 2}
let dict3: dict<string> = #{key: 'value'}
let dict4: dict<any> = #{one: 1, two: '2'}
let dict5: dict<blob> = #{one: 0z01, tw: 0z02}
let dict5: dict<blob> = #{one: 0z01, two: 0z02}
call CheckDefExecFailure(['let dd = {}', 'dd[""] = 6'], 'E713:')
@@ -1721,6 +1721,11 @@ def Test_vim9_comment_not_compiled()
'dsearch /pat/#comment',
'bwipe!',
], 'E488:')
CheckScriptFailure([
'vim9script',
'func! SomeFunc()',
], 'E477:')
enddef
def Test_finish()

View File

@@ -746,6 +746,38 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
809,
/**/
808,
/**/
807,
/**/
806,
/**/
805,
/**/
804,
/**/
803,
/**/
802,
/**/
801,
/**/
800,
/**/
799,
/**/
798,
/**/
797,
/**/
796,
/**/
795,
/**/
794,
/**/
793,
/**/

View File

@@ -84,6 +84,12 @@ ex_vim9script(exarg_T *eap)
{
int lnum_start = SOURCING_LNUM - 1;
if (*p == '!')
{
emsg(_(e_nobang));
break;
}
// Handle :function and :def by calling def_function().
// It will read upto the matching :endded or :endfunction.
eap->cmdidx = *line == 'f' ? CMD_function : CMD_def;