diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index cd38e90e98..989bd25fb6 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -830,6 +830,11 @@ Also see |`=|. Note: these are typed literally, they are not special keys! is replaced with the word under the cursor (like |star|) is replaced with the WORD under the cursor (see |WORD|) + is replaced with the word under the cursor, including more + to form a C expression. E.g., when the cursor is on "arg" + of "ptr->arg" then the result is "ptr->arg"; when the + cursor is on "]" of "list[idx]" then the result is + "list[idx]". This is used for |v:beval_text|. is replaced with the path name under the cursor (like what |gf| uses) When executing autocommands, is replaced with the file name diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 92074cd006..a63a68723d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.0. Last change: 2017 Aug 13 +*eval.txt* For Vim version 8.0. Last change: 2017 Sep 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1449,7 +1449,7 @@ v:beval_text The text under or after the mouse pointer. Usually a word as but a dot and "->" before the position is included. When on a ']' the text before it is used, including the matching '[' and word before it. When on a Visual area within one line the - highlighted text is used. + highlighted text is used. Also see ||. Only valid while evaluating the 'balloonexpr' option. *v:beval_winnr* *beval_winnr-variable* @@ -3301,7 +3301,7 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* When {ic} is given and it's |TRUE| then case is ignored. When {comp} is a string then the number of not overlapping - occurences of {expr} is returned. + occurrences of {expr} is returned. *cscope_connection()* @@ -3467,7 +3467,7 @@ escape({string}, {chars}) *escape()* :echo escape('c:\program files\vim', ' \') < results in: > c:\\program\ files\\vim -< Also see |shellescape()|. +< Also see |shellescape()| and |fnameescape()|. *eval()* eval({string}) Evaluate {string} and return the result. Especially useful to @@ -3887,7 +3887,7 @@ float2nr({expr}) *float2nr()* When the value of {expr} is out of range for a |Number| the result is truncated to 0x7fffffff or -0x7fffffff (or when 64-bit Number support is enabled, 0x7fffffffffffffff or - -0x7fffffffffffffff. NaN results in -0x80000000 (or when + -0x7fffffffffffffff). NaN results in -0x80000000 (or when 64-bit Number support is enabled, -0x8000000000000000). Examples: > echo float2nr(3.95) @@ -4657,12 +4657,12 @@ getqflist([{what}]) *getqflist()* If "nr" is not present then the current quickfix list is used. If both "nr" and a non-zero "id" are specified, then the list specified by "id" is used. - To get the number of lists in the quickfix stack, set 'nr' to - '$' in {what}. The 'nr' value in the returned dictionary + To get the number of lists in the quickfix stack, set "nr" to + "$" in {what}. The "nr" value in the returned dictionary contains the quickfix stack size. - When 'text' is specified, all the other items are ignored. The - returned dictionary contains the entry 'items' with the list - of entries. + When "lines" is specified, all the other items except "efm" + are ignored. The returned dictionary contains the entry + "items" with the list of entries. In case of error processing {what}, an empty dictionary is returned. @@ -6967,6 +6967,7 @@ setline({lnum}, {text}) *setline()* :for [n, l] in [[5, 'aaa'], [6, 'bbb'], [7, 'ccc']] : call setline(n, l) :endfor + < Note: The '[ and '] marks are not set. setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* @@ -7164,16 +7165,17 @@ setreg({regname}, {value} [, {options}]) :call setreg('a', "1\n2\n3", 'b5') < This example shows using the functions to save and restore a - register (note: you may not reliably restore register value - without using the third argument to |getreg()| as without it - newlines are represented as newlines AND Nul bytes are - represented as newlines as well, see |NL-used-for-Nul|). > + register: > :let var_a = getreg('a', 1, 1) :let var_amode = getregtype('a') .... :call setreg('a', var_a, var_amode) +< Note: you may not reliably restore register value + without using the third argument to |getreg()| as without it + newlines are represented as newlines AND Nul bytes are + represented as newlines as well, see |NL-used-for-Nul|. -< You can also change the type of a register by appending + You can also change the type of a register by appending nothing: > :call setreg('a', '', 'al') @@ -8145,7 +8147,7 @@ term_start({cmd}, {options}) *term_start()* are supported: all timeout options "stoponexit" - "out_cb", "err_cb" + "callback", "out_cb", "err_cb" "exit_cb", "close_cb" "in_io", "in_top", "in_bot", "in_name", "in_buf" "out_io", "out_name", "out_buf", "out_modifiable", "out_msg" @@ -8165,6 +8167,7 @@ term_start({cmd}, {options}) *term_start()* "curwin" use the current window, do not split the window; fails if the current buffer cannot be |abandon|ed + "hidden" do not open a window "term_finish" What to do when the job is finished: "close": close any windows "open": open window if needed @@ -8562,7 +8565,7 @@ win_getid([{win} [, {tab}]]) *win_getid()* Get the |window-ID| for the specified window. When {win} is missing use the current window. With {win} this is the window number. The top window has - number 1. + number 1. Use `win_getid(winnr())` for the current window. Without {tab} use the current tab, otherwise the tab with number {tab}. The first tab has number one. Return zero if the window cannot be found. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 983fb2db4d..f6bcde2377 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1415,6 +1415,8 @@ The valid escape sequences are The starting line of the command range. ** The final line of the command range. + ** + The number of items in the command range: 0, 1 or 2 ** Any count supplied (as described for the '-range' and '-count' attributes). diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index e555451b50..753f461265 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -44,6 +44,18 @@ From inside Vim an easy way to run a command and handle the output is with the The 'errorformat' option should be set to match the error messages from your compiler (see |errorformat| below). + *quickfix-ID* +Each quickfix list has a unique identifier called the quickfix ID and this +number will not change within a Vim session. The getqflist() function can be +used to get the identifier assigned to a list. + + *quickfix-ID* +Each quickfix list has a unique identifier called the quickfix ID and this +number will not change within a Vim session. The getqflist() function can be +used to get the identifier assigned to a list. There is also a quickfix list +number which may change whenever more than ten lists are added to a quickfix +stack. + *location-list* *E776* A location list is a window-local quickfix list. You get one after commands like `:lvimgrep`, `:lgrep`, `:lhelpgrep`, `:lmake`, etc., which create a diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 7be6927a0b..62182f5d0f 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 29 +*terminal.txt* For Vim version 8.0. Last change: 2017 Sep 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -30,11 +30,11 @@ This feature is for running a terminal emulator in a Vim window. A job can be started connected to the terminal emulator. For example, to run a shell: > :term bash -Or to run a debugger: > - :term gdb vim +Or to run build command: > + :term make myprogram The job runs asynchronously from Vim, the window will be updated to show -output from the job, also while editing in any other window. +output from the job, also while editing in another window. Typing ~ @@ -109,7 +109,8 @@ Syntax ~ If [range] is given the specified lines are used as input for the job. It will not be possible to type - keys in the terminal window. + keys in the terminal window. For MS-Windows see the + ++eof argument below. Two comma separated numbers are used as "rows,cols". E.g. `:24,80gdb` opens a terminal with 24 rows and 80 @@ -133,14 +134,15 @@ Syntax ~ height. ++cols={width} Use {width} for the terminal window width. - ++eof={text} when using [range], text to send after - the last line was written. The default - is to send CTRL-D. A CR is appended. + ++eof={text} when using [range]: text to send after + the last line was written. Cannot + contain white space. A CR is + appended. For MS-Windows the default + is to send CTRL-D. E.g. for a shell use "++eof=exit" and for Python "++eof=exit()". Special codes can be used like with `:map`, e.g. "" for CTRL-Z. - {only on MS-Windows} If you want to use more options use the |term_start()| function. @@ -303,33 +305,104 @@ term_scrape() inspect terminal screen 3. Debugging *terminal-debug* The Terminal debugging plugin can be used to debug a program with gdb and view -the source code in a Vim window. +the source code in a Vim window. Since this is completely contained inside +Vim this also works remotely over an ssh connection. + + +Starting ~ Load the plugin with this command: > packadd termdebug - +< *:Termdebug* To start debugging use `:TermDebug` folowed by the command name, for example: > :TermDebug vim This opens two windows: -- A terminal window in which "gdb vim" is executed. Here you can directly - interact with gdb. -- A terminal window for the executed program. When "run" is used in gdb the - program I/O will happen in this window, so that it does not interfere with - controlling gdb. -The current window is used to show the source code. When gdb jumps to a -source file location this window will display the code, if possible. Values -of variables can be inspected, breakpoints set and cleared, etc. +gdb window A terminal window in which "gdb vim" is executed. Here you + can directly interact with gdb. The buffer name is "!gdb". +program window A terminal window for the executed program. When "run" is + used in gdb the program I/O will happen in this window, so + that it does not interfere with controlling gdb. The buffer + name is "gdb program". -When the debugger ends the two opened windows are closed. +The current window is used to show the source code. When gdb pauses the +source file location will be displayed, if possible. A sign is used to +highlight the current position (using highlight group debugPC). + +If the buffer in the current window is modified, another window will be opened +to display the current gdb position. + +Focus the terminal of the executed program to interact with it. This works +the same as any command running in a terminal window. + +When the debugger ends, typically by typing "quit" in the gdb window, the two +opened windows are closed. + + +Stepping through code ~ + +Put focus on the gdb window to type commands there. Some common ones are: +- CTRL-C interrupt the program +- next execute the current line and stop at the next line +- step execute the current line and stop at the next statement, entering + functions +- finish execute until leaving the current function +- where show the stack +- frame N go to the Nth stack frame +- continue continue execution + +In the window showing the source code some commands can used to control gdb: + :Break set a breakpoint at the current line; a sign will be displayed + :Delete delete a breakpoint at the current line + :Step execute the gdb "step" command + :Over execute the gdb "next" command (:Next is a Vim command) + :Finish execute the gdb "finish" command + :Continue execute the gdb "continue" command + + +Inspecting variables ~ + + :Evaluate evaluate the expression under the cursor + K same + :Evaluate {expr} evaluate {expr} + :'<,'>Evaluate evaluate the Visually selected text + +This is similar to using "print" in the gdb window. + + +Other commands ~ + + :Gdb jump to the gdb window + :Program jump to the window with the running program + + +Communication ~ + +There is another, hidden, buffer, which is used for Vim to communicate with +gdb. The buffer name is "gdb communication". Do not delete this buffer, it +will break the debugger. Customizing ~ -g:debugger The debugger command. Default "gdb". +To change the name of the gdb command, set the "termdebugger" variable before +invoking `:Termdebug`: > + let termdebugger = "mygdb" +Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI +interface. +The color of the signs can be adjusted with these highlight groups: +- debugPC the current position +- debugBreakpoint a breakpoint + +The defaults are, when 'background' is "light": + hi debugPC term=reverse ctermbg=lightblue guibg=lightblue + hi debugBreakpoint term=reverse ctermbg=red guibg=red + +When 'background' is "dark": + hi debugPC term=reverse ctermbg=darkblue guibg=darkblue + hi debugBreakpoint term=reverse ctermbg=red guibg=red -TODO vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index f69990b52b..c5e4ac8529 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -20,18 +20,26 @@ command -nargs=* -complete=file Termdebug call s:StartDebug() " Name of the gdb command, defaults to "gdb". -if !exists('debugger') - let debugger = 'gdb' +if !exists('termdebugger') + let termdebugger = 'gdb' endif " Sign used to highlight the line where the program has stopped. +" There can be only one. sign define debugPC linehl=debugPC -if &background == 'light' - hi debugPC term=reverse ctermbg=lightblue guibg=lightblue -else - hi debugPC term=reverse ctermbg=darkblue guibg=darkblue -endif let s:pc_id = 12 +let s:break_id = 13 + +" Sign used to indicate a breakpoint. +" Can be used multiple times. +sign define debugBreakpoint text=>> texthl=debugBreakpoint + +if &background == 'light' + hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue +else + hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue +endif +hi default debugBreakpoint term=reverse ctermbg=red guibg=red func s:StartDebug(cmd) let s:startwin = win_getid(winnr()) @@ -46,6 +54,7 @@ func s:StartDebug(cmd) return endif let pty = job_info(term_getjob(s:ptybuf))['tty_out'] + let s:ptywin = win_getid(winnr()) " Create a hidden terminal window to communicate with gdb let s:commbuf = term_start('NONE', { @@ -61,7 +70,7 @@ func s:StartDebug(cmd) let commpty = job_info(term_getjob(s:commbuf))['tty_out'] " Open a terminal window to run the debugger. - let cmd = [g:debugger, '-tty', pty, a:cmd] + let cmd = [g:termdebugger, '-tty', pty, a:cmd] echomsg 'executing "' . join(cmd) . '"' let gdbbuf = term_start(cmd, { \ 'exit_cb': function('s:EndDebug'), @@ -73,15 +82,30 @@ func s:StartDebug(cmd) exe 'bwipe! ' . s:commbuf return endif + let s:gdbwin = win_getid(winnr()) " Connect gdb to the communication pty, using the GDB/MI interface call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") + + " Install debugger commands in the text window. + call win_gotoid(s:startwin) + call s:InstallCommands() + call win_gotoid(s:gdbwin) + + let s:breakpoints = {} endfunc func s:EndDebug(job, status) exe 'bwipe! ' . s:ptybuf exe 'bwipe! ' . s:commbuf - call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn) + + let curwinid = win_getid(winnr()) + + call win_gotoid(s:startwin) + let &signcolumn = s:startsigncolumn + call s:DeleteCommands() + + call win_gotoid(curwinid) endfunc " Handle a message received from gdb on the GDB/MI interface. @@ -95,34 +119,175 @@ func s:CommOutput(chan, msg) endif if msg != '' if msg =~ '^\*\(stopped\|running\)' - let wid = win_getid(winnr()) - - if win_gotoid(s:startwin) - if msg =~ '^\*stopped' - " TODO: proper parsing - let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '') - let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') - if lnum =~ '^[0-9]*$' - if expand('%:h') != fname - if &modified - " TODO: find existing window - exe 'split ' . fnameescape(fname) - let s:startwin = win_getid(winnr()) - else - exe 'edit ' . fnameescape(fname) - endif - endif - exe lnum - exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) - setlocal signcolumn=yes - endif - else - exe 'sign unplace ' . s:pc_id - endif - - call win_gotoid(wid) - endif + call s:HandleCursor(msg) + elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' + call s:HandleNewBreakpoint(msg) + elseif msg =~ '^=breakpoint-deleted,' + call s:HandleBreakpointDelete(msg) + elseif msg =~ '^\^done,value=' + call s:HandleEvaluate(msg) + elseif msg =~ '^\^error,msg=' + call s:HandleError(msg) endif endif endfor endfunc + +" Install commands in the current window to control the debugger. +func s:InstallCommands() + command Break call s:SetBreakpoint() + command Delete call s:DeleteBreakpoint() + command Step call s:SendCommand('-exec-step') + command Over call s:SendCommand('-exec-next') + command Finish call s:SendCommand('-exec-finish') + command Continue call s:SendCommand('-exec-continue') + command -range -nargs=* Evaluate call s:Evaluate(, ) + command Gdb call win_gotoid(s:gdbwin) + command Program call win_gotoid(s:ptywin) + + " TODO: can the K mapping be restored? + nnoremap K :Evaluate +endfunc + +" Delete installed debugger commands in the current window. +func s:DeleteCommands() + delcommand Break + delcommand Delete + delcommand Step + delcommand Over + delcommand Finish + delcommand Continue + delcommand Evaluate + delcommand Gdb + delcommand Program + + nunmap K + sign undefine debugPC + sign undefine debugBreakpoint + exe 'sign unplace ' . s:pc_id + for key in keys(s:breakpoints) + exe 'sign unplace ' . (s:break_id + key) + endfor + unlet s:breakpoints +endfunc + +" :Break - Set a breakpoint at the cursor position. +func s:SetBreakpoint() + call term_sendkeys(s:commbuf, '-break-insert --source ' + \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") +endfunc + +" :Delete - Delete a breakpoint at the cursor position. +func s:DeleteBreakpoint() + let fname = fnameescape(expand('%:p')) + let lnum = line('.') + for [key, val] in items(s:breakpoints) + if val['fname'] == fname && val['lnum'] == lnum + call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") + " Assume this always wors, the reply is simply "^done". + exe 'sign unplace ' . (s:break_id + key) + unlet s:breakpoints[key] + break + endif + endfor +endfunc + +" :Next, :Continue, etc - send a command to gdb +func s:SendCommand(cmd) + call term_sendkeys(s:commbuf, a:cmd . "\r") +endfunc + +" :Evaluate - evaluate what is under the cursor +func s:Evaluate(range, arg) + if a:arg != '' + let expr = a:arg + elseif a:range == 2 + let pos = getcurpos() + let reg = getreg('v', 1, 1) + let regt = getregtype('v') + normal! gv"vy + let expr = @v + call setpos('.', pos) + call setreg('v', reg, regt) + else + let expr = expand('') + endif + call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r") + let s:evalexpr = expr +endfunc + +" Handle the result of data-evaluate-expression +func s:HandleEvaluate(msg) + echomsg '"' . s:evalexpr . '": ' . substitute(a:msg, '.*value="\(.*\)"', '\1', '') +endfunc + +" Handle an error. +func s:HandleError(msg) + echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') +endfunc + +" Handle stopping and running message from gdb. +" Will update the sign that shows the current position. +func s:HandleCursor(msg) + let wid = win_getid(winnr()) + + if win_gotoid(s:startwin) + if a:msg =~ '^\*stopped' + let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') + if lnum =~ '^[0-9]*$' + if expand('%:h') != fname + if &modified + " TODO: find existing window + exe 'split ' . fnameescape(fname) + let s:startwin = win_getid(winnr()) + else + exe 'edit ' . fnameescape(fname) + endif + endif + exe lnum + exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) + setlocal signcolumn=yes + endif + else + exe 'sign unplace ' . s:pc_id + endif + + call win_gotoid(wid) + endif +endfunc + +" Handle setting a breakpoint +" Will update the sign that shows the breakpoint +func s:HandleNewBreakpoint(msg) + let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 + if nr == 0 + return + endif + + if has_key(s:breakpoints, nr) + let entry = s:breakpoints[nr] + else + let entry = {} + let s:breakpoints[nr] = entry + endif + + let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') + + exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname) + + let entry['fname'] = fname + let entry['lnum'] = lnum +endfunc + +" Handle deleting a breakpoint +" Will remove the sign that shows the breakpoint +func s:HandleBreakpointDelete(msg) + let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 + if nr == 0 + return + endif + exe 'sign unplace ' . (s:break_id + nr) + unlet s:breakpoints[nr] +endfunc diff --git a/src/Makefile b/src/Makefile index b1ef20e835..0654688236 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2282,6 +2282,7 @@ test_arglist \ test_taglist \ test_tcl \ test_terminal \ + test_terminal_fail \ test_textobjects \ test_timers \ test_true_false \ diff --git a/src/channel.c b/src/channel.c index 7a9a223b7a..6208d4dc27 100644 --- a/src/channel.c +++ b/src/channel.c @@ -2951,14 +2951,27 @@ channel_close_in(channel_T *channel) ch_close_part(channel, PART_IN); } + static void +remove_from_writeque(writeq_T *wq, writeq_T *entry) +{ + ga_clear(&entry->wq_ga); + wq->wq_next = entry->wq_next; + if (wq->wq_next == NULL) + wq->wq_prev = NULL; + else + wq->wq_next->wq_prev = NULL; + vim_free(entry); +} + /* * Clear the read buffer on "channel"/"part". */ static void channel_clear_one(channel_T *channel, ch_part_T part) { - jsonq_T *json_head = &channel->ch_part[part].ch_json_head; - cbq_T *cb_head = &channel->ch_part[part].ch_cb_head; + chanpart_T *ch_part = &channel->ch_part[part]; + jsonq_T *json_head = &ch_part->ch_json_head; + cbq_T *cb_head = &ch_part->ch_cb_head; while (channel_peek(channel, part) != NULL) vim_free(channel_get(channel, part)); @@ -2978,10 +2991,13 @@ channel_clear_one(channel_T *channel, ch_part_T part) remove_json_node(json_head, json_head->jq_next); } - free_callback(channel->ch_part[part].ch_callback, - channel->ch_part[part].ch_partial); - channel->ch_part[part].ch_callback = NULL; - channel->ch_part[part].ch_partial = NULL; + free_callback(ch_part->ch_callback, ch_part->ch_partial); + ch_part->ch_callback = NULL; + ch_part->ch_partial = NULL; + + while (ch_part->ch_writeque.wq_next != NULL) + remove_from_writeque(&ch_part->ch_writeque, + ch_part->ch_writeque.wq_next); } /* @@ -2996,7 +3012,7 @@ channel_clear(channel_T *channel) channel_clear_one(channel, PART_SOCK); channel_clear_one(channel, PART_OUT); channel_clear_one(channel, PART_ERR); - /* there is no callback or queue for PART_IN */ + channel_clear_one(channel, PART_IN); free_callback(channel->ch_callback, channel->ch_partial); channel->ch_callback = NULL; channel->ch_partial = NULL; @@ -3744,12 +3760,7 @@ channel_send( if (entry != NULL) { /* Remove the entry from the write queue. */ - ga_clear(&entry->wq_ga); - wq->wq_next = entry->wq_next; - if (wq->wq_next == NULL) - wq->wq_prev = NULL; - else - wq->wq_next->wq_prev = NULL; + remove_from_writeque(wq, entry); continue; } if (did_use_queue) diff --git a/src/evalfunc.c b/src/evalfunc.c index 0985f28968..908f07ef94 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4833,7 +4833,7 @@ get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) dict_T *d = what_arg->vval.v_dict; if (d != NULL) - get_errorlist_properties(wp, d, rettv->vval.v_dict); + qf_get_properties(wp, d, rettv->vval.v_dict); } else EMSG(_(e_dictreq)); @@ -8659,7 +8659,10 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr) # endif { if (r != NULL) + { EMSG(r); /* sending worked but evaluation failed */ + vim_free(r); + } else EMSG2(_("E241: Unable to send to %s"), server_name); return; @@ -8719,6 +8722,8 @@ f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) argvars[1].v_type = VAR_STRING; argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); argvars[2].v_type = VAR_UNKNOWN; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; remote_common(argvars, rettv, TRUE); vim_free(argvars[1].vval.v_string); # endif diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7fd978d896..7d102c562f 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -6348,7 +6348,8 @@ ex_command(exarg_T *eap) { ++p; end = skiptowhite(p); - if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg, &addr_type_arg) + if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, + &compl_arg, &addr_type_arg) == FAIL) return; p = skipwhite(end); @@ -6389,7 +6390,7 @@ ex_command(exarg_T *eap) } else uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, - addr_type_arg, eap->forceit); + addr_type_arg, eap->forceit); } /* @@ -6609,8 +6610,18 @@ uc_check_code( char_u *p = code + 1; size_t l = len - 2; int quote = 0; - enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_MODS, - ct_REGISTER, ct_LT, ct_NONE } type = ct_NONE; + enum { + ct_ARGS, + ct_BANG, + ct_COUNT, + ct_LINE1, + ct_LINE2, + ct_RANGE, + ct_MODS, + ct_REGISTER, + ct_LT, + ct_NONE + } type = ct_NONE; if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') { @@ -6632,6 +6643,8 @@ uc_check_code( type = ct_LINE1; else if (STRNICMP(p, "line2>", l) == 0) type = ct_LINE2; + else if (STRNICMP(p, "range>", l) == 0) + type = ct_RANGE; else if (STRNICMP(p, "lt>", l) == 0) type = ct_LT; else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) @@ -6733,11 +6746,13 @@ uc_check_code( case ct_LINE1: case ct_LINE2: + case ct_RANGE: case ct_COUNT: { char num_buf[20]; long num = (type == ct_LINE1) ? eap->line1 : (type == ct_LINE2) ? eap->line2 : + (type == ct_RANGE) ? eap->addr_count : (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; size_t num_len; @@ -10662,31 +10677,33 @@ find_cmdline_var(char_u *src, int *usedlen) "%", #define SPEC_PERC 0 "#", -#define SPEC_HASH 1 +#define SPEC_HASH (SPEC_PERC + 1) "", /* cursor word */ -#define SPEC_CWORD 2 +#define SPEC_CWORD (SPEC_HASH + 1) "", /* cursor WORD */ -#define SPEC_CCWORD 3 +#define SPEC_CCWORD (SPEC_CWORD + 1) + "", /* expr under cursor */ +#define SPEC_CEXPR (SPEC_CCWORD + 1) "", /* cursor path name */ -#define SPEC_CFILE 4 +#define SPEC_CFILE (SPEC_CEXPR + 1) "", /* ":so" file name */ -#define SPEC_SFILE 5 +#define SPEC_SFILE (SPEC_CFILE + 1) "", /* ":so" file line number */ -#define SPEC_SLNUM 6 +#define SPEC_SLNUM (SPEC_SFILE + 1) #ifdef FEAT_AUTOCMD "", /* autocommand file name */ -# define SPEC_AFILE 7 +# define SPEC_AFILE (SPEC_SLNUM + 1) "", /* autocommand buffer number */ -# define SPEC_ABUF 8 +# define SPEC_ABUF (SPEC_AFILE + 1) "", /* autocommand match name */ -# define SPEC_AMATCH 9 +# define SPEC_AMATCH (SPEC_ABUF + 1) #endif #ifdef FEAT_CLIENTSERVER "" # ifdef FEAT_AUTOCMD -# define SPEC_CLIENT 10 +# define SPEC_CLIENT (SPEC_AMATCH + 1) # else -# define SPEC_CLIENT 7 +# define SPEC_CLIENT (SPEC_SLNUM + 1) # endif #endif }; @@ -10774,10 +10791,13 @@ eval_vars( /* * word or WORD under cursor */ - if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD) + if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD + || spec_idx == SPEC_CEXPR) { - resultlen = find_ident_under_cursor(&result, spec_idx == SPEC_CWORD ? - (FIND_IDENT|FIND_STRING) : FIND_STRING); + resultlen = find_ident_under_cursor(&result, + spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING) + : spec_idx == SPEC_CEXPR ? (FIND_IDENT | FIND_STRING | FIND_EVAL) + : FIND_STRING); if (resultlen == 0) { *errormsg = (char_u *)""; diff --git a/src/gui_beval.c b/src/gui_beval.c index d1d9fbf85d..f57fcb3507 100644 --- a/src/gui_beval.c +++ b/src/gui_beval.c @@ -1177,23 +1177,15 @@ drawBalloon(BalloonEval *beval) int x_offset = EVAL_OFFSET_X; int y_offset = EVAL_OFFSET_Y; PangoLayout *layout; -# if GTK_CHECK_VERSION(3,22,2) - GdkRectangle rect; - GdkMonitor * const mon = gdk_display_get_monitor_at_window( - gtk_widget_get_display(beval->balloonShell), - gtk_widget_get_window(beval->balloonShell)); - gdk_monitor_get_geometry(mon, &rect); - screen_w = rect.width; - screen_h = rect.height; -# else +# if !GTK_CHECK_VERSION(3,22,2) GdkScreen *screen; screen = gtk_widget_get_screen(beval->target); gtk_window_set_screen(GTK_WINDOW(beval->balloonShell), screen); - screen_w = gdk_screen_get_width(screen); - screen_h = gdk_screen_get_height(screen); # endif + gui_gtk_get_screen_size_of_win(beval->balloonShell, + &screen_w, &screen_h); # if !GTK_CHECK_VERSION(3,0,0) gtk_widget_ensure_style(beval->balloonShell); gtk_widget_ensure_style(beval->balloonLabel); diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 361f16c659..9225375a10 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -4941,6 +4941,29 @@ gui_mch_set_shellsize(int width, int height, gui_mch_update(); } + void +gui_gtk_get_screen_size_of_win(GtkWidget *wid, int *width, int *height) +{ +#if GTK_CHECK_VERSION(3,22,0) + GdkDisplay *dpy = gtk_widget_get_display(wid); + GdkWindow *win = gtk_widget_get_window(wid); + GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); + GdkRectangle geometry; + + gdk_monitor_get_geometry(monitor, &geometry); + *width = geometry.width; + *height = geometry.height; +#else + GdkScreen* screen; + + if (wid != NULL && gtk_widget_has_screen(wid)) + screen = gtk_widget_get_screen(wid); + else + screen = gdk_screen_get_default(); + *width = gdk_screen_get_width(screen); + *height = gdk_screen_get_height(screen); +#endif +} /* * The screen size is used to make sure the initial window doesn't get bigger @@ -4950,30 +4973,11 @@ gui_mch_set_shellsize(int width, int height, void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) { -#if GTK_CHECK_VERSION(3,22,2) - GdkRectangle rect; - GdkMonitor * const mon = gdk_display_get_monitor_at_window( - gtk_widget_get_display(gui.mainwin), - gtk_widget_get_window(gui.mainwin)); - gdk_monitor_get_geometry(mon, &rect); + gui_gtk_get_screen_size_of_win(gui.mainwin, screen_w, screen_h); - *screen_w = rect.width; /* Subtract 'guiheadroom' from the height to allow some room for the * window manager (task list and window title bar). */ - *screen_h = rect.height - p_ghr; -#else - GdkScreen* screen; - - if (gui.mainwin != NULL && gtk_widget_has_screen(gui.mainwin)) - screen = gtk_widget_get_screen(gui.mainwin); - else - screen = gdk_screen_get_default(); - - *screen_w = gdk_screen_get_width(screen); - /* Subtract 'guiheadroom' from the height to allow some room for the - * window manager (task list and window title bar). */ - *screen_h = gdk_screen_get_height(screen) - p_ghr; -#endif + *screen_h -= p_ghr; /* * FIXME: dirty trick: Because the gui_get_base_height() doesn't include diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c index 275abfc235..15a62fb452 100644 --- a/src/if_xcmdsrv.c +++ b/src/if_xcmdsrv.c @@ -421,6 +421,7 @@ serverSendToVim( { LookupName(dpy, loosename ? loosename : name, /*DELETE=*/TRUE, NULL); + vim_free(loosename); continue; } } diff --git a/src/mbyte.c b/src/mbyte.c index efd02c028d..fb28789306 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -4887,8 +4887,7 @@ im_preedit_window_set_position(void) if (preedit_window == NULL) return; - sw = gdk_screen_get_width(gtk_widget_get_screen(preedit_window)); - sh = gdk_screen_get_height(gtk_widget_get_screen(preedit_window)); + gui_gtk_get_screen_size_of_win(preedit_window, &sw, &sh); #if GTK_CHECK_VERSION(3,0,0) gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y); #else diff --git a/src/normal.c b/src/normal.c index 9a6d4e2e83..627aecbcde 100644 --- a/src/normal.c +++ b/src/normal.c @@ -3355,9 +3355,6 @@ reset_VIsual(void) } } -#if defined(FEAT_BEVAL) -static int find_is_eval_item(char_u *ptr, int *colp, int *nbp, int dir); - /* * Check for a balloon-eval special item to include when searching for an * identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! @@ -3396,7 +3393,6 @@ find_is_eval_item( } return FALSE; } -#endif /* * Find the identifier under or to the right of the cursor. @@ -3446,9 +3442,7 @@ find_ident_at_pos( int prev_class; int prevcol; #endif -#if defined(FEAT_BEVAL) int bn = 0; /* bracket nesting */ -#endif /* * if i == 0: try to find an identifier @@ -3466,11 +3460,9 @@ find_ident_at_pos( { while (ptr[col] != NUL) { -# if defined(FEAT_BEVAL) /* Stop at a ']' to evaluate "a[x]". */ if ((find_type & FIND_EVAL) && ptr[col] == ']') break; -# endif this_class = mb_get_class(ptr + col); if (this_class != 0 && (i == 1 || this_class != 1)) break; @@ -3481,16 +3473,12 @@ find_ident_at_pos( #endif while (ptr[col] != NUL && (i == 0 ? !vim_iswordc(ptr[col]) : VIM_ISWHITE(ptr[col])) -# if defined(FEAT_BEVAL) && (!(find_type & FIND_EVAL) || ptr[col] != ']') -# endif ) ++col; -#if defined(FEAT_BEVAL) /* When starting on a ']' count it, so that we include the '['. */ bn = ptr[col] == ']'; -#endif /* * 2. Back up to start of identifier/string. @@ -3499,11 +3487,9 @@ find_ident_at_pos( if (has_mbyte) { /* Remember class of character under cursor. */ -# if defined(FEAT_BEVAL) if ((find_type & FIND_EVAL) && ptr[col] == ']') this_class = mb_get_class((char_u *)"a"); else -# endif this_class = mb_get_class(ptr + col); while (col > 0 && this_class != 0) { @@ -3513,12 +3499,10 @@ find_ident_at_pos( && (i == 0 || prev_class == 0 || (find_type & FIND_IDENT)) -# if defined(FEAT_BEVAL) && (!(find_type & FIND_EVAL) || prevcol == 0 || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD)) -# endif ) break; col = prevcol; @@ -3540,12 +3524,10 @@ find_ident_at_pos( : (!VIM_ISWHITE(ptr[col - 1]) && (!(find_type & FIND_IDENT) || !vim_iswordc(ptr[col - 1])))) -#if defined(FEAT_BEVAL) || ((find_type & FIND_EVAL) && col > 1 && find_is_eval_item(ptr + col - 1, &col, &bn, BACKWARD)) -#endif )) --col; @@ -3577,10 +3559,8 @@ find_ident_at_pos( /* * 3. Find the end if the identifier/string. */ -#if defined(FEAT_BEVAL) bn = 0; startcol -= col; -#endif col = 0; #ifdef FEAT_MBYTE if (has_mbyte) @@ -3590,11 +3570,9 @@ find_ident_at_pos( while (ptr[col] != NUL && ((i == 0 ? mb_get_class(ptr + col) == this_class : mb_get_class(ptr + col) != 0) -# if defined(FEAT_BEVAL) || ((find_type & FIND_EVAL) && col <= (int)startcol && find_is_eval_item(ptr + col, &col, &bn, FORWARD)) -# endif )) col += (*mb_ptr2len)(ptr + col); } @@ -3602,11 +3580,9 @@ find_ident_at_pos( #endif while ((i == 0 ? vim_iswordc(ptr[col]) : (ptr[col] != NUL && !VIM_ISWHITE(ptr[col]))) -# if defined(FEAT_BEVAL) || ((find_type & FIND_EVAL) && col <= (int)startcol && find_is_eval_item(ptr + col, &col, &bn, FORWARD)) -# endif ) { ++col; diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro index aaf710c172..51546ae9ef 100644 --- a/src/proto/gui_gtk_x11.pro +++ b/src/proto/gui_gtk_x11.pro @@ -25,6 +25,7 @@ int gui_mch_maximized(void); void gui_mch_unmaximize(void); void gui_mch_newfont(void); void gui_mch_set_shellsize(int width, int height, int min_width, int min_height, int base_width, int base_height, int direction); +void gui_gtk_get_screen_size_of_win(GtkWidget *win, int *width, int *height); void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h); void gui_mch_settitle(char_u *title, char_u *icon); void gui_mch_enable_menu(int showit); diff --git a/src/proto/quickfix.pro b/src/proto/quickfix.pro index ad6ad34330..a801edd0e2 100644 --- a/src/proto/quickfix.pro +++ b/src/proto/quickfix.pro @@ -22,7 +22,7 @@ void ex_cnext(exarg_T *eap); void ex_cfile(exarg_T *eap); void ex_vimgrep(exarg_T *eap); int get_errorlist(qf_info_T *qi, win_T *wp, int qf_idx, list_T *list); -int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict); +int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict); int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, dict_T *what); int set_ref_in_quickfix(int copyID); void ex_cbuffer(exarg_T *eap); diff --git a/src/quickfix.c b/src/quickfix.c index ee871fc463..096790317e 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4690,13 +4690,28 @@ qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) return status; } +/* + * Return the quickfix/location list number with the given identifier. + * Returns -1 if list is not found. + */ + static int +qf_id2nr(qf_info_T *qi, int_u qfid) +{ + int qf_idx; + + for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) + if (qi->qf_lists[qf_idx].qf_id == qfid) + return qf_idx; + return -1; +} + /* * Return quickfix/location list details (title) as a * dictionary. 'what' contains the details to return. If 'list_idx' is -1, * then current list is used. Otherwise the specified list is used. */ int -get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) +qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) { qf_info_T *qi = &ql_info; int status = OK; @@ -4752,12 +4767,8 @@ get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) /* For zero, use the current list or the list specifed by 'nr' */ if (di->di_tv.vval.v_number != 0) { - for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) - { - if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) - break; - } - if (qf_idx == qi->qf_listcount) + qf_idx = qf_id2nr(qi, di->di_tv.vval.v_number); + if (qf_idx == -1) return FAIL; /* List not found */ } flags |= QF_GETLIST_ID; @@ -5024,10 +5035,8 @@ qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title) /* Use the quickfix/location list with the specified id */ if (di->di_tv.v_type == VAR_NUMBER) { - for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) - if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) - break; - if (qf_idx == qi->qf_listcount) + qf_idx = qf_id2nr(qi, di->di_tv.vval.v_number); + if (qf_idx == -1) return FAIL; /* List not found */ } else @@ -5062,6 +5071,16 @@ qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title) retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, title_save, action == ' ' ? 'a' : action); + if (action == 'r') + { + /* + * When replacing the quickfix list entries using + * qf_add_entries(), the title is set with a ':' prefix. + * Restore the title with the saved title. + */ + vim_free(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = vim_strsave(title_save); + } vim_free(title_save); } } diff --git a/src/screen.c b/src/screen.c index 0d477d2877..26d064a2d7 100644 --- a/src/screen.c +++ b/src/screen.c @@ -3139,6 +3139,7 @@ win_line( #endif #ifdef FEAT_TERMINAL int get_term_attr = FALSE; + int term_attr = 0; /* background for terminal window */ #endif /* draw_state: items that are drawn in sequence: */ @@ -3256,6 +3257,7 @@ win_line( { extra_check = TRUE; get_term_attr = TRUE; + term_attr = term_get_attr(wp->w_buffer, lnum, -1); } #endif @@ -5056,6 +5058,9 @@ win_line( else if (( # ifdef FEAT_DIFF diff_hlf != (hlf_T)0 || +# endif +# ifdef FEAT_TERMINAL + term_attr != 0 || # endif line_attr != 0 ) && ( @@ -5090,6 +5095,15 @@ win_line( HL_ATTR(HLF_CUL)); } } +# endif +# ifdef FEAT_TERMINAL + if (term_attr != 0) + { + char_attr = term_attr; + if (wp->w_p_cul && lnum == wp->w_cursor.lnum) + char_attr = hl_combine_attr(char_attr, + HL_ATTR(HLF_CUL)); + } # endif } #endif diff --git a/src/syntax.c b/src/syntax.c index aba3507a46..a55daae8de 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -7364,7 +7364,6 @@ do_highlight( int attr; int id; int idx; - struct hl_group *item; struct hl_group item_before; int dodefault = FALSE; int doclear = FALSE; @@ -7380,6 +7379,9 @@ do_highlight( #else # define is_menu_group 0 # define is_tooltip_group 0 +#endif +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + int did_highlight_changed = FALSE; #endif /* @@ -7461,13 +7463,12 @@ do_highlight( } from_id = syn_check_group(from_start, (int)(from_end - from_start)); - item = &HL_TABLE()[from_id - 1]; if (STRNCMP(to_start, "NONE", 4) == 0) to_id = 0; else to_id = syn_check_group(to_start, (int)(to_end - to_start)); - if (from_id > 0 && (!init || item->sg_set == 0)) + if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) { /* * Don't allow a link when there already is some highlighting @@ -7479,19 +7480,19 @@ do_highlight( if (sourcing_name == NULL && !dodefault) EMSG(_("E414: group has settings, highlight link ignored")); } - else if (item->sg_link != to_id + else if (HL_TABLE()[from_id - 1].sg_link != to_id #ifdef FEAT_EVAL - || item->sg_scriptID != current_SID + || HL_TABLE()[from_id - 1].sg_scriptID != current_SID #endif - || item->sg_cleared) + || HL_TABLE()[from_id - 1].sg_cleared) { if (!init) - item->sg_set |= SG_LINK; - item->sg_link = to_id; + HL_TABLE()[from_id - 1].sg_set |= SG_LINK; + HL_TABLE()[from_id - 1].sg_link = to_id; #ifdef FEAT_EVAL - item->sg_scriptID = current_SID; + HL_TABLE()[from_id - 1].sg_scriptID = current_SID; #endif - item->sg_cleared = FALSE; + HL_TABLE()[from_id - 1].sg_cleared = FALSE; redraw_all_later(SOME_VALID); /* Only call highlight_changed() once after multiple changes. */ @@ -7569,8 +7570,9 @@ do_highlight( #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) if (USE_24BIT) highlight_gui_started(); + else #endif - highlight_changed(); + highlight_changed(); redraw_later_clear(); return; } @@ -7585,23 +7587,22 @@ do_highlight( if (id == 0) /* failed (out of memory) */ return; idx = id - 1; /* index is ID minus one */ - item = &HL_TABLE()[idx]; /* Return if "default" was used and the group already has settings. */ if (dodefault && hl_has_settings(idx, TRUE)) return; /* Make a copy so we can check if any attribute actually changed. */ - item_before = *item; + item_before = HL_TABLE()[idx]; - if (STRCMP(item->sg_name_u, "NORMAL") == 0) + if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) is_normal_group = TRUE; #ifdef FEAT_GUI_X11 - else if (STRCMP(item->sg_name_u, "MENU") == 0) + else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0) is_menu_group = TRUE; - else if (STRCMP(item->sg_name_u, "SCROLLBAR") == 0) + else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0) is_scrollbar_group = TRUE; - else if (STRCMP(item->sg_name_u, "TOOLTIP") == 0) + else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0) is_tooltip_group = TRUE; #endif @@ -7610,7 +7611,7 @@ do_highlight( { highlight_clear(idx); if (!doclear) - item->sg_set = 0; + HL_TABLE()[idx].sg_set = 0; } if (!doclear) @@ -7641,10 +7642,10 @@ do_highlight( if (STRCMP(key, "NONE") == 0) { - if (!init || item->sg_set == 0) + if (!init || HL_TABLE()[idx].sg_set == 0) { if (!init) - item->sg_set |= SG_TERM+SG_CTERM+SG_GUI; + HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; highlight_clear(idx); } continue; @@ -7731,31 +7732,31 @@ do_highlight( break; if (*key == 'T') { - if (!init || !(item->sg_set & SG_TERM)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) { if (!init) - item->sg_set |= SG_TERM; - item->sg_term = attr; + HL_TABLE()[idx].sg_set |= SG_TERM; + HL_TABLE()[idx].sg_term = attr; } } else if (*key == 'C') { - if (!init || !(item->sg_set & SG_CTERM)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { if (!init) - item->sg_set |= SG_CTERM; - item->sg_cterm = attr; - item->sg_cterm_bold = FALSE; + HL_TABLE()[idx].sg_set |= SG_CTERM; + HL_TABLE()[idx].sg_cterm = attr; + HL_TABLE()[idx].sg_cterm_bold = FALSE; } } #if defined(FEAT_GUI) || defined(FEAT_EVAL) else { - if (!init || !(item->sg_set & SG_GUI)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) - item->sg_set |= SG_GUI; - item->sg_gui = attr; + HL_TABLE()[idx].sg_set |= SG_GUI; + HL_TABLE()[idx].sg_gui = attr; } } #endif @@ -7764,74 +7765,74 @@ do_highlight( { /* in non-GUI fonts are simply ignored */ #ifdef FEAT_GUI - if (item->sg_font_name != NULL - && STRCMP(item->sg_font_name, arg) == 0) + if (HL_TABLE()[idx].sg_font_name != NULL + && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0) { /* Font name didn't change, ignore. */ } else if (!gui.shell_created) { /* GUI not started yet, always accept the name. */ - vim_free(item->sg_font_name); - item->sg_font_name = vim_strsave(arg); + vim_free(HL_TABLE()[idx].sg_font_name); + HL_TABLE()[idx].sg_font_name = vim_strsave(arg); } else { - GuiFont temp_sg_font = item->sg_font; + GuiFont temp_sg_font = HL_TABLE()[idx].sg_font; # ifdef FEAT_XFONTSET - GuiFontset temp_sg_fontset = item->sg_fontset; + GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset; # endif /* First, save the current font/fontset. * Then try to allocate the font/fontset. - * If the allocation fails, item->sg_font OR + * If the allocation fails, HL_TABLE()[idx].sg_font OR * sg_fontset will be set to NOFONT or NOFONTSET respectively. */ - item->sg_font = NOFONT; + HL_TABLE()[idx].sg_font = NOFONT; # ifdef FEAT_XFONTSET - item->sg_fontset = NOFONTSET; + HL_TABLE()[idx].sg_fontset = NOFONTSET; # endif hl_do_font(idx, arg, is_normal_group, is_menu_group, is_tooltip_group, FALSE); # ifdef FEAT_XFONTSET - if (item->sg_fontset != NOFONTSET) + if (HL_TABLE()[idx].sg_fontset != NOFONTSET) { /* New fontset was accepted. Free the old one, if there * was one. */ gui_mch_free_fontset(temp_sg_fontset); - vim_free(item->sg_font_name); - item->sg_font_name = vim_strsave(arg); + vim_free(HL_TABLE()[idx].sg_font_name); + HL_TABLE()[idx].sg_font_name = vim_strsave(arg); } else - item->sg_fontset = temp_sg_fontset; + HL_TABLE()[idx].sg_fontset = temp_sg_fontset; # endif - if (item->sg_font != NOFONT) + if (HL_TABLE()[idx].sg_font != NOFONT) { /* New font was accepted. Free the old one, if there was * one. */ gui_mch_free_font(temp_sg_font); - vim_free(item->sg_font_name); - item->sg_font_name = vim_strsave(arg); + vim_free(HL_TABLE()[idx].sg_font_name); + HL_TABLE()[idx].sg_font_name = vim_strsave(arg); } else - item->sg_font = temp_sg_font; + HL_TABLE()[idx].sg_font = temp_sg_font; } #endif } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(item->sg_set & SG_CTERM)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { if (!init) - item->sg_set |= SG_CTERM; + HL_TABLE()[idx].sg_set |= SG_CTERM; /* When setting the foreground color, and previously the "bold" * flag was set for a light color, reset it now */ - if (key[5] == 'F' && item->sg_cterm_bold) + if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { - item->sg_cterm &= ~HL_BOLD; - item->sg_cterm_bold = FALSE; + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = FALSE; } if (VIM_ISDIGIT(*arg)) @@ -7888,22 +7889,22 @@ do_highlight( * colors (on some terminals, e.g. "linux") */ if (bold == TRUE) { - item->sg_cterm |= HL_BOLD; - item->sg_cterm_bold = TRUE; + HL_TABLE()[idx].sg_cterm |= HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = TRUE; } else if (bold == FALSE) - item->sg_cterm &= ~HL_BOLD; + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; } /* Add one to the argument, to avoid zero. Zero is used for * "NONE", then "color" is -1. */ if (key[5] == 'F') { - item->sg_cterm_fg = color + 1; + HL_TABLE()[idx].sg_cterm_fg = color + 1; if (is_normal_group) { cterm_normal_fg_color = color + 1; - cterm_normal_fg_bold = (item->sg_cterm & HL_BOLD); + cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); #ifdef FEAT_GUI /* Don't do this if the GUI is used. */ if (!gui.in_use && !gui.starting) @@ -7917,7 +7918,7 @@ do_highlight( } else { - item->sg_cterm_bg = color + 1; + HL_TABLE()[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; @@ -7957,23 +7958,23 @@ do_highlight( else if (STRCMP(key, "GUIFG") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(item->sg_set & SG_GUI)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) - item->sg_set |= SG_GUI; + HL_TABLE()[idx].sg_set |= SG_GUI; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* In GUI guifg colors are only used when recognized */ i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) { - item->sg_gui_fg = i; + HL_TABLE()[idx].sg_gui_fg = i; # endif - vim_free(item->sg_gui_fg_name); + vim_free(HL_TABLE()[idx].sg_gui_fg_name); if (STRCMP(arg, "NONE") != 0) - item->sg_gui_fg_name = vim_strsave(arg); + HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg); else - item->sg_gui_fg_name = NULL; + HL_TABLE()[idx].sg_gui_fg_name = NULL; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) # ifdef FEAT_GUI_X11 if (is_menu_group) @@ -7994,23 +7995,23 @@ do_highlight( else if (STRCMP(key, "GUIBG") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(item->sg_set & SG_GUI)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) - item->sg_set |= SG_GUI; + HL_TABLE()[idx].sg_set |= SG_GUI; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* In GUI guifg colors are only used when recognized */ i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) { - item->sg_gui_bg = i; + HL_TABLE()[idx].sg_gui_bg = i; # endif - vim_free(item->sg_gui_bg_name); + vim_free(HL_TABLE()[idx].sg_gui_bg_name); if (STRCMP(arg, "NONE") != 0) - item->sg_gui_bg_name = vim_strsave(arg); + HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg); else - item->sg_gui_bg_name = NULL; + HL_TABLE()[idx].sg_gui_bg_name = NULL; # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) # ifdef FEAT_GUI_X11 if (is_menu_group) @@ -8031,22 +8032,22 @@ do_highlight( else if (STRCMP(key, "GUISP") == 0) { #if defined(FEAT_GUI) || defined(FEAT_EVAL) - if (!init || !(item->sg_set & SG_GUI)) + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) - item->sg_set |= SG_GUI; + HL_TABLE()[idx].sg_set |= SG_GUI; # ifdef FEAT_GUI i = color_name2handle(arg); if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) { - item->sg_gui_sp = i; + HL_TABLE()[idx].sg_gui_sp = i; # endif - vim_free(item->sg_gui_sp_name); + vim_free(HL_TABLE()[idx].sg_gui_sp_name); if (STRCMP(arg, "NONE") != 0) - item->sg_gui_sp_name = vim_strsave(arg); + HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg); else - item->sg_gui_sp_name = NULL; + HL_TABLE()[idx].sg_gui_sp_name = NULL; # ifdef FEAT_GUI } # endif @@ -8059,7 +8060,7 @@ do_highlight( char_u *tname; if (!init) - item->sg_set |= SG_TERM; + HL_TABLE()[idx].sg_set |= SG_TERM; /* * The "start" and "stop" arguments can be a literal escape @@ -8126,13 +8127,13 @@ do_highlight( p = vim_strsave(buf); if (key[2] == 'A') { - vim_free(item->sg_start); - item->sg_start = p; + vim_free(HL_TABLE()[idx].sg_start); + HL_TABLE()[idx].sg_start = p; } else { - vim_free(item->sg_stop); - item->sg_stop = p; + vim_free(HL_TABLE()[idx].sg_stop); + HL_TABLE()[idx].sg_stop = p; } } else @@ -8141,13 +8142,13 @@ do_highlight( error = TRUE; break; } - item->sg_cleared = FALSE; + HL_TABLE()[idx].sg_cleared = FALSE; /* * When highlighting has been given for a group, don't link it. */ - if (!init || !(item->sg_set & SG_LINK)) - item->sg_link = 0; + if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) + HL_TABLE()[idx].sg_link = 0; /* * Continue with next argument. @@ -8164,10 +8165,10 @@ do_highlight( { if (is_normal_group) { - item->sg_term_attr = 0; - item->sg_cterm_attr = 0; + HL_TABLE()[idx].sg_term_attr = 0; + HL_TABLE()[idx].sg_cterm_attr = 0; #ifdef FEAT_GUI - item->sg_gui_attr = 0; + HL_TABLE()[idx].sg_gui_attr = 0; /* * Need to update all groups, because they might be using "bg" * and/or "fg", which have been changed now. @@ -8175,7 +8176,11 @@ do_highlight( #endif #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) if (USE_24BIT) + { highlight_gui_started(); + did_highlight_changed = TRUE; + redraw_all_later(NOT_VALID); + } #endif } #ifdef FEAT_GUI_X11 @@ -8202,7 +8207,7 @@ do_highlight( else set_hl_attr(idx); #ifdef FEAT_EVAL - item->sg_scriptID = current_SID; + HL_TABLE()[idx].sg_scriptID = current_SID; #endif } @@ -8211,7 +8216,11 @@ do_highlight( /* Only call highlight_changed() once, after a sequence of highlight * commands, and only if an attribute actually changed. */ - if (memcmp(item, &item_before, sizeof(item_before)) != 0) + if (memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0 +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + && !did_highlight_changed +#endif + ) { redraw_all_later(NOT_VALID); need_highlight_changed = TRUE; diff --git a/src/term.c b/src/term.c index ee46c80d62..0a7bf36d29 100644 --- a/src/term.c +++ b/src/term.c @@ -4572,12 +4572,12 @@ check_termcode( /* Detect terminals that set $TERM to something like * "xterm-256colors" but are not fully xterm * compatible. */ -# ifdef MACOS + /* Mac Terminal.app sends 1;95;0 */ if (version == 95 && STRNCMP(tp + extra - 2, "1;95;0c", 7) == 0) is_not_xterm = TRUE; -# endif + /* Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0. * xfce4-terminal sends 1;2802;0. * screen sends 83;40500;0 diff --git a/src/terminal.c b/src/terminal.c index 556c7e2a52..969cfa4362 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -39,6 +39,7 @@ * * TODO: * - patch to use GUI or cterm colors for vterm. Yasuhiro, #2067 + * - patch to add tmap, jakalope (Jacob Askeland) #2073 * - Redirecting output does not work on MS-Windows. * - implement term_setsize() * - add test for giving error for invalid 'termsize' value. @@ -84,6 +85,7 @@ typedef struct { typedef struct sb_line_S { int sb_cols; /* can differ per line */ cellattr_T *sb_cells; /* allocated */ + cellattr_T sb_fill_attr; /* for short line */ } sb_line_T; /* typedef term_T in structs.h */ @@ -126,6 +128,7 @@ struct terminal_S { garray_T tl_scrollback; int tl_scrollback_scrolled; + cellattr_T tl_default_color; VTermPos tl_cursor_pos; int tl_cursor_visible; @@ -393,6 +396,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) vim_snprintf((char *)p, len, "!%s (%d)", cmd, i); if (buflist_findname(p) == NULL) { + vim_free(curbuf->b_ffname); curbuf->b_ffname = p; break; } @@ -552,6 +556,7 @@ ex_terminal(exarg_T *eap) argvar[1].v_type = VAR_UNKNOWN; term_start(argvar, &opt, eap->forceit); vim_free(tofree); + vim_free(opt.jo_eof_chars); } /* @@ -944,6 +949,28 @@ add_scrollback_line_to_buffer(term_T *term, char_u *text, int len) } } + static void +cell2cellattr(const VTermScreenCell *cell, cellattr_T *attr) +{ + attr->width = cell->width; + attr->attrs = cell->attrs; + attr->fg = cell->fg; + attr->bg = cell->bg; +} + + static int +equal_celattr(cellattr_T *a, cellattr_T *b) +{ + /* Comparing the colors should be sufficient. */ + return a->fg.red == b->fg.red + && a->fg.green == b->fg.green + && a->fg.blue == b->fg.blue + && a->bg.red == b->bg.red + && a->bg.green == b->bg.green + && a->bg.blue == b->bg.blue; +} + + /* * Add the current lines of the terminal to scrollback and to the buffer. * Called after the job has ended and when switching to Terminal-Normal mode. @@ -956,21 +983,30 @@ move_terminal_to_buffer(term_T *term) int lines_skipped = 0; VTermPos pos; VTermScreenCell cell; + cellattr_T fill_attr, new_fill_attr; cellattr_T *p; VTermScreen *screen; if (term->tl_vterm == NULL) return; screen = vterm_obtain_screen(term->tl_vterm); + fill_attr = new_fill_attr = term->tl_default_color; + for (pos.row = 0; pos.row < term->tl_rows; ++pos.row) { len = 0; for (pos.col = 0; pos.col < term->tl_cols; ++pos.col) if (vterm_screen_get_cell(screen, pos, &cell) != 0 && cell.chars[0] != NUL) + { len = pos.col + 1; + new_fill_attr = term->tl_default_color; + } + else + /* Assume the last attr is the filler attr. */ + cell2cellattr(&cell, &new_fill_attr); - if (len == 0) + if (len == 0 && equal_celattr(&new_fill_attr, &fill_attr)) ++lines_skipped; else { @@ -985,14 +1021,19 @@ move_terminal_to_buffer(term_T *term) 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); } } - p = (cellattr_T *)alloc((int)sizeof(cellattr_T) * len); - if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK) + if (len == 0) + p = NULL; + else + p = (cellattr_T *)alloc((int)sizeof(cellattr_T) * len); + if ((p != NULL || len == 0) + && ga_grow(&term->tl_scrollback, 1) == OK) { garray_T ga; int width; @@ -1014,10 +1055,7 @@ move_terminal_to_buffer(term_T *term) { width = cell.width; - p[pos.col].width = cell.width; - p[pos.col].attrs = cell.attrs; - p[pos.col].fg = cell.fg; - p[pos.col].bg = cell.bg; + cell2cellattr(&cell, &p[pos.col]); if (ga_grow(&ga, MB_MAXBYTES) == OK) { @@ -1032,6 +1070,8 @@ move_terminal_to_buffer(term_T *term) } line->sb_cols = len; line->sb_cells = p; + line->sb_fill_attr = new_fill_attr; + fill_attr = new_fill_attr; ++term->tl_scrollback.ga_len; if (ga_grow(&ga, 1) == FAIL) @@ -1048,6 +1088,10 @@ move_terminal_to_buffer(term_T *term) } } + /* Obtain the current background color. */ + vterm_state_get_default_colors(vterm_obtain_state(term->tl_vterm), + &term->tl_default_color.fg, &term->tl_default_color.bg); + FOR_ALL_WINDOWS(wp) { if (wp->w_buffer == term->tl_buffer) @@ -2004,11 +2048,14 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) int col; sb_line_T *line; garray_T ga; + cellattr_T fill_attr = term->tl_default_color; /* do not store empty cells at the end */ for (i = 0; i < cols; ++i) if (cells[i].chars[0] != 0) len = i + 1; + else + cell2cellattr(&cells[i], &fill_attr); ga_init2(&ga, 1, 100); if (len > 0) @@ -2025,10 +2072,7 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) for (i = 0; (c = cells[col].chars[i]) > 0 || i == 0; ++i) ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c, (char_u *)ga.ga_data + ga.ga_len); - p[col].width = cells[col].width; - p[col].attrs = cells[col].attrs; - p[col].fg = cells[col].fg; - p[col].bg = cells[col].bg; + cell2cellattr(&cells[col], &p[col]); } } if (ga_grow(&ga, 1) == FAIL) @@ -2044,6 +2088,7 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) + term->tl_scrollback.ga_len; line->sb_cols = len; line->sb_cells = p; + line->sb_fill_attr = fill_attr; ++term->tl_scrollback.ga_len; ++term->tl_scrollback_scrolled; } @@ -2320,6 +2365,7 @@ term_change_in_curbuf(void) /* * Get the screen attribute for a position in the buffer. + * Use a negative "col" to get the filler background color. */ int term_get_attr(buf_T *buf, linenr_T lnum, int col) @@ -2329,11 +2375,15 @@ term_get_attr(buf_T *buf, linenr_T lnum, int col) cellattr_T *cellattr; if (lnum > term->tl_scrollback.ga_len) - return 0; - line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1; - if (col >= line->sb_cols) - return 0; - cellattr = line->sb_cells + col; + cellattr = &term->tl_default_color; + else + { + line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1; + if (col < 0 || col >= line->sb_cols) + cellattr = &line->sb_fill_attr; + else + cellattr = line->sb_cells + col; + } return cell2attr(cellattr->attrs, cellattr->fg, cellattr->bg); } @@ -2346,6 +2396,8 @@ create_vterm(term_T *term, int rows, int cols) VTerm *vterm; VTermScreen *screen; VTermValue value; + VTermColor *fg, *bg; + int fgval, bgval; vterm = vterm_new(rows, cols); term->tl_vterm = vterm; @@ -2356,14 +2408,23 @@ create_vterm(term_T *term, int rows, int cols) /* Vterm uses a default black background. Set it to white when * 'background' is "light". */ + vim_memset(&term->tl_default_color.attrs, 0, sizeof(VTermScreenCellAttrs)); + term->tl_default_color.width = 1; + fg = &term->tl_default_color.fg; + bg = &term->tl_default_color.bg; if (*p_bg == 'l') { - VTermColor fg, bg; - - fg.red = fg.green = fg.blue = 0; - bg.red = bg.green = bg.blue = 255; - vterm_state_set_default_colors(vterm_obtain_state(vterm), &fg, &bg); + fgval = 0; + bgval = 255; } + else + { + fgval = 255; + bgval = 0; + } + fg->red = fg->green = fg->blue = fgval; + bg->red = bg->green = bg->blue = bgval; + vterm_state_set_default_colors(vterm_obtain_state(vterm), fg, bg); /* Required to initialize most things. */ vterm_screen_reset(screen, 1 /* hard */); @@ -3101,6 +3162,7 @@ term_and_job_init( jobopt_T *opt) { WCHAR *cmd_wchar = NULL; + WCHAR *cwd_wchar = NULL; channel_T *channel = NULL; job_T *job = NULL; DWORD error; @@ -3128,6 +3190,8 @@ term_and_job_init( cmd_wchar = enc_to_utf16(cmd, NULL); if (cmd_wchar == NULL) return FAIL; + if (opt->jo_cwd != NULL) + cwd_wchar = enc_to_utf16(opt->jo_cwd, NULL); job = job_alloc(); if (job == NULL) @@ -3154,7 +3218,7 @@ term_and_job_init( WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN, NULL, cmd_wchar, - NULL, + cwd_wchar, NULL, &winpty_err); if (spawn_config == NULL) @@ -3205,6 +3269,7 @@ term_and_job_init( winpty_spawn_config_free(spawn_config); vim_free(cmd_wchar); + vim_free(cwd_wchar); create_vterm(term, term->tl_rows, term->tl_cols); @@ -3228,8 +3293,8 @@ term_and_job_init( failed: if (argvar->v_type == VAR_LIST) vim_free(ga.ga_data); - if (cmd_wchar != NULL) - vim_free(cmd_wchar); + vim_free(cmd_wchar); + vim_free(cwd_wchar); if (spawn_config != NULL) winpty_spawn_config_free(spawn_config); if (channel != NULL) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index ad95b1b6e1..b979c975b7 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -194,6 +194,7 @@ NEW_TESTS = test_arabic.res \ test_system.res \ test_tcl.res \ test_terminal.res \ + test_terminal_fail.res \ test_textobjects.res \ test_undo.res \ test_usercommands.res \ diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim index 2ee550b1c8..89572ac098 100644 --- a/src/testdir/test_clientserver.vim +++ b/src/testdir/test_clientserver.vim @@ -35,7 +35,8 @@ func Test_client_server() endif " Takes a short while for the server to be active. - call WaitFor('serverlist() =~ "' . name . '"') + " When using valgrind it takes much longer. + call WaitFor('serverlist() =~ "' . name . '"', 5000) call assert_match(name, serverlist()) call remote_foreground(name) diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 21d6aa9881..d33723b120 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -389,10 +389,22 @@ func! Test_normal10_expand() call setline(1, ['1', 'ifooar,,cbar']) 2 norm! $ - let a=expand('') - let b=expand('') - call assert_equal('cbar', a) - call assert_equal('ifooar,,cbar', b) + call assert_equal('cbar', expand('')) + call assert_equal('ifooar,,cbar', expand('')) + + call setline(1, ['prx = list[idx];']) + 1 + let expected = ['', 'prx', 'prx', 'prx', + \ 'list', 'list', 'list', 'list', 'list', 'list', 'list', + \ 'idx', 'idx', 'idx', 'idx', + \ 'list[idx]', + \ '];', + \ ] + for i in range(1, 16) + exe 'norm ' . i . '|' + call assert_equal(expected[i], expand(''), 'i == ' . i) + endfor + " clean up bw! endfunc diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 6a8d0e7bf2..671381ce19 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -487,6 +487,19 @@ func s:test_xhelpgrep(cchar) " This wipes out the buffer, make sure that doesn't cause trouble. Xclose + if a:cchar == 'l' + " When a help window is present, running :lhelpgrep should reuse the + " help window and not the current window + new | only + call g:Xsetlist([], 'f') + help index.txt + wincmd w + lhelpgrep quickfix + call assert_equal(1, winnr()) + call assert_notequal([], getloclist(1)) + call assert_equal([], getloclist(2)) + endif + new | only " Search for non existing help string @@ -1684,6 +1697,10 @@ func HistoryTest(cchar) call assert_equal(' error list 1 of 3; 1 ' . common, res[0]) call assert_equal(' error list 2 of 3; 2 ' . common, res[1]) call assert_equal('> error list 3 of 3; 3 ' . common, res[2]) + + call g:Xsetlist([], 'f') + let l = split(execute(a:cchar . 'hist'), "\n") + call assert_equal('No entries', l[0]) endfunc func Test_history() @@ -1862,6 +1879,11 @@ func Xproperty_tests(cchar) let l = g:Xgetlist({'items':1}) call assert_equal(0, len(l.items)) + call g:Xsetlist([], 'r', {'title' : 'TestTitle'}) + call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]}) + call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]}) + call assert_equal('TestTitle', g:Xgetlist({'title' : 1}).title) + " The following used to crash Vim with address sanitizer call g:Xsetlist([], 'f') call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]}) @@ -1904,10 +1926,10 @@ func Xproperty_tests(cchar) call g:Xsetlist([], 'r', l2) let newl1=g:Xgetlist({'nr':1,'all':1}) let newl2=g:Xgetlist({'nr':2,'all':1}) - call assert_equal(':Fruits', newl1.title) + call assert_equal('Fruits', newl1.title) call assert_equal(['Fruits'], newl1.context) call assert_equal('Line20', newl1.items[0].text) - call assert_equal(':Colors', newl2.title) + call assert_equal('Colors', newl2.title) call assert_equal(['Colors'], newl2.context) call assert_equal('Line10', newl2.items[0].text) call g:Xsetlist([], 'f') diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index b35d88093f..9f12625f22 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -104,6 +104,15 @@ func! s:Nasty_exit_cb(job, st) let g:buf = 0 endfunc +func Get_cat_123_cmd() + if has('win32') + return 'cmd /c "cls && color 2 && echo 123"' + else + call writefile(["\[32m123"], 'Xtext') + return "cat Xtext" + endif +endfunc + func Test_terminal_nasty_cb() let cmd = Get_cat_123_cmd() let g:buf = term_start(cmd, {'exit_cb': function('s:Nasty_exit_cb')}) @@ -143,15 +152,6 @@ func Check_123(buf) call assert_equal('123', l) endfunc -func Get_cat_123_cmd() - if has('win32') - return 'cmd /c "cls && color 2 && echo 123"' - else - call writefile(["\[32m123"], 'Xtext') - return "cat Xtext" - endif -endfunc - func Test_terminal_scrape_123() let cmd = Get_cat_123_cmd() let buf = term_start(cmd) @@ -197,7 +197,7 @@ func Test_terminal_scrape_multibyte() let g:line = 1 endif - call WaitFor('term_scrape(g:buf, g:line)[0].chars == "l"') + call WaitFor('len(term_scrape(g:buf, g:line)) >= 7 && term_scrape(g:buf, g:line)[0].chars == "l"') let l = term_scrape(g:buf, g:line) call assert_true(len(l) >= 7) call assert_equal('l', l[0].chars) @@ -393,18 +393,16 @@ func Test_finish_open_close() call assert_equal(2, winnr('$')) call assert_equal(4, winheight(0)) bwipe - endfunc func Test_terminal_cwd() - if !has('unix') + if !executable('pwd') return endif call mkdir('Xdir') let buf = term_start('pwd', {'cwd': 'Xdir'}) - sleep 100m - call term_wait(buf) - call assert_equal(getcwd() . '/Xdir', getline(1)) + call WaitFor('"Xdir" == fnamemodify(getline(1), ":t")') + call assert_equal('Xdir', fnamemodify(getline(1), ":t")) exe buf . 'bwipe' call delete('Xdir', 'rf') @@ -609,18 +607,13 @@ func Test_terminal_redir_file() call term_wait(buf) call WaitFor('len(readfile("Xfile")) > 0') call assert_match('123', readfile('Xfile')[0]) + let g:job = term_getjob(buf) + call WaitFor('job_status(g:job) == "dead"') call delete('Xfile') bwipe endif if has('unix') - let buf = term_start('xyzabc', {'err_io': 'file', 'err_name': 'Xfile'}) - call term_wait(buf) - call WaitFor('len(readfile("Xfile")) > 0') - call assert_match('executing job failed', readfile('Xfile')[0]) - call delete('Xfile') - bwipe - call writefile(['one line'], 'Xfile') let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xfile'}) call term_wait(buf) diff --git a/src/testdir/test_terminal_fail.vim b/src/testdir/test_terminal_fail.vim new file mode 100644 index 0000000000..aad4b98cb5 --- /dev/null +++ b/src/testdir/test_terminal_fail.vim @@ -0,0 +1,21 @@ +" This test is in a separate file, because it usually causes reports for memory +" leaks under valgrind. That is because when fork/exec fails memory is not +" freed. Since the process exists right away it's not a real leak. + +if !has('terminal') + finish +endif + +source shared.vim + +func Test_terminal_redir_fails() + if has('unix') + let buf = term_start('xyzabc', {'err_io': 'file', 'err_name': 'Xfile'}) + call term_wait(buf) + call WaitFor('len(readfile("Xfile")) > 0') + call assert_match('executing job failed', readfile('Xfile')[0]) + call WaitFor('!&modified') + call delete('Xfile') + bwipe + endif +endfunc diff --git a/src/version.c b/src/version.c index 4497552be7..34269eef58 100644 --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,46 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1097, +/**/ + 1096, +/**/ + 1095, +/**/ + 1094, +/**/ + 1093, +/**/ + 1092, +/**/ + 1091, +/**/ + 1090, +/**/ + 1089, +/**/ + 1088, +/**/ + 1087, +/**/ + 1086, +/**/ + 1085, +/**/ + 1084, +/**/ + 1083, +/**/ + 1082, +/**/ + 1081, +/**/ + 1080, +/**/ + 1079, +/**/ + 1078, /**/ 1077, /**/