forked from aniani/vim
patch 8.0.1523: cannot write and read terminal screendumps
Problem: Cannot write and read terminal screendumps. Solution: Add term_dumpwrite(), term_dumpread() and term_dumpdiff(). Also add assert_equalfile().
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
*eval.txt* For Vim version 8.0. Last change: 2018 Feb 10
|
*eval.txt* For Vim version 8.0. Last change: 2018 Feb 18
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -2020,6 +2020,8 @@ argv() List the argument list
|
|||||||
assert_beeps({cmd}) none assert {cmd} causes a beep
|
assert_beeps({cmd}) none assert {cmd} causes a beep
|
||||||
assert_equal({exp}, {act} [, {msg}])
|
assert_equal({exp}, {act} [, {msg}])
|
||||||
none assert {exp} is equal to {act}
|
none assert {exp} is equal to {act}
|
||||||
|
assert_equalfile({fname-one}, {fname-two})
|
||||||
|
none assert file contents is equal
|
||||||
assert_exception({error} [, {msg}])
|
assert_exception({error} [, {msg}])
|
||||||
none assert {error} is in v:exception
|
none assert {error} is in v:exception
|
||||||
assert_fails({cmd} [, {error}]) none assert {cmd} fails
|
assert_fails({cmd} [, {error}]) none assert {cmd} fails
|
||||||
@@ -2410,6 +2412,12 @@ tagfiles() List tags files used
|
|||||||
tan({expr}) Float tangent of {expr}
|
tan({expr}) Float tangent of {expr}
|
||||||
tanh({expr}) Float hyperbolic tangent of {expr}
|
tanh({expr}) Float hyperbolic tangent of {expr}
|
||||||
tempname() String name for a temporary file
|
tempname() String name for a temporary file
|
||||||
|
term_dumpdiff({filename}, {filename} [, {options}])
|
||||||
|
Number display difference between two dumps
|
||||||
|
term_dumpload({filename} [, {options}])
|
||||||
|
Number displaying a screen dump
|
||||||
|
term_dumpwrite({buf}, {filename} [, {max-height} [, {max-width}]])
|
||||||
|
none dump terminal window contents
|
||||||
term_getaltscreen({buf}) Number get the alternate screen flag
|
term_getaltscreen({buf}) Number get the alternate screen flag
|
||||||
term_getattr({attr}, {what}) Number get the value of attribute {what}
|
term_getattr({attr}, {what}) Number get the value of attribute {what}
|
||||||
term_getcursor({buf}) List get the cursor position of a terminal
|
term_getcursor({buf}) List get the cursor position of a terminal
|
||||||
@@ -2590,6 +2598,14 @@ assert_equal({expected}, {actual} [, {msg}])
|
|||||||
< Will result in a string to be added to |v:errors|:
|
< Will result in a string to be added to |v:errors|:
|
||||||
test.vim line 12: Expected 'foo' but got 'bar' ~
|
test.vim line 12: Expected 'foo' but got 'bar' ~
|
||||||
|
|
||||||
|
*assert_equalfile()*
|
||||||
|
assert_equalfile({fname-one}, {fname-two})
|
||||||
|
When the files {fname-one} and {fname-two} do not contain
|
||||||
|
exactly the same text an error message is added to |v:errors|.
|
||||||
|
When {fname-one} or {fname-two} does not exist the error will
|
||||||
|
mention that.
|
||||||
|
Mainly useful with |terminal-diff|.
|
||||||
|
|
||||||
assert_exception({error} [, {msg}]) *assert_exception()*
|
assert_exception({error} [, {msg}]) *assert_exception()*
|
||||||
When v:exception does not contain the string {error} an error
|
When v:exception does not contain the string {error} an error
|
||||||
message is added to |v:errors|.
|
message is added to |v:errors|.
|
||||||
@@ -4587,8 +4603,7 @@ getftype({fname}) *getftype()*
|
|||||||
"file" are returned. On MS-Windows a symbolic link to a
|
"file" are returned. On MS-Windows a symbolic link to a
|
||||||
directory returns "dir" instead of "link".
|
directory returns "dir" instead of "link".
|
||||||
|
|
||||||
*getjumplist()*
|
getjumplist([{winnr} [, {tabnr}]]) *getjumplist()*
|
||||||
getjumplist([{winnr} [, {tabnr}]])
|
|
||||||
Returns the |jumplist| for the specified window.
|
Returns the |jumplist| for the specified window.
|
||||||
|
|
||||||
Without arguments use the current window.
|
Without arguments use the current window.
|
||||||
@@ -8135,6 +8150,53 @@ tempname() *tempname()* *temp-file-name*
|
|||||||
For MS-Windows forward slashes are used when the 'shellslash'
|
For MS-Windows forward slashes are used when the 'shellslash'
|
||||||
option is set or when 'shellcmdflag' starts with '-'.
|
option is set or when 'shellcmdflag' starts with '-'.
|
||||||
|
|
||||||
|
*term_dumpdiff()*
|
||||||
|
term_dumpdiff({filename}, {filename} [, {options}])
|
||||||
|
Open a new window displaying the difference between the two
|
||||||
|
files. The files must have been created with
|
||||||
|
|term_dumpwrite()|.
|
||||||
|
Returns the buffer number or zero when the diff fails.
|
||||||
|
Also see |terminal-diff|.
|
||||||
|
NOTE: this does not work with double-width characters yet.
|
||||||
|
|
||||||
|
The top part of the buffer contains the contents of the first
|
||||||
|
file, the bottom part of the buffer contains the contents of
|
||||||
|
the second file. The middle part shows the differences.
|
||||||
|
The parts are separated by a line of dashes.
|
||||||
|
|
||||||
|
{options} are not implemented yet.
|
||||||
|
|
||||||
|
Each character in the middle part indicates a difference. If
|
||||||
|
there are multiple differences only the first in this list is
|
||||||
|
used:
|
||||||
|
X different character
|
||||||
|
w different width
|
||||||
|
f different foreground color
|
||||||
|
b different background color
|
||||||
|
a different attribute
|
||||||
|
+ missing position in first file
|
||||||
|
- missing position in second file
|
||||||
|
|
||||||
|
Using the "s" key the top and bottom parts are swapped. This
|
||||||
|
makes it easy to spot a difference.
|
||||||
|
|
||||||
|
*term_dumpload()*
|
||||||
|
term_dumpload({filename} [, {options}])
|
||||||
|
Open a new window displaying the contents of {filename}
|
||||||
|
The file must have been created with |term_dumpwrite()|.
|
||||||
|
Returns the buffer number or zero when it fails.
|
||||||
|
Also see |terminal-diff|.
|
||||||
|
|
||||||
|
{options} are not implemented yet.
|
||||||
|
|
||||||
|
*term_dumpwrite()*
|
||||||
|
term_dumpwrite({buf}, {filename} [, {max-height} [, {max-width}]])
|
||||||
|
Dump the contents of the terminal screen of {buf} in the file
|
||||||
|
{filename}. This uses a format that can be used with
|
||||||
|
|term_dumpread()| and |term_dumpdiff()|.
|
||||||
|
If {filename} already exists an error is given. *E953*
|
||||||
|
Also see |terminal-diff|.
|
||||||
|
|
||||||
term_getaltscreen({buf}) *term_getaltscreen()*
|
term_getaltscreen({buf}) *term_getaltscreen()*
|
||||||
Returns 1 if the terminal of {buf} is using the alternate
|
Returns 1 if the terminal of {buf} is using the alternate
|
||||||
screen.
|
screen.
|
||||||
|
67
src/eval.c
67
src/eval.c
@@ -8833,6 +8833,73 @@ assert_equal_common(typval_T *argvars, assert_type_T atype)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
assert_equalfile(typval_T *argvars)
|
||||||
|
{
|
||||||
|
char_u buf1[NUMBUFLEN];
|
||||||
|
char_u buf2[NUMBUFLEN];
|
||||||
|
char_u *fname1 = get_tv_string_buf_chk(&argvars[0], buf1);
|
||||||
|
char_u *fname2 = get_tv_string_buf_chk(&argvars[1], buf2);
|
||||||
|
garray_T ga;
|
||||||
|
FILE *fd1;
|
||||||
|
FILE *fd2;
|
||||||
|
|
||||||
|
if (fname1 == NULL || fname2 == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IObuff[0] = NUL;
|
||||||
|
fd1 = mch_fopen((char *)fname1, READBIN);
|
||||||
|
if (fd1 == NULL)
|
||||||
|
{
|
||||||
|
vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fd2 = mch_fopen((char *)fname2, READBIN);
|
||||||
|
if (fd2 == NULL)
|
||||||
|
{
|
||||||
|
fclose(fd1);
|
||||||
|
vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int c1, c2;
|
||||||
|
long count = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
c1 = fgetc(fd1);
|
||||||
|
c2 = fgetc(fd2);
|
||||||
|
if (c1 == EOF)
|
||||||
|
{
|
||||||
|
if (c2 != EOF)
|
||||||
|
STRCPY(IObuff, "first file is shorter");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (c2 == EOF)
|
||||||
|
{
|
||||||
|
STRCPY(IObuff, "second file is shorter");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (c1 != c2)
|
||||||
|
{
|
||||||
|
vim_snprintf((char *)IObuff, IOSIZE,
|
||||||
|
"difference at byte %ld", count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IObuff[0] != NUL)
|
||||||
|
{
|
||||||
|
prepare_assert_error(&ga);
|
||||||
|
ga_concat(&ga, IObuff);
|
||||||
|
assert_error(&ga);
|
||||||
|
ga_clear(&ga);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
assert_match_common(typval_T *argvars, assert_type_T atype)
|
assert_match_common(typval_T *argvars, assert_type_T atype)
|
||||||
{
|
{
|
||||||
|
@@ -46,6 +46,7 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv);
|
|||||||
static void f_argv(typval_T *argvars, typval_T *rettv);
|
static void f_argv(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_assert_beeps(typval_T *argvars, typval_T *rettv);
|
static void f_assert_beeps(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_assert_equal(typval_T *argvars, typval_T *rettv);
|
static void f_assert_equal(typval_T *argvars, typval_T *rettv);
|
||||||
|
static void f_assert_equalfile(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_assert_exception(typval_T *argvars, typval_T *rettv);
|
static void f_assert_exception(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_assert_fails(typval_T *argvars, typval_T *rettv);
|
static void f_assert_fails(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_assert_false(typval_T *argvars, typval_T *rettv);
|
static void f_assert_false(typval_T *argvars, typval_T *rettv);
|
||||||
@@ -487,6 +488,7 @@ static struct fst
|
|||||||
#endif
|
#endif
|
||||||
{"assert_beeps", 1, 2, f_assert_beeps},
|
{"assert_beeps", 1, 2, f_assert_beeps},
|
||||||
{"assert_equal", 2, 3, f_assert_equal},
|
{"assert_equal", 2, 3, f_assert_equal},
|
||||||
|
{"assert_equalfile", 2, 2, f_assert_equalfile},
|
||||||
{"assert_exception", 1, 2, f_assert_exception},
|
{"assert_exception", 1, 2, f_assert_exception},
|
||||||
{"assert_fails", 1, 2, f_assert_fails},
|
{"assert_fails", 1, 2, f_assert_fails},
|
||||||
{"assert_false", 1, 2, f_assert_false},
|
{"assert_false", 1, 2, f_assert_false},
|
||||||
@@ -847,6 +849,9 @@ static struct fst
|
|||||||
#endif
|
#endif
|
||||||
{"tempname", 0, 0, f_tempname},
|
{"tempname", 0, 0, f_tempname},
|
||||||
#ifdef FEAT_TERMINAL
|
#ifdef FEAT_TERMINAL
|
||||||
|
{"term_dumpdiff", 2, 3, f_term_dumpdiff},
|
||||||
|
{"term_dumpload", 1, 2, f_term_dumpload},
|
||||||
|
{"term_dumpwrite", 2, 4, f_term_dumpwrite},
|
||||||
{"term_getaltscreen", 1, 1, f_term_getaltscreen},
|
{"term_getaltscreen", 1, 1, f_term_getaltscreen},
|
||||||
{"term_getattr", 2, 2, f_term_getattr},
|
{"term_getattr", 2, 2, f_term_getattr},
|
||||||
{"term_getcursor", 1, 1, f_term_getcursor},
|
{"term_getcursor", 1, 1, f_term_getcursor},
|
||||||
@@ -1296,6 +1301,15 @@ f_assert_equal(typval_T *argvars, typval_T *rettv UNUSED)
|
|||||||
assert_equal_common(argvars, ASSERT_EQUAL);
|
assert_equal_common(argvars, ASSERT_EQUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "assert_equalfile(fname-one, fname-two)" function
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
f_assert_equalfile(typval_T *argvars, typval_T *rettv UNUSED)
|
||||||
|
{
|
||||||
|
assert_equalfile(argvars);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "assert_notequal(expected, actual[, msg])" function
|
* "assert_notequal(expected, actual[, msg])" function
|
||||||
*/
|
*/
|
||||||
|
@@ -7474,6 +7474,11 @@ v_visop(cmdarg_T *cap)
|
|||||||
static void
|
static void
|
||||||
nv_subst(cmdarg_T *cap)
|
nv_subst(cmdarg_T *cap)
|
||||||
{
|
{
|
||||||
|
#ifdef FEAT_TERMINAL
|
||||||
|
/* When showing output of term_dumpdiff() swap the top and botom. */
|
||||||
|
if (term_swap_diff() == OK)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
if (VIsual_active) /* "vs" and "vS" are the same as "vc" */
|
if (VIsual_active) /* "vs" and "vS" are the same as "vc" */
|
||||||
{
|
{
|
||||||
if (cap->cmdchar == 'S')
|
if (cap->cmdchar == 'S')
|
||||||
|
@@ -122,6 +122,7 @@ void reset_v_option_vars(void);
|
|||||||
void prepare_assert_error(garray_T *gap);
|
void prepare_assert_error(garray_T *gap);
|
||||||
void assert_error(garray_T *gap);
|
void assert_error(garray_T *gap);
|
||||||
void assert_equal_common(typval_T *argvars, assert_type_T atype);
|
void assert_equal_common(typval_T *argvars, assert_type_T atype);
|
||||||
|
void assert_equalfile(typval_T *argvars);
|
||||||
void assert_match_common(typval_T *argvars, assert_type_T atype);
|
void assert_match_common(typval_T *argvars, assert_type_T atype);
|
||||||
void assert_inrange(typval_T *argvars);
|
void assert_inrange(typval_T *argvars);
|
||||||
void assert_bool(typval_T *argvars, int isTrue);
|
void assert_bool(typval_T *argvars, int isTrue);
|
||||||
|
@@ -21,6 +21,10 @@ int term_get_attr(buf_T *buf, linenr_T lnum, int col);
|
|||||||
char_u *term_get_status_text(term_T *term);
|
char_u *term_get_status_text(term_T *term);
|
||||||
int set_ref_in_term(int copyID);
|
int set_ref_in_term(int copyID);
|
||||||
void set_terminal_default_colors(int cterm_fg, int cterm_bg);
|
void set_terminal_default_colors(int cterm_fg, int cterm_bg);
|
||||||
|
void f_term_dumpwrite(typval_T *argvars, typval_T *rettv);
|
||||||
|
int term_swap_diff(void);
|
||||||
|
void f_term_dumpdiff(typval_T *argvars, typval_T *rettv);
|
||||||
|
void f_term_dumpload(typval_T *argvars, typval_T *rettv);
|
||||||
void f_term_getaltscreen(typval_T *argvars, typval_T *rettv);
|
void f_term_getaltscreen(typval_T *argvars, typval_T *rettv);
|
||||||
void f_term_getattr(typval_T *argvars, typval_T *rettv);
|
void f_term_getattr(typval_T *argvars, typval_T *rettv);
|
||||||
void f_term_getcursor(typval_T *argvars, typval_T *rettv);
|
void f_term_getcursor(typval_T *argvars, typval_T *rettv);
|
||||||
|
858
src/terminal.c
858
src/terminal.c
@@ -47,6 +47,9 @@
|
|||||||
* - Redirecting output does not work on MS-Windows, Test_terminal_redir_file()
|
* - Redirecting output does not work on MS-Windows, Test_terminal_redir_file()
|
||||||
* is disabled.
|
* is disabled.
|
||||||
* - cursor blinks in terminal on widows with a timer. (xtal8, #2142)
|
* - cursor blinks in terminal on widows with a timer. (xtal8, #2142)
|
||||||
|
* - What to store in a session file? Shell at the prompt would be OK to
|
||||||
|
* restore, but others may not. Open the window and let the user start the
|
||||||
|
* command? Also see #2650.
|
||||||
* - When closing gvim with an active terminal buffer, the dialog suggests
|
* - When closing gvim with an active terminal buffer, the dialog suggests
|
||||||
* saving the buffer. Should say something else. (Manas Thakur, #2215)
|
* saving the buffer. Should say something else. (Manas Thakur, #2215)
|
||||||
* Also: #2223
|
* Also: #2223
|
||||||
@@ -55,9 +58,6 @@
|
|||||||
* - Adding WinBar to terminal window doesn't display, text isn't shifted down.
|
* - Adding WinBar to terminal window doesn't display, text isn't shifted down.
|
||||||
* - MS-Windows GUI: still need to type a key after shell exits? #1924
|
* - MS-Windows GUI: still need to type a key after shell exits? #1924
|
||||||
* - After executing a shell command the status line isn't redraw.
|
* - After executing a shell command the status line isn't redraw.
|
||||||
* - What to store in a session file? Shell at the prompt would be OK to
|
|
||||||
* restore, but others may not. Open the window and let the user start the
|
|
||||||
* command?
|
|
||||||
* - implement term_setsize()
|
* - implement term_setsize()
|
||||||
* - add test for giving error for invalid 'termsize' value.
|
* - add test for giving error for invalid 'termsize' value.
|
||||||
* - support minimal size when 'termsize' is "rows*cols".
|
* - support minimal size when 'termsize' is "rows*cols".
|
||||||
@@ -99,7 +99,8 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
VTermScreenCellAttrs attrs;
|
VTermScreenCellAttrs attrs;
|
||||||
char width;
|
char width;
|
||||||
VTermColor fg, bg;
|
VTermColor fg;
|
||||||
|
VTermColor bg;
|
||||||
} cellattr_T;
|
} cellattr_T;
|
||||||
|
|
||||||
typedef struct sb_line_S {
|
typedef struct sb_line_S {
|
||||||
@@ -153,6 +154,9 @@ struct terminal_S {
|
|||||||
int tl_scrollback_scrolled;
|
int tl_scrollback_scrolled;
|
||||||
cellattr_T tl_default_color;
|
cellattr_T tl_default_color;
|
||||||
|
|
||||||
|
linenr_T tl_top_diff_rows; /* rows of top diff file or zero */
|
||||||
|
linenr_T tl_bot_diff_rows; /* rows of bottom diff file */
|
||||||
|
|
||||||
VTermPos tl_cursor_pos;
|
VTermPos tl_cursor_pos;
|
||||||
int tl_cursor_visible;
|
int tl_cursor_visible;
|
||||||
int tl_cursor_blink;
|
int tl_cursor_blink;
|
||||||
@@ -282,12 +286,35 @@ setup_job_options(jobopt_T *opt, int rows, int cols)
|
|||||||
opt->jo_term_cols = cols;
|
opt->jo_term_cols = cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close a terminal buffer (and its window). Used when creating the terminal
|
||||||
|
* fails.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
term_close_buffer(buf_T *buf, buf_T *old_curbuf)
|
||||||
|
{
|
||||||
|
free_terminal(buf);
|
||||||
|
if (old_curbuf != NULL)
|
||||||
|
{
|
||||||
|
--curbuf->b_nwindows;
|
||||||
|
curbuf = old_curbuf;
|
||||||
|
curwin->w_buffer = curbuf;
|
||||||
|
++curbuf->b_nwindows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wiping out the buffer will also close the window and call
|
||||||
|
* free_terminal(). */
|
||||||
|
do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a terminal window and return its buffer.
|
* Start a terminal window and return its buffer.
|
||||||
|
* When "without_job" is TRUE only create the buffer, b_term and open the
|
||||||
|
* window.
|
||||||
* Returns NULL when failed.
|
* Returns NULL when failed.
|
||||||
*/
|
*/
|
||||||
static buf_T *
|
static buf_T *
|
||||||
term_start(typval_T *argvar, jobopt_T *opt, int forceit)
|
term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
|
||||||
{
|
{
|
||||||
exarg_T split_ea;
|
exarg_T split_ea;
|
||||||
win_T *old_curwin = curwin;
|
win_T *old_curwin = curwin;
|
||||||
@@ -454,6 +481,9 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
|
|||||||
set_term_and_win_size(term);
|
set_term_and_win_size(term);
|
||||||
setup_job_options(opt, term->tl_rows, term->tl_cols);
|
setup_job_options(opt, term->tl_rows, term->tl_cols);
|
||||||
|
|
||||||
|
if (without_job)
|
||||||
|
return curbuf;
|
||||||
|
|
||||||
/* System dependent: setup the vterm and maybe start the job in it. */
|
/* System dependent: setup the vterm and maybe start the job in it. */
|
||||||
if (argvar->v_type == VAR_STRING
|
if (argvar->v_type == VAR_STRING
|
||||||
&& argvar->vval.v_string != NULL
|
&& argvar->vval.v_string != NULL
|
||||||
@@ -492,20 +522,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buf_T *buf = curbuf;
|
term_close_buffer(curbuf, old_curbuf);
|
||||||
|
|
||||||
free_terminal(curbuf);
|
|
||||||
if (old_curbuf != NULL)
|
|
||||||
{
|
|
||||||
--curbuf->b_nwindows;
|
|
||||||
curbuf = old_curbuf;
|
|
||||||
curwin->w_buffer = curbuf;
|
|
||||||
++curbuf->b_nwindows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wiping out the buffer will also close the window and call
|
|
||||||
* free_terminal(). */
|
|
||||||
do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return newbuf;
|
return newbuf;
|
||||||
@@ -597,7 +614,7 @@ ex_terminal(exarg_T *eap)
|
|||||||
argvar[0].v_type = VAR_STRING;
|
argvar[0].v_type = VAR_STRING;
|
||||||
argvar[0].vval.v_string = cmd;
|
argvar[0].vval.v_string = cmd;
|
||||||
argvar[1].v_type = VAR_UNKNOWN;
|
argvar[1].v_type = VAR_UNKNOWN;
|
||||||
term_start(argvar, &opt, eap->forceit);
|
term_start(argvar, &opt, FALSE, eap->forceit);
|
||||||
vim_free(tofree);
|
vim_free(tofree);
|
||||||
vim_free(opt.jo_eof_chars);
|
vim_free(opt.jo_eof_chars);
|
||||||
}
|
}
|
||||||
@@ -1035,6 +1052,36 @@ equal_celattr(cellattr_T *a, cellattr_T *b)
|
|||||||
&& a->bg.blue == b->bg.blue;
|
&& a->bg.blue == b->bg.blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an empty scrollback line to "term". When "lnum" is not zero, add the
|
||||||
|
* line at this position. Otherwise at the end.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_empty_scrollback(term_T *term, cellattr_T *fill_attr, int lnum)
|
||||||
|
{
|
||||||
|
if (ga_grow(&term->tl_scrollback, 1) == OK)
|
||||||
|
{
|
||||||
|
sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
|
||||||
|
+ term->tl_scrollback.ga_len;
|
||||||
|
|
||||||
|
if (lnum > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < term->tl_scrollback.ga_len - lnum; ++i)
|
||||||
|
{
|
||||||
|
*line = *(line - 1);
|
||||||
|
--line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line->sb_cols = 0;
|
||||||
|
line->sb_cells = NULL;
|
||||||
|
line->sb_fill_attr = *fill_attr;
|
||||||
|
++term->tl_scrollback.ga_len;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the current lines of the terminal to scrollback and to the buffer.
|
* Add the current lines of the terminal to scrollback and to the buffer.
|
||||||
@@ -1079,18 +1126,8 @@ move_terminal_to_buffer(term_T *term)
|
|||||||
{
|
{
|
||||||
/* Line was skipped, add an empty line. */
|
/* Line was skipped, add an empty line. */
|
||||||
--lines_skipped;
|
--lines_skipped;
|
||||||
if (ga_grow(&term->tl_scrollback, 1) == OK)
|
if (add_empty_scrollback(term, &fill_attr, 0) == OK)
|
||||||
{
|
|
||||||
sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
|
|
||||||
+ term->tl_scrollback.ga_len;
|
|
||||||
|
|
||||||
line->sb_cols = 0;
|
|
||||||
line->sb_cells = NULL;
|
|
||||||
line->sb_fill_attr = fill_attr;
|
|
||||||
++term->tl_scrollback.ga_len;
|
|
||||||
|
|
||||||
add_scrollback_line_to_buffer(term, (char_u *)"", 0);
|
add_scrollback_line_to_buffer(term, (char_u *)"", 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
@@ -1849,10 +1886,10 @@ color2index(VTermColor *color, int fg, int *boldp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the attributes of a vterm cell into an attribute index.
|
* Convert Vterm attributes to highlight flags.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
|
vtermAttr2hl(VTermScreenCellAttrs cellattrs)
|
||||||
{
|
{
|
||||||
int attr = 0;
|
int attr = 0;
|
||||||
|
|
||||||
@@ -1866,6 +1903,35 @@ cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
|
|||||||
attr |= HL_STRIKETHROUGH;
|
attr |= HL_STRIKETHROUGH;
|
||||||
if (cellattrs.reverse)
|
if (cellattrs.reverse)
|
||||||
attr |= HL_INVERSE;
|
attr |= HL_INVERSE;
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store Vterm attributes in "cell" from highlight flags.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hl2vtermAttr(int attr, cellattr_T *cell)
|
||||||
|
{
|
||||||
|
vim_memset(&cell->attrs, 0, sizeof(VTermScreenCellAttrs));
|
||||||
|
if (attr & HL_BOLD)
|
||||||
|
cell->attrs.bold = 1;
|
||||||
|
if (attr & HL_UNDERLINE)
|
||||||
|
cell->attrs.underline = 1;
|
||||||
|
if (attr & HL_ITALIC)
|
||||||
|
cell->attrs.italic = 1;
|
||||||
|
if (attr & HL_STRIKETHROUGH)
|
||||||
|
cell->attrs.strike = 1;
|
||||||
|
if (attr & HL_INVERSE)
|
||||||
|
cell->attrs.reverse = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the attributes of a vterm cell into an attribute index.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
|
||||||
|
{
|
||||||
|
int attr = vtermAttr2hl(cellattrs);
|
||||||
|
|
||||||
#ifdef FEAT_GUI
|
#ifdef FEAT_GUI
|
||||||
if (gui.in_use)
|
if (gui.in_use)
|
||||||
@@ -2785,6 +2851,730 @@ term_get_buf(typval_T *argvars)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
same_color(VTermColor *a, VTermColor *b)
|
||||||
|
{
|
||||||
|
return a->red == b->red
|
||||||
|
&& a->green == b->green
|
||||||
|
&& a->blue == b->blue
|
||||||
|
&& a->ansi_index == b->ansi_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_term_color(FILE *fd, VTermColor *color)
|
||||||
|
{
|
||||||
|
fprintf(fd, "%02x%02x%02x%d",
|
||||||
|
(int)color->red, (int)color->green, (int)color->blue,
|
||||||
|
(int)color->ansi_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "term_dumpwrite(buf, filename, max-height, max-width)" function
|
||||||
|
*
|
||||||
|
* Each screen cell in full is:
|
||||||
|
* |{characters}+{attributes}#{fg-color}{color-idx}#{bg-color}{color-idx}
|
||||||
|
* {characters} is a space for an empty cell
|
||||||
|
* For a double-width character "+" is changed to "*" and the next cell is
|
||||||
|
* skipped.
|
||||||
|
* {attributes} is the decimal value of HL_BOLD + HL_UNDERLINE, etc.
|
||||||
|
* when "&" use the same as the previous cell.
|
||||||
|
* {fg-color} is hex RGB, when "&" use the same as the previous cell.
|
||||||
|
* {bg-color} is hex RGB, when "&" use the same as the previous cell.
|
||||||
|
* {color-idx} is a number from 0 to 255
|
||||||
|
*
|
||||||
|
* Screen cell with same width, attributes and color as the previous one:
|
||||||
|
* |{characters}
|
||||||
|
*
|
||||||
|
* To use the color of the previous cell, use "&" instead of {color}-{idx}.
|
||||||
|
*
|
||||||
|
* Repeating the previous screen cell:
|
||||||
|
* @{count}
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
|
||||||
|
{
|
||||||
|
buf_T *buf = term_get_buf(argvars);
|
||||||
|
term_T *term;
|
||||||
|
char_u *fname;
|
||||||
|
int max_height = 99999;
|
||||||
|
int max_width = 99999;
|
||||||
|
stat_T st;
|
||||||
|
FILE *fd;
|
||||||
|
VTermPos pos;
|
||||||
|
VTermScreen *screen;
|
||||||
|
VTermScreenCell prev_cell;
|
||||||
|
|
||||||
|
if (check_restricted() || check_secure())
|
||||||
|
return;
|
||||||
|
if (buf == NULL)
|
||||||
|
return;
|
||||||
|
term = buf->b_term;
|
||||||
|
|
||||||
|
fname = get_tv_string_chk(&argvars[1]);
|
||||||
|
if (fname == NULL)
|
||||||
|
return;
|
||||||
|
if (mch_stat((char *)fname, &st) >= 0)
|
||||||
|
{
|
||||||
|
EMSG2(_("E953: File exists: %s"), fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||||
|
{
|
||||||
|
max_height = get_tv_number(&argvars[2]);
|
||||||
|
if (argvars[3].v_type != VAR_UNKNOWN)
|
||||||
|
max_width = get_tv_number(&argvars[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*fname == NUL || (fd = mch_fopen((char *)fname, WRITEBIN)) == NULL)
|
||||||
|
{
|
||||||
|
EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vim_memset(&prev_cell, 0, sizeof(prev_cell));
|
||||||
|
|
||||||
|
screen = vterm_obtain_screen(term->tl_vterm);
|
||||||
|
for (pos.row = 0; pos.row < max_height && pos.row < term->tl_rows;
|
||||||
|
++pos.row)
|
||||||
|
{
|
||||||
|
int repeat = 0;
|
||||||
|
|
||||||
|
for (pos.col = 0; pos.col < max_width && pos.col < term->tl_cols;
|
||||||
|
++pos.col)
|
||||||
|
{
|
||||||
|
VTermScreenCell cell;
|
||||||
|
int same_attr;
|
||||||
|
int same_chars = TRUE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (vterm_screen_get_cell(screen, pos, &cell) == 0)
|
||||||
|
vim_memset(&cell, 0, sizeof(cell));
|
||||||
|
|
||||||
|
for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
|
||||||
|
{
|
||||||
|
if (cell.chars[i] != prev_cell.chars[i])
|
||||||
|
same_chars = FALSE;
|
||||||
|
if (cell.chars[i] == NUL || prev_cell.chars[i] == NUL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
same_attr = vtermAttr2hl(cell.attrs)
|
||||||
|
== vtermAttr2hl(prev_cell.attrs)
|
||||||
|
&& same_color(&cell.fg, &prev_cell.fg)
|
||||||
|
&& same_color(&cell.bg, &prev_cell.bg);
|
||||||
|
if (same_chars && cell.width == prev_cell.width && same_attr)
|
||||||
|
{
|
||||||
|
++repeat;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (repeat > 0)
|
||||||
|
{
|
||||||
|
fprintf(fd, "@%d", repeat);
|
||||||
|
repeat = 0;
|
||||||
|
}
|
||||||
|
fputs("|", fd);
|
||||||
|
|
||||||
|
if (cell.chars[0] == NUL)
|
||||||
|
fputs(" ", fd);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char_u charbuf[10];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
for (i = 0; i < VTERM_MAX_CHARS_PER_CELL
|
||||||
|
&& cell.chars[i] != NUL; ++i)
|
||||||
|
{
|
||||||
|
len = utf_char2bytes(cell.chars[0], charbuf);
|
||||||
|
fwrite(charbuf, len, 1, fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When only the characters differ we don't write anything, the
|
||||||
|
* following "|", "@" or NL will indicate using the same
|
||||||
|
* attributes. */
|
||||||
|
if (cell.width != prev_cell.width || !same_attr)
|
||||||
|
{
|
||||||
|
if (cell.width == 2)
|
||||||
|
{
|
||||||
|
fputs("*", fd);
|
||||||
|
++pos.col;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs("+", fd);
|
||||||
|
|
||||||
|
if (same_attr)
|
||||||
|
{
|
||||||
|
fputs("&", fd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(fd, "%d", vtermAttr2hl(cell.attrs));
|
||||||
|
if (same_color(&cell.fg, &prev_cell.fg))
|
||||||
|
fputs("&", fd);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fputs("#", fd);
|
||||||
|
dump_term_color(fd, &cell.fg);
|
||||||
|
}
|
||||||
|
if (same_color(&cell.bg, &prev_cell.bg))
|
||||||
|
fputs("&", fd);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fputs("#", fd);
|
||||||
|
dump_term_color(fd, &cell.bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_cell = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (repeat > 0)
|
||||||
|
fprintf(fd, "@%d", repeat);
|
||||||
|
fputs("\n", fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when a dump is corrupted. Put a breakpoint here when debugging.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
dump_is_corrupt(garray_T *gap)
|
||||||
|
{
|
||||||
|
ga_concat(gap, (char_u *)"CORRUPT");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
append_cell(garray_T *gap, cellattr_T *cell)
|
||||||
|
{
|
||||||
|
if (ga_grow(gap, 1) == OK)
|
||||||
|
{
|
||||||
|
*(((cellattr_T *)gap->ga_data) + gap->ga_len) = *cell;
|
||||||
|
++gap->ga_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the dump file from "fd" and append lines to the current buffer.
|
||||||
|
* Return the cell width of the longest line.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
read_dump_file(FILE *fd)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
garray_T ga_text;
|
||||||
|
garray_T ga_cell;
|
||||||
|
char_u *prev_char = NULL;
|
||||||
|
int attr = 0;
|
||||||
|
cellattr_T cell;
|
||||||
|
term_T *term = curbuf->b_term;
|
||||||
|
int max_cells = 0;
|
||||||
|
|
||||||
|
ga_init2(&ga_text, 1, 90);
|
||||||
|
ga_init2(&ga_cell, sizeof(cellattr_T), 90);
|
||||||
|
vim_memset(&cell, 0, sizeof(cell));
|
||||||
|
|
||||||
|
c = fgetc(fd);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (c == EOF)
|
||||||
|
break;
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
/* End of a line: append it to the buffer. */
|
||||||
|
if (ga_text.ga_data == NULL)
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
if (ga_grow(&term->tl_scrollback, 1) == OK)
|
||||||
|
{
|
||||||
|
sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
|
||||||
|
+ term->tl_scrollback.ga_len;
|
||||||
|
|
||||||
|
if (max_cells < ga_cell.ga_len)
|
||||||
|
max_cells = ga_cell.ga_len;
|
||||||
|
line->sb_cols = ga_cell.ga_len;
|
||||||
|
line->sb_cells = ga_cell.ga_data;
|
||||||
|
line->sb_fill_attr = term->tl_default_color;
|
||||||
|
++term->tl_scrollback.ga_len;
|
||||||
|
ga_init(&ga_cell);
|
||||||
|
|
||||||
|
ga_append(&ga_text, NUL);
|
||||||
|
ml_append(curbuf->b_ml.ml_line_count, ga_text.ga_data,
|
||||||
|
ga_text.ga_len, FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ga_clear(&ga_cell);
|
||||||
|
ga_text.ga_len = 0;
|
||||||
|
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
else if (c == '|')
|
||||||
|
{
|
||||||
|
int prev_len = ga_text.ga_len;
|
||||||
|
|
||||||
|
/* normal character(s) followed by "+", "*", "|", "@" or NL */
|
||||||
|
c = fgetc(fd);
|
||||||
|
if (c != EOF)
|
||||||
|
ga_append(&ga_text, c);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
c = fgetc(fd);
|
||||||
|
if (c == '+' || c == '*' || c == '|' || c == '@'
|
||||||
|
|| c == EOF || c == '\n')
|
||||||
|
break;
|
||||||
|
ga_append(&ga_text, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save the character for repeating it */
|
||||||
|
vim_free(prev_char);
|
||||||
|
if (ga_text.ga_data != NULL)
|
||||||
|
prev_char = vim_strnsave(((char_u *)ga_text.ga_data) + prev_len,
|
||||||
|
ga_text.ga_len - prev_len);
|
||||||
|
|
||||||
|
if (c == '@' || c == '|' || c == '\n')
|
||||||
|
{
|
||||||
|
/* use all attributes from previous cell */
|
||||||
|
}
|
||||||
|
else if (c == '+' || c == '*')
|
||||||
|
{
|
||||||
|
int is_bg;
|
||||||
|
|
||||||
|
cell.width = c == '+' ? 1 : 2;
|
||||||
|
|
||||||
|
c = fgetc(fd);
|
||||||
|
if (c == '&')
|
||||||
|
{
|
||||||
|
/* use same attr as previous cell */
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
else if (isdigit(c))
|
||||||
|
{
|
||||||
|
/* get the decimal attribute */
|
||||||
|
attr = 0;
|
||||||
|
while (isdigit(c))
|
||||||
|
{
|
||||||
|
attr = attr * 10 + (c - '0');
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
hl2vtermAttr(attr, &cell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
|
||||||
|
/* is_bg == 0: fg, is_bg == 1: bg */
|
||||||
|
for (is_bg = 0; is_bg <= 1; ++is_bg)
|
||||||
|
{
|
||||||
|
if (c == '&')
|
||||||
|
{
|
||||||
|
/* use same color as previous cell */
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
else if (c == '#')
|
||||||
|
{
|
||||||
|
int red, green, blue, index = 0;
|
||||||
|
|
||||||
|
c = fgetc(fd);
|
||||||
|
red = hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
red = (red << 4) + hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
green = hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
green = (green << 4) + hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
blue = hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
blue = (blue << 4) + hex2nr(c);
|
||||||
|
c = fgetc(fd);
|
||||||
|
if (!isdigit(c))
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
while (isdigit(c))
|
||||||
|
{
|
||||||
|
index = index * 10 + (c - '0');
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_bg)
|
||||||
|
{
|
||||||
|
cell.bg.red = red;
|
||||||
|
cell.bg.green = green;
|
||||||
|
cell.bg.blue = blue;
|
||||||
|
cell.bg.ansi_index = index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.fg.red = red;
|
||||||
|
cell.fg.green = green;
|
||||||
|
cell.fg.blue = blue;
|
||||||
|
cell.fg.ansi_index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
|
||||||
|
append_cell(&ga_cell, &cell);
|
||||||
|
}
|
||||||
|
else if (c == '@')
|
||||||
|
{
|
||||||
|
if (prev_char == NULL)
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* repeat previous character, get the count */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
c = fgetc(fd);
|
||||||
|
if (!isdigit(c))
|
||||||
|
break;
|
||||||
|
count = count * 10 + (c - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count-- > 0)
|
||||||
|
{
|
||||||
|
ga_concat(&ga_text, prev_char);
|
||||||
|
append_cell(&ga_cell, &cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
c = fgetc(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ga_text.ga_len > 0)
|
||||||
|
{
|
||||||
|
/* trailing characters after last NL */
|
||||||
|
dump_is_corrupt(&ga_text);
|
||||||
|
ga_append(&ga_text, NUL);
|
||||||
|
ml_append(curbuf->b_ml.ml_line_count, ga_text.ga_data,
|
||||||
|
ga_text.ga_len, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ga_clear(&ga_text);
|
||||||
|
vim_free(prev_char);
|
||||||
|
|
||||||
|
return max_cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Common for "term_dumpdiff()" and "term_dumpload()".
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
term_load_dump(typval_T *argvars, typval_T *rettv, int do_diff)
|
||||||
|
{
|
||||||
|
jobopt_T opt;
|
||||||
|
buf_T *buf;
|
||||||
|
char_u buf1[NUMBUFLEN];
|
||||||
|
char_u buf2[NUMBUFLEN];
|
||||||
|
char_u *fname1;
|
||||||
|
char_u *fname2;
|
||||||
|
FILE *fd1;
|
||||||
|
FILE *fd2;
|
||||||
|
char_u *textline = NULL;
|
||||||
|
|
||||||
|
/* First open the files. If this fails bail out. */
|
||||||
|
fname1 = get_tv_string_buf_chk(&argvars[0], buf1);
|
||||||
|
if (do_diff)
|
||||||
|
fname2 = get_tv_string_buf_chk(&argvars[1], buf2);
|
||||||
|
if (fname1 == NULL || (do_diff && fname2 == NULL))
|
||||||
|
{
|
||||||
|
EMSG(_(e_invarg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd1 = mch_fopen((char *)fname1, READBIN);
|
||||||
|
if (fd1 == NULL)
|
||||||
|
{
|
||||||
|
EMSG2(_(e_notread), fname1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (do_diff)
|
||||||
|
{
|
||||||
|
fd2 = mch_fopen((char *)fname2, READBIN);
|
||||||
|
if (fd2 == NULL)
|
||||||
|
{
|
||||||
|
fclose(fd1);
|
||||||
|
EMSG2(_(e_notread), fname2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_job_options(&opt);
|
||||||
|
/* TODO: use the {options} argument */
|
||||||
|
|
||||||
|
/* TODO: use the file name arguments for the buffer name */
|
||||||
|
opt.jo_term_name = (char_u *)"dump diff";
|
||||||
|
|
||||||
|
buf = term_start(&argvars[0], &opt, TRUE, FALSE);
|
||||||
|
if (buf != NULL && buf->b_term != NULL)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
linenr_T bot_lnum;
|
||||||
|
linenr_T lnum;
|
||||||
|
term_T *term = buf->b_term;
|
||||||
|
int width;
|
||||||
|
int width2;
|
||||||
|
|
||||||
|
rettv->vval.v_number = buf->b_fnum;
|
||||||
|
|
||||||
|
/* read the files, fill the buffer with the diff */
|
||||||
|
width = read_dump_file(fd1);
|
||||||
|
|
||||||
|
/* Delete the empty line that was in the empty buffer. */
|
||||||
|
ml_delete(1, FALSE);
|
||||||
|
|
||||||
|
/* For term_dumpload() we are done here. */
|
||||||
|
if (!do_diff)
|
||||||
|
goto theend;
|
||||||
|
|
||||||
|
term->tl_top_diff_rows = curbuf->b_ml.ml_line_count;
|
||||||
|
|
||||||
|
textline = alloc(width + 1);
|
||||||
|
if (textline == NULL)
|
||||||
|
goto theend;
|
||||||
|
for (i = 0; i < width; ++i)
|
||||||
|
textline[i] = '=';
|
||||||
|
textline[width] = NUL;
|
||||||
|
if (add_empty_scrollback(term, &term->tl_default_color, 0) == OK)
|
||||||
|
ml_append(curbuf->b_ml.ml_line_count, textline, 0, FALSE);
|
||||||
|
if (add_empty_scrollback(term, &term->tl_default_color, 0) == OK)
|
||||||
|
ml_append(curbuf->b_ml.ml_line_count, textline, 0, FALSE);
|
||||||
|
|
||||||
|
bot_lnum = curbuf->b_ml.ml_line_count;
|
||||||
|
width2 = read_dump_file(fd2);
|
||||||
|
if (width2 > width)
|
||||||
|
{
|
||||||
|
vim_free(textline);
|
||||||
|
textline = alloc(width2 + 1);
|
||||||
|
if (textline == NULL)
|
||||||
|
goto theend;
|
||||||
|
width = width2;
|
||||||
|
textline[width] = NUL;
|
||||||
|
}
|
||||||
|
term->tl_bot_diff_rows = curbuf->b_ml.ml_line_count - bot_lnum;
|
||||||
|
|
||||||
|
for (lnum = 1; lnum <= term->tl_top_diff_rows; ++lnum)
|
||||||
|
{
|
||||||
|
if (lnum + bot_lnum > curbuf->b_ml.ml_line_count)
|
||||||
|
{
|
||||||
|
/* bottom part has fewer rows, fill with "-" */
|
||||||
|
for (i = 0; i < width; ++i)
|
||||||
|
textline[i] = '-';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char_u *line1;
|
||||||
|
char_u *line2;
|
||||||
|
char_u *p1;
|
||||||
|
char_u *p2;
|
||||||
|
int col;
|
||||||
|
sb_line_T *sb_line = (sb_line_T *)term->tl_scrollback.ga_data;
|
||||||
|
cellattr_T *cellattr1 = (sb_line + lnum - 1)->sb_cells;
|
||||||
|
cellattr_T *cellattr2 = (sb_line + lnum + bot_lnum - 1)
|
||||||
|
->sb_cells;
|
||||||
|
|
||||||
|
/* Make a copy, getting the second line will invalidate it. */
|
||||||
|
line1 = vim_strsave(ml_get(lnum));
|
||||||
|
if (line1 == NULL)
|
||||||
|
break;
|
||||||
|
p1 = line1;
|
||||||
|
|
||||||
|
line2 = ml_get(lnum + bot_lnum);
|
||||||
|
p2 = line2;
|
||||||
|
for (col = 0; col < width && *p1 != NUL && *p2 != NUL; ++col)
|
||||||
|
{
|
||||||
|
int len1 = utfc_ptr2len(p1);
|
||||||
|
int len2 = utfc_ptr2len(p2);
|
||||||
|
|
||||||
|
textline[col] = ' ';
|
||||||
|
if (len1 != len2 || STRNCMP(p1, p2, len1) != 0)
|
||||||
|
textline[col] = 'X';
|
||||||
|
else if (cellattr1 != NULL && cellattr2 != NULL)
|
||||||
|
{
|
||||||
|
if ((cellattr1 + col)->width
|
||||||
|
!= (cellattr2 + col)->width)
|
||||||
|
textline[col] = 'w';
|
||||||
|
else if (!same_color(&(cellattr1 + col)->fg,
|
||||||
|
&(cellattr2 + col)->fg))
|
||||||
|
textline[col] = 'f';
|
||||||
|
else if (!same_color(&(cellattr1 + col)->bg,
|
||||||
|
&(cellattr2 + col)->bg))
|
||||||
|
textline[col] = 'b';
|
||||||
|
else if (vtermAttr2hl((cellattr1 + col)->attrs)
|
||||||
|
!= vtermAttr2hl(((cellattr2 + col)->attrs)))
|
||||||
|
textline[col] = 'a';
|
||||||
|
}
|
||||||
|
p1 += len1;
|
||||||
|
p2 += len2;
|
||||||
|
/* TODO: handle different width */
|
||||||
|
}
|
||||||
|
vim_free(line1);
|
||||||
|
|
||||||
|
while (col < width)
|
||||||
|
{
|
||||||
|
if (*p1 == NUL && *p2 == NUL)
|
||||||
|
textline[col] = '?';
|
||||||
|
else if (*p1 == NUL)
|
||||||
|
{
|
||||||
|
textline[col] = '+';
|
||||||
|
p2 += utfc_ptr2len(p2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textline[col] = '-';
|
||||||
|
p1 += utfc_ptr2len(p1);
|
||||||
|
}
|
||||||
|
++col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (add_empty_scrollback(term, &term->tl_default_color,
|
||||||
|
term->tl_top_diff_rows) == OK)
|
||||||
|
ml_append(term->tl_top_diff_rows + lnum, textline, 0, FALSE);
|
||||||
|
++bot_lnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lnum + bot_lnum <= curbuf->b_ml.ml_line_count)
|
||||||
|
{
|
||||||
|
/* bottom part has more rows, fill with "+" */
|
||||||
|
for (i = 0; i < width; ++i)
|
||||||
|
textline[i] = '+';
|
||||||
|
if (add_empty_scrollback(term, &term->tl_default_color,
|
||||||
|
term->tl_top_diff_rows) == OK)
|
||||||
|
ml_append(term->tl_top_diff_rows + lnum, textline, 0, FALSE);
|
||||||
|
++lnum;
|
||||||
|
++bot_lnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
term->tl_cols = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
theend:
|
||||||
|
vim_free(textline);
|
||||||
|
fclose(fd1);
|
||||||
|
if (do_diff)
|
||||||
|
fclose(fd2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the current buffer shows the output of term_dumpdiff(), swap the top and
|
||||||
|
* bottom files.
|
||||||
|
* Return FAIL when this is not possible.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
term_swap_diff()
|
||||||
|
{
|
||||||
|
term_T *term = curbuf->b_term;
|
||||||
|
linenr_T line_count;
|
||||||
|
linenr_T top_rows;
|
||||||
|
linenr_T bot_rows;
|
||||||
|
linenr_T bot_start;
|
||||||
|
linenr_T lnum;
|
||||||
|
char_u *p;
|
||||||
|
sb_line_T *sb_line;
|
||||||
|
|
||||||
|
if (term == NULL
|
||||||
|
|| !term_is_finished(curbuf)
|
||||||
|
|| term->tl_top_diff_rows == 0
|
||||||
|
|| term->tl_scrollback.ga_len == 0)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
line_count = curbuf->b_ml.ml_line_count;
|
||||||
|
top_rows = term->tl_top_diff_rows;
|
||||||
|
bot_rows = term->tl_bot_diff_rows;
|
||||||
|
bot_start = line_count - bot_rows;
|
||||||
|
sb_line = (sb_line_T *)term->tl_scrollback.ga_data;
|
||||||
|
|
||||||
|
/* move lines from top to above the bottom part */
|
||||||
|
for (lnum = 1; lnum <= top_rows; ++lnum)
|
||||||
|
{
|
||||||
|
p = vim_strsave(ml_get(1));
|
||||||
|
if (p == NULL)
|
||||||
|
return OK;
|
||||||
|
ml_append(bot_start, p, 0, FALSE);
|
||||||
|
ml_delete(1, FALSE);
|
||||||
|
vim_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move lines from bottom to the top */
|
||||||
|
for (lnum = 1; lnum <= bot_rows; ++lnum)
|
||||||
|
{
|
||||||
|
p = vim_strsave(ml_get(bot_start + lnum));
|
||||||
|
if (p == NULL)
|
||||||
|
return OK;
|
||||||
|
ml_delete(bot_start + lnum, FALSE);
|
||||||
|
ml_append(lnum - 1, p, 0, FALSE);
|
||||||
|
vim_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top_rows == bot_rows)
|
||||||
|
{
|
||||||
|
/* rows counts are equal, can swap cell properties */
|
||||||
|
for (lnum = 0; lnum < top_rows; ++lnum)
|
||||||
|
{
|
||||||
|
sb_line_T temp;
|
||||||
|
|
||||||
|
temp = *(sb_line + lnum);
|
||||||
|
*(sb_line + lnum) = *(sb_line + bot_start + lnum);
|
||||||
|
*(sb_line + bot_start + lnum) = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t size = sizeof(sb_line_T) * term->tl_scrollback.ga_len;
|
||||||
|
sb_line_T *temp = (sb_line_T *)alloc((int)size);
|
||||||
|
|
||||||
|
/* need to copy cell properties into temp memory */
|
||||||
|
if (temp != NULL)
|
||||||
|
{
|
||||||
|
mch_memmove(temp, term->tl_scrollback.ga_data, size);
|
||||||
|
mch_memmove(term->tl_scrollback.ga_data,
|
||||||
|
temp + bot_start,
|
||||||
|
sizeof(sb_line_T) * bot_rows);
|
||||||
|
mch_memmove((sb_line_T *)term->tl_scrollback.ga_data + bot_rows,
|
||||||
|
temp + top_rows,
|
||||||
|
sizeof(sb_line_T) * (line_count - top_rows - bot_rows));
|
||||||
|
mch_memmove((sb_line_T *)term->tl_scrollback.ga_data
|
||||||
|
+ line_count - top_rows,
|
||||||
|
temp,
|
||||||
|
sizeof(sb_line_T) * top_rows);
|
||||||
|
vim_free(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
term->tl_top_diff_rows = bot_rows;
|
||||||
|
term->tl_bot_diff_rows = top_rows;
|
||||||
|
|
||||||
|
update_screen(NOT_VALID);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "term_dumpdiff(filename, filename, options)" function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
f_term_dumpdiff(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
term_load_dump(argvars, rettv, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "term_dumpload(filename, options)" function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
f_term_dumpload(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
term_load_dump(argvars, rettv, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "term_getaltscreen(buf)" function
|
* "term_getaltscreen(buf)" function
|
||||||
*/
|
*/
|
||||||
@@ -3230,7 +4020,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
|
|||||||
|
|
||||||
if (opt.jo_vertical)
|
if (opt.jo_vertical)
|
||||||
cmdmod.split = WSP_VERT;
|
cmdmod.split = WSP_VERT;
|
||||||
buf = term_start(&argvars[0], &opt, FALSE);
|
buf = term_start(&argvars[0], &opt, FALSE, FALSE);
|
||||||
|
|
||||||
if (buf != NULL && buf->b_term != NULL)
|
if (buf != NULL && buf->b_term != NULL)
|
||||||
rettv->vval.v_number = buf->b_fnum;
|
rettv->vval.v_number = buf->b_fnum;
|
||||||
|
@@ -25,6 +25,41 @@ func Test_assert_equal()
|
|||||||
call remove(v:errors, 0)
|
call remove(v:errors, 0)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_assert_equalfile()
|
||||||
|
call assert_equalfile('abcabc', 'xyzxyz')
|
||||||
|
call assert_match("E485: Can't read file abcabc", v:errors[0])
|
||||||
|
call remove(v:errors, 0)
|
||||||
|
|
||||||
|
let goodtext = ["one", "two", "three"]
|
||||||
|
call writefile(goodtext, 'Xone')
|
||||||
|
call assert_equalfile('Xone', 'xyzxyz')
|
||||||
|
call assert_match("E485: Can't read file xyzxyz", v:errors[0])
|
||||||
|
call remove(v:errors, 0)
|
||||||
|
|
||||||
|
call writefile(goodtext, 'Xtwo')
|
||||||
|
call assert_equalfile('Xone', 'Xtwo')
|
||||||
|
|
||||||
|
call writefile([goodtext[0]], 'Xone')
|
||||||
|
call assert_equalfile('Xone', 'Xtwo')
|
||||||
|
call assert_match("first file is shorter", v:errors[0])
|
||||||
|
call remove(v:errors, 0)
|
||||||
|
|
||||||
|
call writefile(goodtext, 'Xone')
|
||||||
|
call writefile([goodtext[0]], 'Xtwo')
|
||||||
|
call assert_equalfile('Xone', 'Xtwo')
|
||||||
|
call assert_match("second file is shorter", v:errors[0])
|
||||||
|
call remove(v:errors, 0)
|
||||||
|
|
||||||
|
call writefile(['1234X89'], 'Xone')
|
||||||
|
call writefile(['1234Y89'], 'Xtwo')
|
||||||
|
call assert_equalfile('Xone', 'Xtwo')
|
||||||
|
call assert_match("difference at byte 4", v:errors[0])
|
||||||
|
call remove(v:errors, 0)
|
||||||
|
|
||||||
|
call delete('Xone')
|
||||||
|
call delete('Xtwo')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_assert_notequal()
|
func Test_assert_notequal()
|
||||||
let n = 4
|
let n = 4
|
||||||
call assert_notequal('foo', n)
|
call assert_notequal('foo', n)
|
||||||
|
@@ -771,6 +771,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1523,
|
||||||
/**/
|
/**/
|
||||||
1522,
|
1522,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user