diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 76949be70a..2ea5c4f93e 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2016 Feb 10 +*change.txt* For Vim version 7.4. Last change: 2016 Mar 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -109,7 +109,9 @@ is an error when 'cpoptions' includes the 'E' flag. *J* J Join [count] lines, with a minimum of two lines. Remove the indent and insert up to two spaces (see - below). + below). Fails when on the last line of the buffer. + If [count] is too big it is reduce to the number of + lines available. *v_J* {Visual}J Join the highlighted lines, with a minimum of two diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index a89bdb7875..36a7c9b688 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 7.4. Last change: 2016 Mar 06 +*channel.txt* For Vim version 7.4. Last change: 2016 Mar 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -44,8 +44,8 @@ There are four main types of jobs: 4. Running a filter, synchronously. Uses pipes. -For when using sockets See |job-start|, |job-may-start| and |channel-open|. -For 2 and 3, one or more jobs using pipes, see |job-start|. +For when using sockets See |job-start|, |job-start-nochannel| and +|channel-open|. For 2 and 3, one or more jobs using pipes, see |job-start|. For 4 use the ":{range}!cmd" command, see |filter|. Over the socket and pipes these protocols are available: @@ -162,7 +162,7 @@ Use |ch_status()| to see if the channel could be opened. the channel uses pipes. When "err-cb" wasn't set the channel callback is used. - TODO: *close-cb* + *close-cb* "close-cb" A function that is called when the channel gets closed, other than by calling ch_close(). It should be defined like this: > func MyCloseHandler(channel) @@ -410,7 +410,6 @@ are: "open" The channel can be used. "closed" The channel was closed. -TODO: To obtain the job associated with a channel: ch_getjob(channel) To read one message from a channel: > @@ -486,15 +485,6 @@ time a line is added to the buffer, the last-but-one line will be send to the job stdin. This allows for editing the last line and sending it when pressing Enter. -TODO: -To run a job and read its output once it is done: > - let job = job_start({command}, {'exit-cb': 'MyHandler'}) - func MyHandler(job, status) - let channel = job_getchannel() - let output = ch_readall(channel) - " parse output - endfunc - ============================================================================== 9. Starting a job without a channel *job-start-nochannel* @@ -504,28 +494,23 @@ To start another process without creating a channel: > This starts {command} in the background, Vim does not wait for it to finish. -TODO: When Vim sees that neither stdin, stdout or stderr are connected, no channel will be created. Often you will want to include redirection in the command to avoid it getting stuck. There are several options you can use, see |job-options|. -TODO: *job-may-start* -To start a job only when connecting to an address does not work use -job_maystart('command', {address}, {options}), For Example: > - let job = job_maystart(command, address, {"waittime": 1000}) - let channel = job_gethandle(job) - -This comes down to: > + *job-start-if-needed* +To start a job only when connecting to an address does not work, do something +like this: > let channel = ch_open(address, {"waittime": 0}) if ch_status(channel) == "fail" let job = job_start(command) let channel = ch_open(address, {"waittime": 1000}) - call job_sethandle(channel) endif -Note that the specified waittime applies to when the job has been started. -This gives the job some time to make the port available. + +Note that the waittime for ch_open() gives the job one second to make the port +available. ============================================================================== 10. Job options *job-options* @@ -560,43 +545,54 @@ See |job_setoptions()| and |ch_setoptions()|. "stoponexit": "" Do not stop the job when Vim exits. The default is "term". -TODO: *job-term* + *job-term* "term": "open" Start a terminal and connect the job stdin/stdout/stderr to it. + NOTE: Not implemented yet! - *job-in-io* -"in-io": "null" disconnect stdin TODO +"channel": {channel} Use an existing channel instead of creating a new one. + The parts of the channel that get used for the new job + will be disconnected from what they were used before. + If the channel was still use by another job this may + cause I/O errors. + Existing callbacks and other settings remain. + + *job-in-io* *in-top* *in-bot* *in-name* *in-buf* +"in-io": "null" disconnect stdin (read from /dev/null) "in-io": "pipe" stdin is connected to the channel (default) -"in-io": "file" stdin reads from a file TODO +"in-io": "file" stdin reads from a file "in-io": "buffer" stdin reads from a buffer "in-top": number when using "buffer": first line to send (default: 1) "in-bot": number when using "buffer": last line to send (default: last) "in-name": "/path/file" the name of the file or buffer to read from -"in-buf": number the number of the buffer to read from TODO +"in-buf": number the number of the buffer to read from - *job-out-io* -"out-io": "null" disconnect stdout TODO + *job-out-io* *out-name* *out-buf* +"out-io": "null" disconnect stdout (goes to /dev/null) "out-io": "pipe" stdout is connected to the channel (default) -"out-io": "file" stdout writes to a file TODO +"out-io": "file" stdout writes to a file "out-io": "buffer" stdout appends to a buffer "out-name": "/path/file" the name of the file or buffer to write to -"out-buf": number the number of the buffer to write to TODO +"out-buf": number the number of the buffer to write to - *job-err-io* + *job-err-io* *err-name* *err-buf* "err-io": "out" stderr messages to go to stdout -"err-io": "null" disconnect stderr TODO +"err-io": "null" disconnect stderr (goes to /dev/null) "err-io": "pipe" stderr is connected to the channel (default) -"err-io": "file" stderr writes to a file TODO -"err-io": "buffer" stderr appends to a buffer TODO +"err-io": "file" stderr writes to a file +"err-io": "buffer" stderr appends to a buffer "err-name": "/path/file" the name of the file or buffer to write to -"err-buf": number the number of the buffer to write to TODO +"err-buf": number the number of the buffer to write to + + +Writing to a buffer ~ When the out-io or err-io mode is "buffer" and there is a callback, the text is appended to the buffer before invoking the callback. When a buffer is used both for input and output, the output lines are put above the last line, since the last line is what is written to the channel -input. Otherwise lines are appened below the last line. +input. Otherwise lines are appended below the last line. When using JS or JSON mode with "buffer", only messages with zero or negative ID will be added to the buffer, after decoding + encoding. Messages with a @@ -616,6 +612,14 @@ line and the window is scrolled up to show the cursor if needed. Undo is synced for every added line. + +Writing to a file ~ + +The file is created with permissions 600 (read-write for the user, not +accessible for others). Use |setfperm()| to change this. + +If the file already exists it is truncated. + ============================================================================== 11. Controlling a job *job-control* diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d3b20f4a90..ef45ce212a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Mar 07 +*eval.txt* For Vim version 7.4. Last change: 2016 Mar 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4468,6 +4468,9 @@ items({dict}) *items()* job_getchannel({job}) *job_getchannel()* Get the channel handle that {job} is using. + To check if the job has no channel: > + if string(job_getchannel()) == 'channel fail' +< {only available when compiled with the |+job| feature} job_setoptions({job}, {options}) *job_setoptions()* @@ -4524,6 +4527,8 @@ job_status({job}) *job_status()* *E916* If an exit callback was set with the "exit-cb" option and the job is now detected to be "dead" the callback will be invoked. + For more information see |job_info()|. + {only available when compiled with the |+job| feature} job_stop({job} [, {how}]) *job_stop()* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bc4b5a9395..8bcda6a59c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 7.4. Last change: 2016 Feb 24 +*options.txt* For Vim version 7.4. Last change: 2016 Mar 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -7909,14 +7909,16 @@ A jump table for the options with a short description can be found at |Q_op|. {not in Vi} {only in the GTK+ 2 and MacVim GUIs} Controls the size of toolbar icons. The possible values are: - tiny Use tiny toolbar icons. - small Use small toolbar icons (default). - medium Use medium-sized toolbar icons. - large Use large toolbar icons. + tiny Use tiny icons. + small Use small icons (default). + medium Use medium-sized icons. + large Use large icons. + huge Use even larger icons. + giant Use very big icons. The exact dimensions in pixels of the various icon sizes depend on - the current theme. Common dimensions are large=32x32, medium=24x24, - small=20x20 and tiny=16x16. In MacVim, both tiny and small equal - 24x24, whereas medium and large equal 32x32. + the current theme. Common dimensions are giant=48x48, huge=32x32, + large=24x24, medium=24x24, small=20x20 and tiny=16x16. In MacVim, both + tiny and small equal 24x24, whereas medium and large equal 32x32. If 'toolbariconsize' is empty, the global default size as determined by user preferences or the current theme is used. diff --git a/runtime/doc/tags b/runtime/doc/tags index e93f21f840..60f634bd45 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5278,6 +5278,7 @@ charity uganda.txt /*charity* charset mbyte.txt /*charset* charset-conversion mbyte.txt /*charset-conversion* chill.vim syntax.txt /*chill.vim* +chmod eval.txt /*chmod* cindent() eval.txt /*cindent()* cinkeys-format indent.txt /*cinkeys-format* cino-# indent.txt /*cino-#* @@ -5697,7 +5698,9 @@ end intro.txt /*end* end-of-file pattern.txt /*end-of-file* enlightened-terminal syntax.txt /*enlightened-terminal* erlang.vim syntax.txt /*erlang.vim* +err-buf channel.txt /*err-buf* err-cb channel.txt /*err-cb* +err-name channel.txt /*err-name* err-timeout channel.txt /*err-timeout* errmsg-variable eval.txt /*errmsg-variable* error-file-format quickfix.txt /*error-file-format* @@ -6821,6 +6824,10 @@ improved-viminfo version5.txt /*improved-viminfo* improvements-5 version5.txt /*improvements-5* improvements-6 version6.txt /*improvements-6* improvements-7 version7.txt /*improvements-7* +in-bot channel.txt /*in-bot* +in-buf channel.txt /*in-buf* +in-name channel.txt /*in-name* +in-top channel.txt /*in-top* inactive-buffer windows.txt /*inactive-buffer* include-search tagsrch.txt /*include-search* inclusive motion.txt /*inclusive* @@ -6904,11 +6911,11 @@ job-err-cb channel.txt /*job-err-cb* job-err-io channel.txt /*job-err-io* job-exit-cb channel.txt /*job-exit-cb* job-in-io channel.txt /*job-in-io* -job-may-start channel.txt /*job-may-start* job-options channel.txt /*job-options* job-out-cb channel.txt /*job-out-cb* job-out-io channel.txt /*job-out-io* job-start channel.txt /*job-start* +job-start-if-needed channel.txt /*job-start-if-needed* job-start-nochannel channel.txt /*job-start-nochannel* job-stoponexit channel.txt /*job-stoponexit* job-term channel.txt /*job-term* @@ -7666,7 +7673,9 @@ os_unix.txt os_unix.txt /*os_unix.txt* os_vms.txt os_vms.txt /*os_vms.txt* os_win32.txt os_win32.txt /*os_win32.txt* other-features vi_diff.txt /*other-features* +out-buf channel.txt /*out-buf* out-cb channel.txt /*out-cb* +out-name channel.txt /*out-name* out-timeout channel.txt /*out-timeout* p change.txt /*p* pack-add repeat.txt /*pack-add* @@ -8073,6 +8082,7 @@ set-spc-auto spell.txt /*set-spc-auto* setbufvar() eval.txt /*setbufvar()* setcharsearch() eval.txt /*setcharsearch()* setcmdpos() eval.txt /*setcmdpos()* +setfperm() eval.txt /*setfperm()* setline() eval.txt /*setline()* setloclist() eval.txt /*setloclist()* setmatches() eval.txt /*setmatches()* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index c6c38e14c0..afa8219135 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 7.4. Last change: 2016 Mar 07 +*todo.txt* For Vim version 7.4. Last change: 2016 Mar 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -35,16 +35,11 @@ not be repeated below, unless there is extra information. -------------------- Known bugs and current work ----------------------- +channel: +- CHANNEL_PIPES -> FEAT_JOB +- FEAT_JOB / FEAT_CHANNEL -> FEAT_JOBCHANNEL ? - move code from eval.c to channel.c -- implement TODO items in ":help channel": - - job_start() options: - in-io: null, in-buf - out-io: null, file, out-buf - err-io: null, file (err-name), buffer (err-buf) - existing channel to use - - job_maystart() - - add job_info(): process ID, run/dead, etc. - - add ch_info(): in/out/err mode, timeout, callbacks, etc. +- add job_info(): process ID, run/dead, etc. +- add ch_info(): in/out/err mode, timeout, callbacks, etc. - Move more details from eval.txt to channel.txt. Add tags in eval.txt. - When receiving malformed json starting with a quote it doesn't get discarded. Any invalid JSON or JSON that isn't a list will block further @@ -53,16 +48,16 @@ not be repeated below, unless there is extra information. properly. - When a message in the queue but there is no callback, drop it after a while? Add timestamp to queued messages and callbacks with ID, remove after a - minute. -- Add more log calls, basically at every branch, before every callback, etc. -- add remark about undo sync, is there a way to force it? + minute. Option to set the droptime. +- Add more ch_log calls, basically at every branch, before every callback, etc. +- Add remark about undo sync, is there a way to force it? - When starting a job, have an option to open the server socket, so we know the port, and pass it to the command with --socket-fd {nr}. (Olaf Dabrunz, Feb 9) How to do this on MS-Windows? - Add more unit-testing in json_test.c - Add a test where ["eval","getline(123)"] gets a line with special characters (NUL, 0x80, etc.). Check that it isn't garbled. -- make sure errors lead to a useful error msg. ["ex","foobar"] +- Make sure errors lead to a useful error msg. ["ex","foobar"] - For connection to server, a "keep open" flag would be useful. Retry connecting in the main loop with zero timeout. Later @@ -71,13 +66,16 @@ Later emoji patch from Yasuhiro Matsumoto. Asked Thomas Dickey. +Remove sticky type checking. + Packages: - Add command to update help tags in 'runtimepath'. Pathogen has something like that. - colorscheme command in .vimrc doesn't work. - - Postpone until later? - - Also search in 'packpath'? - - command to load packages now? + - Also search in 'packpath', both "start" and "opt", don't add dir to 'rtp' +- command like :runtime that also search 'packpath'. :packruntime + use "ever" or "opt"? both? +- command to load packages now? More plugin support: - Have a way to install a callback from the main loop. Called every second or @@ -198,9 +196,13 @@ Two patches now? New update Feb 24. Patch to support 64 bit ints for Number. (Ken Takata, 2016 Jan 21) Also in update of Feb 24? +After 7.5 is released: +- Drop support for older MS-Windows systems, before XP. + Patch from Ken Takata, 2016 Mar 8. + Patch to add setbufline(). (email from Yasuhiro Matsumoto, patch by Ozaki Kiichi, 2016 Feb 28) -https://gist.github.com/ichizok/64bdc92aed19ec9001dd +Update Mar 8: https://gist.github.com/mattn/23c1f50999084992ca98 Need to try out instructions in INSSTALLpc.txt about how to install all interfaces and how to build Vim with them. @@ -225,8 +227,6 @@ What if there is an invalid character? Should jsonencode()/jsondecode() restrict recursiveness? Or avoid recursiveness. -Patch to fix bug in statusline highlighting. (Christian Brabandt, 2016 Feb 2) - Use vim.vim syntax highlighting for help file examples, but without ":" in 'iskeyword' for syntax. @@ -306,7 +306,7 @@ set_color_count(). Python: ":py raw_input('prompt')" doesn't work. (Manu Hack) -Comparing nested structures with "==" uses a different comperator than when +Comparing nested structures with "==" uses a different comparator than when comparing individual items. Also, "'' == 0" evaluates to true, which isn't nice. Add "===" to have a strict comparison (type and value match). diff --git a/runtime/ftplugin/r.vim b/runtime/ftplugin/r.vim index 43b208b842..4ea3073922 100644 --- a/runtime/ftplugin/r.vim +++ b/runtime/ftplugin/r.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: R " Maintainer: Jakson Alves de Aquino -" Last Change: Sun Feb 23, 2014 04:07PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Only do this when not yet done for this buffer if exists("b:did_ftplugin") diff --git a/runtime/ftplugin/rhelp.vim b/runtime/ftplugin/rhelp.vim index 9b72fb42f9..fdac38f3e9 100644 --- a/runtime/ftplugin/rhelp.vim +++ b/runtime/ftplugin/rhelp.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: R help file " Maintainer: Jakson Alves de Aquino -" Last Change: Wed Jul 09, 2014 06:23PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:37PM " Only do this when not yet done for this buffer if exists("b:did_ftplugin") diff --git a/runtime/ftplugin/rmd.vim b/runtime/ftplugin/rmd.vim index 55723ff396..ec64a07675 100644 --- a/runtime/ftplugin/rmd.vim +++ b/runtime/ftplugin/rmd.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: R help file " Maintainer: Jakson Alves de Aquino -" Last Change: Wed Jul 09, 2014 06:23PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:37PM " Original work by Alex Zvoleff (adjusted for rmd by Michel Kuhlmann) " Only do this when not yet done for this buffer diff --git a/runtime/ftplugin/rnoweb.vim b/runtime/ftplugin/rnoweb.vim index baf53d0108..e184399dcb 100644 --- a/runtime/ftplugin/rnoweb.vim +++ b/runtime/ftplugin/rnoweb.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: Rnoweb " Maintainer: Jakson Alves de Aquino -" Last Change: Wed Jul 09, 2014 06:23PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:37PM " Only do this when not yet done for this buffer if exists("b:did_ftplugin") diff --git a/runtime/ftplugin/rrst.vim b/runtime/ftplugin/rrst.vim index 8140169e61..ecfd6e87a1 100644 --- a/runtime/ftplugin/rrst.vim +++ b/runtime/ftplugin/rrst.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: reStructuredText documentation format with R code " Maintainer: Jakson Alves de Aquino -" Last Change: Wed Jul 09, 2014 06:23PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Original work by Alex Zvoleff " Only do this when not yet done for this buffer diff --git a/runtime/indent/r.vim b/runtime/indent/r.vim index 105f0cd7ad..01f3812ed2 100644 --- a/runtime/indent/r.vim +++ b/runtime/indent/r.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: R " Author: Jakson Alves de Aquino -" Last Change: Thu Mar 26, 2015 05:36PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Thu Feb 18, 2016 06:32AM " Only load this indent file when no other was loaded. @@ -32,7 +33,7 @@ if ! exists("g:r_indent_ess_compatible") let g:r_indent_ess_compatible = 0 endif if ! exists("g:r_indent_op_pattern") - let g:r_indent_op_pattern = '\(+\|-\|\*\|/\|=\|\~\|%\)$' + let g:r_indent_op_pattern = '\(&\||\|+\|-\|\*\|/\|=\|\~\|%\|->\)\s*$' endif function s:RDelete_quotes(line) @@ -265,7 +266,7 @@ function GetRIndent() return 0 endif - if cline =~ '^\s*{' + if cline =~ '^\s*{' && s:Get_paren_balance(cline, '{', '}') > 0 if g:r_indent_ess_compatible && line =~ ')$' let nlnum = lnum let nline = line @@ -283,7 +284,7 @@ function GetRIndent() endif " line is an incomplete command: - if line =~ '\<\(if\|while\|for\|function\)\s*()$' || line =~ '\$' return indent(lnum) + &sw endif @@ -344,7 +345,7 @@ function GetRIndent() endif let post_block = 0 - if line =~ '}$' + if line =~ '}$' && s:Get_paren_balance(line, '{', '}') < 0 let lnum = s:Get_matching_brace(lnum, '{', '}', 0) let line = SanitizeRLine(getline(lnum)) if lnum > 0 && line =~ '^\s*{' @@ -359,14 +360,14 @@ function GetRIndent() let olnum = s:Get_prev_line(lnum) let oline = getline(olnum) if olnum > 0 - if line =~ g:r_indent_op_pattern - if oline =~ g:r_indent_op_pattern + if line =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 + if oline =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 return indent(lnum) else return indent(lnum) + &sw endif else - if oline =~ g:r_indent_op_pattern + if oline =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 return indent(lnum) - &sw endif endif @@ -471,7 +472,6 @@ function GetRIndent() endif let ind = indent(lnum) - let pind = indent(plnum) if g:r_indent_align_args == 0 && pb != 0 let ind += pb * &sw @@ -483,6 +483,12 @@ function GetRIndent() return ind endif + if plnum > 0 + let pind = indent(plnum) + else + let pind = 0 + endif + if ind == pind || (ind == (pind + &sw) && pline =~ '{$' && ppost_else == 0) return ind endif diff --git a/runtime/indent/rhelp.vim b/runtime/indent/rhelp.vim index 3b37128b2c..9dc2031cb6 100644 --- a/runtime/indent/rhelp.vim +++ b/runtime/indent/rhelp.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: R Documentation (Help), *.Rd " Author: Jakson Alves de Aquino -" Last Change: Thu Oct 16, 2014 07:07AM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Only load this indent file when no other was loaded. diff --git a/runtime/indent/rmd.vim b/runtime/indent/rmd.vim index 9a8a3cb719..88904405e8 100644 --- a/runtime/indent/rmd.vim +++ b/runtime/indent/rmd.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: Rmd " Author: Jakson Alves de Aquino -" Last Change: Thu Jul 10, 2014 07:11PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Only load this indent file when no other was loaded. diff --git a/runtime/indent/rnoweb.vim b/runtime/indent/rnoweb.vim index d0cad3d8d9..29fa5bc78f 100644 --- a/runtime/indent/rnoweb.vim +++ b/runtime/indent/rnoweb.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: Rnoweb " Author: Jakson Alves de Aquino -" Last Change: Sun Mar 22, 2015 09:28AM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Only load this indent file when no other was loaded. diff --git a/runtime/indent/rrst.vim b/runtime/indent/rrst.vim index 8bfa8344ce..f3ee53e7fb 100644 --- a/runtime/indent/rrst.vim +++ b/runtime/indent/rrst.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: Rrst " Author: Jakson Alves de Aquino -" Last Change: Wed Jul 09, 2014 07:33PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Tue Apr 07, 2015 04:38PM " Only load this indent file when no other was loaded. diff --git a/runtime/syntax/python.vim b/runtime/syntax/python.vim index 6161eb3953..68979a87c2 100644 --- a/runtime/syntax/python.vim +++ b/runtime/syntax/python.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Python " Maintainer: Zvezdan Petkovic -" Last Change: 2015 Sep 15 +" Last Change: 2016 Feb 20 " Credits: Neil Schemenauer " Dmitry Vasiliev " @@ -199,6 +199,8 @@ if !exists("python_no_builtin_highlight") syn keyword pythonBuiltin ascii bytes exec " non-essential built-in functions; Python 2 only syn keyword pythonBuiltin apply buffer coerce intern + " avoid highlighting attributes as builtins + syn match pythonAttribute /\.\h\w*/hs=s+1 contains=ALLBUT,pythonBuiltin transparent endif " From the 'Python Library Reference' class hierarchy at the bottom. diff --git a/runtime/syntax/r.vim b/runtime/syntax/r.vim index e48b6686cb..d96bf96acb 100644 --- a/runtime/syntax/r.vim +++ b/runtime/syntax/r.vim @@ -5,17 +5,21 @@ " Tom Payne " Contributor: Johannes Ranke " Homepage: https://github.com/jalvesaq/R-Vim-runtime -" Last Change: Wed Oct 21, 2015 06:33AM +" Last Change: Thu Mar 10, 2016 12:26PM " Filenames: *.R *.r *.Rhistory *.Rt " " NOTE: The highlighting of R functions is defined in " runtime files created by a filetype plugin, if installed. " " CONFIGURATION: -" syntax folding can be turned on by +" Syntax folding can be turned on by " " let r_syntax_folding = 1 " +" ROxygen highlighting can be turned off by +" +" let r_hl_roxygen = 0 +" " Some lines of code were borrowed from Zhuojun Chen. if exists("b:current_syntax") @@ -24,9 +28,12 @@ endif setlocal iskeyword=@,48-57,_,. -if exists("g:r_syntax_folding") +if exists("g:r_syntax_folding") && g:r_syntax_folding setlocal foldmethod=syntax endif +if !exists("g:r_hl_roxygen") + let g:r_hl_roxygen = 1 +endif syn case match @@ -35,18 +42,20 @@ syn match rCommentTodo contained "\(BUG\|FIXME\|NOTE\|TODO\):" syn match rComment contains=@Spell,rCommentTodo,rOBlock "#.*" " Roxygen -syn region rOBlock start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\)\@!" contains=rOTitle,rOKeyword,rOExamples,@Spell keepend -syn region rOTitle start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\s*$\)\@=" contained contains=rOCommentKey -syn match rOCommentKey "#\{1,2}'" containedin=rOTitle contained +if g:r_hl_roxygen + syn region rOBlock start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\)\@!" contains=rOTitle,rOKeyword,rOExamples,@Spell keepend + syn region rOTitle start="^\s*\n#\{1,2}' " start="\%^#\{1,2}' " end="^\(#\{1,2}'\s*$\)\@=" contained contains=rOCommentKey + syn match rOCommentKey "#\{1,2}'" containedin=rOTitle contained -syn region rOExamples start="^#\{1,2}' @examples.*"rs=e+1,hs=e+1 end="^\(#\{1,2}' @.*\)\@=" end="^\(#\{1,2}'\)\@!" contained contains=rOKeyword + syn region rOExamples start="^#\{1,2}' @examples.*"rs=e+1,hs=e+1 end="^\(#\{1,2}' @.*\)\@=" end="^\(#\{1,2}'\)\@!" contained contains=rOKeyword -syn match rOKeyword contained "@\(param\|return\|name\|rdname\|examples\|example\|include\|docType\)" -syn match rOKeyword contained "@\(S3method\|TODO\|aliases\|alias\|assignee\|author\|callGraphDepth\|callGraph\)" -syn match rOKeyword contained "@\(callGraphPrimitives\|concept\|exportClass\|exportMethod\|exportPattern\|export\|formals\)" -syn match rOKeyword contained "@\(format\|importClassesFrom\|importFrom\|importMethodsFrom\|import\|keywords\|useDynLib\)" -syn match rOKeyword contained "@\(method\|noRd\|note\|references\|seealso\|setClass\|slot\|source\|title\|usage\)" -syn match rOKeyword contained "@\(family\|template\|templateVar\|description\|details\|inheritParams\|field\)" + syn match rOKeyword contained "@\(param\|return\|name\|rdname\|examples\|example\|include\|docType\)" + syn match rOKeyword contained "@\(S3method\|TODO\|aliases\|alias\|assignee\|author\|callGraphDepth\|callGraph\)" + syn match rOKeyword contained "@\(callGraphPrimitives\|concept\|exportClass\|exportMethod\|exportPattern\|export\|formals\)" + syn match rOKeyword contained "@\(format\|importClassesFrom\|importFrom\|importMethodsFrom\|import\|keywords\|useDynLib\)" + syn match rOKeyword contained "@\(method\|noRd\|note\|references\|seealso\|setClass\|slot\|source\|title\|usage\)" + syn match rOKeyword contained "@\(family\|template\|templateVar\|description\|details\|inheritParams\|field\)" +endif if &filetype == "rhelp" @@ -159,12 +168,13 @@ syn match rBraceError "[)}]" contained syn match rCurlyError "[)\]]" contained syn match rParenError "[\]}]" contained -" Source list of R functions produced by a filetype plugin (if installed) -if has("nvim") - " Nvim-R +if !exists("g:R_hi_fun") + let g:R_hi_fun = 1 +endif +if g:R_hi_fun + " Nvim-R: runtime R/functions.vim -else - " Vim-R-plugin + " Vim-R-plugin: runtime r-plugin/functions.vim endif @@ -235,11 +245,13 @@ hi def link rStatement Statement hi def link rString String hi def link rStrError Error hi def link rType Type -hi def link rOKeyword Title -hi def link rOBlock Comment -hi def link rOTitle Title -hi def link rOCommentKey Comment -hi def link rOExamples SpecialComment +if g:r_hl_roxygen + hi def link rOKeyword Title + hi def link rOBlock Comment + hi def link rOTitle Title + hi def link rOCommentKey Comment + hi def link rOExamples SpecialComment +endif let b:current_syntax="r" diff --git a/runtime/syntax/rhelp.vim b/runtime/syntax/rhelp.vim index 32c91add48..47c764e296 100644 --- a/runtime/syntax/rhelp.vim +++ b/runtime/syntax/rhelp.vim @@ -2,25 +2,21 @@ " Language: R Help File " Maintainer: Jakson Aquino " Former Maintainer: Johannes Ranke -" Last Change: Wed Jul 09, 2014 10:28PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Sat Feb 06, 2016 11:34AM " Remarks: - Includes R syntax highlighting in the appropriate " sections if an r.vim file is in the same directory or in the " default debian location. " - There is no Latex markup in equations " - Thanks to Will Gray for finding and fixing a bug -" - No support for \if, \ifelse and \out as I don't understand -" them and have no examples at hand (help welcome). -" - No support for \var tag within quoted string (dito) +" - No support for \var tag within quoted string " Version Clears: {{{1 -" For version 5.x: Clear all syntax items -" For version 6.x and 7.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif +scriptencoding utf-8 setlocal iskeyword=@,48-57,_,. syn case match @@ -29,9 +25,11 @@ syn case match syn region rhelpIdentifier matchgroup=rhelpSection start="\\name{" end="}" syn region rhelpIdentifier matchgroup=rhelpSection start="\\alias{" end="}" syn region rhelpIdentifier matchgroup=rhelpSection start="\\pkg{" end="}" contains=rhelpLink +syn region rhelpIdentifier matchgroup=rhelpSection start="\\CRANpkg{" end="}" contains=rhelpLink syn region rhelpIdentifier matchgroup=rhelpSection start="\\method{" end="}" contained syn region rhelpIdentifier matchgroup=rhelpSection start="\\Rdversion{" end="}" + " Highlighting of R code using an existing r.vim syntax file if available {{{1 syn include @R syntax/r.vim @@ -69,76 +67,115 @@ syn match rhelpDelimiter "\\cr" syn match rhelpDelimiter "\\tab " " Keywords {{{1 -syn match rhelpKeyword "\\R" -syn match rhelpKeyword "\\ldots" +syn match rhelpKeyword "\\R\>" +syn match rhelpKeyword "\\ldots\>" +syn match rhelpKeyword "\\sspace\>" syn match rhelpKeyword "--" syn match rhelpKeyword "---" -syn match rhelpKeyword "<" -syn match rhelpKeyword ">" -syn match rhelpKeyword "\\ge" -syn match rhelpKeyword "\\le" -syn match rhelpKeyword "\\alpha" -syn match rhelpKeyword "\\beta" -syn match rhelpKeyword "\\gamma" -syn match rhelpKeyword "\\delta" -syn match rhelpKeyword "\\epsilon" -syn match rhelpKeyword "\\zeta" -syn match rhelpKeyword "\\eta" -syn match rhelpKeyword "\\theta" -syn match rhelpKeyword "\\iota" -syn match rhelpKeyword "\\kappa" -syn match rhelpKeyword "\\lambda" -syn match rhelpKeyword "\\mu" -syn match rhelpKeyword "\\nu" -syn match rhelpKeyword "\\xi" -syn match rhelpKeyword "\\omicron" -syn match rhelpKeyword "\\pi" -syn match rhelpKeyword "\\rho" -syn match rhelpKeyword "\\sigma" -syn match rhelpKeyword "\\tau" -syn match rhelpKeyword "\\upsilon" -syn match rhelpKeyword "\\phi" -syn match rhelpKeyword "\\chi" -syn match rhelpKeyword "\\psi" -syn match rhelpKeyword "\\omega" -syn match rhelpKeyword "\\Alpha" -syn match rhelpKeyword "\\Beta" -syn match rhelpKeyword "\\Gamma" -syn match rhelpKeyword "\\Delta" -syn match rhelpKeyword "\\Epsilon" -syn match rhelpKeyword "\\Zeta" -syn match rhelpKeyword "\\Eta" -syn match rhelpKeyword "\\Theta" -syn match rhelpKeyword "\\Iota" -syn match rhelpKeyword "\\Kappa" -syn match rhelpKeyword "\\Lambda" -syn match rhelpKeyword "\\Mu" -syn match rhelpKeyword "\\Nu" -syn match rhelpKeyword "\\Xi" -syn match rhelpKeyword "\\Omicron" -syn match rhelpKeyword "\\Pi" -syn match rhelpKeyword "\\Rho" -syn match rhelpKeyword "\\Sigma" -syn match rhelpKeyword "\\Tau" -syn match rhelpKeyword "\\Upsilon" -syn match rhelpKeyword "\\Phi" -syn match rhelpKeyword "\\Chi" -syn match rhelpKeyword "\\Psi" -syn match rhelpKeyword "\\Omega" + +" Condition Keywords {{{2 +syn match rhelpKeyword "\\if\>" +syn match rhelpKeyword "\\ifelse\>" +syn match rhelpKeyword "\\out\>" +" Examples of usage: +" \ifelse{latex}{\eqn{p = 5 + 6 - 7 \times 8}}{\eqn{p = 5 + 6 - 7 * 8}} +" \ifelse{latex}{\out{$\alpha$}}{\ifelse{html}{\out{α}}{alpha}} + +" Keywords and operators valid only if in math mode {{{2 +syn match rhelpMathOp "<" contained +syn match rhelpMathOp ">" contained +syn match rhelpMathOp "+" contained +syn match rhelpMathOp "-" contained +syn match rhelpMathOp "=" contained + +" Conceal function based on syntax/tex.vim {{{2 +if exists("g:tex_conceal") + let s:tex_conceal = g:tex_conceal +else + let s:tex_conceal = 'gm' +endif +function s:HideSymbol(pat, cchar, hide) + if a:hide + exe "syn match rhelpMathSymb '" . a:pat . "' contained conceal cchar=" . a:cchar + else + exe "syn match rhelpMathSymb '" . a:pat . "' contained" + endif +endfunction + +" Math symbols {{{2 +if s:tex_conceal =~ 'm' + let s:hd = 1 +else + let s:hd = 0 +endif +call s:HideSymbol('\\infty\>', '∞', s:hd) +call s:HideSymbol('\\ge\>', '≥', s:hd) +call s:HideSymbol('\\le\>', '≤', s:hd) +call s:HideSymbol('\\prod\>', '∏', s:hd) +call s:HideSymbol('\\sum\>', '∑', s:hd) +syn match rhelpMathSymb "\\sqrt\>" contained + +" Greek letters {{{2 +if s:tex_conceal =~ 'g' + let s:hd = 1 +else + let s:hd = 0 +endif +call s:HideSymbol('\\alpha\>', 'α', s:hd) +call s:HideSymbol('\\beta\>', 'β', s:hd) +call s:HideSymbol('\\gamma\>', 'γ', s:hd) +call s:HideSymbol('\\delta\>', 'δ', s:hd) +call s:HideSymbol('\\epsilon\>', 'ϵ', s:hd) +call s:HideSymbol('\\zeta\>', 'ζ', s:hd) +call s:HideSymbol('\\eta\>', 'η', s:hd) +call s:HideSymbol('\\theta\>', 'θ', s:hd) +call s:HideSymbol('\\iota\>', 'ι', s:hd) +call s:HideSymbol('\\kappa\>', 'κ', s:hd) +call s:HideSymbol('\\lambda\>', 'λ', s:hd) +call s:HideSymbol('\\mu\>', 'μ', s:hd) +call s:HideSymbol('\\nu\>', 'ν', s:hd) +call s:HideSymbol('\\xi\>', 'ξ', s:hd) +call s:HideSymbol('\\pi\>', 'π', s:hd) +call s:HideSymbol('\\rho\>', 'ρ', s:hd) +call s:HideSymbol('\\sigma\>', 'σ', s:hd) +call s:HideSymbol('\\tau\>', 'τ', s:hd) +call s:HideSymbol('\\upsilon\>', 'υ', s:hd) +call s:HideSymbol('\\phi\>', 'ϕ', s:hd) +call s:HideSymbol('\\chi\>', 'χ', s:hd) +call s:HideSymbol('\\psi\>', 'ψ', s:hd) +call s:HideSymbol('\\omega\>', 'ω', s:hd) +call s:HideSymbol('\\Gamma\>', 'Γ', s:hd) +call s:HideSymbol('\\Delta\>', 'Δ', s:hd) +call s:HideSymbol('\\Theta\>', 'Θ', s:hd) +call s:HideSymbol('\\Lambda\>', 'Λ', s:hd) +call s:HideSymbol('\\Xi\>', 'Ξ', s:hd) +call s:HideSymbol('\\Pi\>', 'Π', s:hd) +call s:HideSymbol('\\Sigma\>', 'Σ', s:hd) +call s:HideSymbol('\\Upsilon\>', 'Υ', s:hd) +call s:HideSymbol('\\Phi\>', 'Φ', s:hd) +call s:HideSymbol('\\Psi\>', 'Ψ', s:hd) +call s:HideSymbol('\\Omega\>', 'Ω', s:hd) +delfunction s:HideSymbol +" Note: The letters 'omicron', 'Alpha', 'Beta', 'Epsilon', 'Zeta', 'Eta', +" 'Iota', 'Kappa', 'Mu', 'Nu', 'Omicron', 'Rho', 'Tau' and 'Chi' are listed +" at src/library/tools/R/Rd2txt.R because they are valid in HTML, although +" they do not make valid LaTeX code (e.g. Α versus \Alpha). " Links {{{1 -syn region rhelpLink matchgroup=rhelpSection start="\\link{" end="}" contained keepend extend -syn region rhelpLink matchgroup=rhelpSection start="\\link\[.\{-}\]{" end="}" contained keepend extend -syn region rhelpLink matchgroup=rhelpSection start="\\linkS4class{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\link{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\link\[.\{-}\]{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\linkS4class{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\url{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\href{" end="}" contained keepend extend +syn region rhelpLink matchgroup=rhelpType start="\\figure{" end="}" contained keepend extend " Verbatim like {{{1 -if v:version > 703 - syn region rhelpVerbatim matchgroup=rhelpType start="\\samp{" skip='\\\@1" @@ -148,12 +185,9 @@ syn match rhelpType "\\sQuote\>" syn match rhelpType "\\dQuote\>" syn match rhelpType "\\preformatted\>" syn match rhelpType "\\kbd\>" -syn match rhelpType "\\eqn\>" -syn match rhelpType "\\deqn\>" syn match rhelpType "\\file\>" syn match rhelpType "\\email\>" -syn match rhelpType "\\url\>" -syn match rhelpType "\\href\>" +syn match rhelpType "\\enc\>" syn match rhelpType "\\var\>" syn match rhelpType "\\env\>" syn match rhelpType "\\option\>" @@ -163,6 +197,7 @@ syn match rhelpType "\\renewcommand\>" syn match rhelpType "\\dfn\>" syn match rhelpType "\\cite\>" syn match rhelpType "\\acronym\>" +syn match rhelpType "\\doi\>" " rhelp sections {{{1 syn match rhelpSection "\\encoding\>" @@ -202,9 +237,9 @@ syn match rhelpDelimiter "{\|\[\|(\|)\|\]\|}" syn match rhelpComment /%.*$/ " Error {{{1 -syn region rhelpRegion matchgroup=Delimiter start=/(/ matchgroup=Delimiter end=/)/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim -syn region rhelpRegion matchgroup=Delimiter start=/{/ matchgroup=Delimiter end=/}/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim -syn region rhelpRegion matchgroup=Delimiter start=/\[/ matchgroup=Delimiter end=/]/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim +syn region rhelpRegion matchgroup=Delimiter start=/(/ matchgroup=Delimiter end=/)/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim,rhelpEquation +syn region rhelpRegion matchgroup=Delimiter start=/{/ matchgroup=Delimiter end=/}/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim,rhelpEquation +syn region rhelpRegion matchgroup=Delimiter start=/\[/ matchgroup=Delimiter end=/]/ contains=@Spell,rhelpCodeSpecial,rhelpComment,rhelpDelimiter,rhelpDots,rhelpFreesec,rhelpFreesubsec,rhelpIdentifier,rhelpKeyword,rhelpLink,rhelpPreProc,rhelpRComment,rhelpRcode,rhelpRegion,rhelpS4method,rhelpSection,rhelpSexpr,rhelpSpecialChar,rhelpString,rhelpType,rhelpVerbatim,rhelpEquation syn match rhelpError /[)\]}]/ syn match rhelpBraceError /[)}]/ contained syn match rhelpCurlyError /[)\]]/ contained @@ -213,36 +248,27 @@ syn match rhelpParenError /[\]}]/ contained syntax sync match rhelpSyncRcode grouphere rhelpRcode "\\examples{" " Define the default highlighting {{{1 -" For version 5.7 and earlier: only when not done already -" For version 5.8 and later: only when an item doesn't have highlighting yet -if version >= 508 || !exists("did_rhelp_syntax_inits") - if version < 508 - let did_rhelp_syntax_inits = 1 - command -nargs=+ HiLink hi link - else - command -nargs=+ HiLink hi def link - endif - HiLink rhelpVerbatim String - HiLink rhelpDelimiter Delimiter - HiLink rhelpIdentifier Identifier - HiLink rhelpString String - HiLink rhelpCodeSpecial Special - HiLink rhelpKeyword Keyword - HiLink rhelpDots Keyword - HiLink rhelpLink Underlined - HiLink rhelpType Type - HiLink rhelpSection PreCondit - HiLink rhelpError Error - HiLink rhelpBraceError Error - HiLink rhelpCurlyError Error - HiLink rhelpParenError Error - HiLink rhelpPreProc PreProc - HiLink rhelpDelimiter Delimiter - HiLink rhelpComment Comment - HiLink rhelpRComment Comment - HiLink rhelpSpecialChar SpecialChar - delcommand HiLink -endif +hi def link rhelpVerbatim String +hi def link rhelpDelimiter Delimiter +hi def link rhelpIdentifier Identifier +hi def link rhelpString String +hi def link rhelpCodeSpecial Special +hi def link rhelpKeyword Keyword +hi def link rhelpDots Keyword +hi def link rhelpLink Underlined +hi def link rhelpType Type +hi def link rhelpSection PreCondit +hi def link rhelpError Error +hi def link rhelpBraceError Error +hi def link rhelpCurlyError Error +hi def link rhelpParenError Error +hi def link rhelpPreProc PreProc +hi def link rhelpDelimiter Delimiter +hi def link rhelpComment Comment +hi def link rhelpRComment Comment +hi def link rhelpSpecialChar SpecialChar +hi def link rhelpMathSymb Special +hi def link rhelpMathOp Operator let b:current_syntax = "rhelp" diff --git a/runtime/syntax/rmd.vim b/runtime/syntax/rmd.vim index 6f1b847453..4cde7441d3 100644 --- a/runtime/syntax/rmd.vim +++ b/runtime/syntax/rmd.vim @@ -1,15 +1,13 @@ " markdown Text with R statements " Language: markdown with R code chunks -" Last Change: Wed Jul 09, 2014 10:29PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Sat Feb 06, 2016 06:45AM " " CONFIGURATION: " To highlight chunk headers as R code, put in your vimrc: " let rmd_syn_hl_chunk = 1 -" for portability -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif @@ -58,6 +56,8 @@ if rmdIsPandoc == 0 if exists("b:current_syntax") unlet b:current_syntax endif + " Extend cluster + syn cluster texMathZoneGroup add=rmdrInline " Inline syntax match rmdLaTeXInlDelim "\$" syntax match rmdLaTeXInlDelim "\\\$" diff --git a/runtime/syntax/rnoweb.vim b/runtime/syntax/rnoweb.vim index 7d42395b5c..665acc53e2 100644 --- a/runtime/syntax/rnoweb.vim +++ b/runtime/syntax/rnoweb.vim @@ -1,20 +1,14 @@ " Vim syntax file " Language: R noweb Files " Maintainer: Johannes Ranke -" Last Change: 2009 May 05 -" Version: 0.9 -" SVN: $Id: rnoweb.vim 84 2009-05-03 19:52:47Z ranke $ +" Last Change: Sat Feb 06, 2016 06:47AM +" Version: 0.9.1 " Remarks: - This file is inspired by the proposal of -" Fernando Henrique Ferraz Pereira da Rosa -" http://www.ime.usp.br/~feferraz/en/sweavevim.html +" Fernando Henrique Ferraz Pereira da Rosa +" http://www.ime.usp.br/~feferraz/en/sweavevim.html " -" Version Clears: {{{1 -" For version 5.x: Clear all syntax items -" For version 6.x and 7.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif @@ -26,21 +20,22 @@ unlet b:current_syntax syn cluster texMatchGroup add=@rnoweb syn cluster texMathMatchGroup add=rnowebSexpr +syn cluster texMathZoneGroup add=rnowebSexpr syn cluster texEnvGroup add=@rnoweb syn cluster texFoldGroup add=@rnoweb -syn cluster texDocGroup add=@rnoweb -syn cluster texPartGroup add=@rnoweb -syn cluster texChapterGroup add=@rnoweb -syn cluster texSectionGroup add=@rnoweb -syn cluster texSubSectionGroup add=@rnoweb -syn cluster texSubSubSectionGroup add=@rnoweb -syn cluster texParaGroup add=@rnoweb +syn cluster texDocGroup add=@rnoweb +syn cluster texPartGroup add=@rnoweb +syn cluster texChapterGroup add=@rnoweb +syn cluster texSectionGroup add=@rnoweb +syn cluster texSubSectionGroup add=@rnoweb +syn cluster texSubSubSectionGroup add=@rnoweb +syn cluster texParaGroup add=@rnoweb " Highlighting of R code using an existing r.vim syntax file if available {{{1 syn include @rnowebR syntax/r.vim syn region rnowebChunk matchgroup=rnowebDelimiter start="^<<.*>>=" matchgroup=rnowebDelimiter end="^@" contains=@rnowebR,rnowebChunkReference,rnowebChunk fold keepend syn match rnowebChunkReference "^<<.*>>$" contained -syn region rnowebSexpr matchgroup=Delimiter start="\\Sexpr{" matchgroup=Delimiter end="}" contains=@rnowebR +syn region rnowebSexpr matchgroup=Delimiter start="\\Sexpr{" matchgroup=Delimiter end="}" contains=@rnowebR contained " Sweave options command {{{1 syn region rnowebSweaveopts matchgroup=Delimiter start="\\SweaveOpts{" matchgroup=Delimiter end="}" diff --git a/runtime/syntax/rrst.vim b/runtime/syntax/rrst.vim index 4667b3a2c1..24d3844df0 100644 --- a/runtime/syntax/rrst.vim +++ b/runtime/syntax/rrst.vim @@ -1,16 +1,14 @@ " reStructured Text with R statements " Language: reST with R code chunks " Maintainer: Alex Zvoleff, azvoleff@mail.sdsu.edu -" Last Change: Wed Jul 09, 2014 10:29PM +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Sat Feb 06, 2016 06:45AM " " CONFIGURATION: " To highlight chunk headers as R code, put in your vimrc: " let rrst_syn_hl_chunk = 1 -" for portability -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists("b:current_syntax") finish endif diff --git a/runtime/syntax/vhdl.vim b/runtime/syntax/vhdl.vim index 044ef83d17..32503823ee 100644 --- a/runtime/syntax/vhdl.vim +++ b/runtime/syntax/vhdl.vim @@ -54,37 +54,37 @@ syn match vhdlError "\" " Types and type qualifiers " Predefined standard VHDL types -syn match vhdlType "bit[\']*" -syn match vhdlType "boolean[\']*" -syn match vhdlType "natural[\']*" -syn match vhdlType "positive[\']*" -syn match vhdlType "integer[\']*" -syn match vhdlType "real[\']*" -syn match vhdlType "time[\']*" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" -syn match vhdlType "bit_vector[\']*" -syn match vhdlType "boolean_vector[\']*" -syn match vhdlType "integer_vector[\']*" -syn match vhdlType "real_vector[\']*" -syn match vhdlType "time_vector[\']*" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" -syn match vhdlType "character[\']*" -syn match vhdlType "string[\']*" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" "syn keyword vhdlType severity_level -syn match vhdlType "line[\']*" -syn match vhdlType "text[\']*" +syn keyword vhdlType line +syn keyword vhdlType text " Predefined standard IEEE VHDL types -syn match vhdlType "std_ulogic[\']*" -syn match vhdlType "std_logic[\']*" -syn match vhdlType "std_ulogic_vector[\']*" -syn match vhdlType "std_logic_vector[\']*" -syn match vhdlType "unresolved_signed[\']*" -syn match vhdlType "unresolved_unsigned[\']*" -syn match vhdlType "u_signed[\']*" -syn match vhdlType "u_unsigned[\']*" -syn match vhdlType "signed[\']*" -syn match vhdlType "unsigned[\']*" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" +syn match vhdlType "\\'\=" " array attributes diff --git a/src/channel.c b/src/channel.c index 65eb6e1e6c..9a4c448a45 100644 --- a/src/channel.c +++ b/src/channel.c @@ -97,11 +97,24 @@ static proftime_T log_start; #endif void -ch_logfile(FILE *file) +ch_logfile(char_u *fname, char_u *opt) { + FILE *file = NULL; + if (log_fd != NULL) fclose(log_fd); + + if (*fname != NUL) + { + file = fopen((char *)fname, *opt == 'w' ? "w" : "a"); + if (file == NULL) + { + EMSG2(_(e_notopen), fname); + return; + } + } log_fd = file; + if (log_fd != NULL) { fprintf(log_fd, "==== start log session ====\n"); @@ -363,7 +376,7 @@ channel_still_useful(channel_T *channel) * killed. * Return TRUE if the channel was freed. */ - int + static int channel_may_free(channel_T *channel) { if (!channel_still_useful(channel)) @@ -374,6 +387,19 @@ channel_may_free(channel_T *channel) return FALSE; } +/* + * Decrement the reference count on "channel" and maybe free it when it goes + * down to zero. Don't free it if there is a pending action. + * Returns TRUE when the channel is no longer referenced. + */ + int +channel_unref(channel_T *channel) +{ + if (channel != NULL && --channel->ch_refcount <= 0) + return channel_may_free(channel); + return FALSE; +} + /* * Close a channel and free all its resources. */ @@ -839,6 +865,65 @@ channel_open( return channel; } +/* + * Implements ch_open(). + */ + channel_T * +channel_open_func(typval_T *argvars) +{ + char_u *address; + char_u *p; + char *rest; + int port; + jobopt_T opt; + channel_T *channel; + + address = get_tv_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN + && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)) + { + EMSG(_(e_invarg)); + return NULL; + } + + /* parse address */ + p = vim_strchr(address, ':'); + if (p == NULL) + { + EMSG2(_(e_invarg2), address); + return NULL; + } + *p++ = NUL; + port = strtol((char *)p, &rest, 10); + if (*address == NUL || port <= 0 || *rest != NUL) + { + p[-1] = ':'; + EMSG2(_(e_invarg2), address); + return NULL; + } + + /* parse options */ + clear_job_options(&opt); + opt.jo_mode = MODE_JSON; + opt.jo_timeout = 2000; + if (get_job_options(&argvars[1], &opt, + JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL) + return NULL; + if (opt.jo_timeout < 0) + { + EMSG(_(e_invarg)); + return NULL; + } + + channel = channel_open((char *)address, port, opt.jo_waittime, NULL); + if (channel != NULL) + { + opt.jo_set = JO_ALL; + channel_set_options(channel, &opt); + } + return channel; +} + static void may_close_part(sock_T *fd) { @@ -2326,6 +2411,62 @@ channel_read_json_block( return FAIL; } +/* + * Common for ch_read() and ch_readraw(). + */ + void +common_channel_read(typval_T *argvars, typval_T *rettv, int raw) +{ + channel_T *channel; + int part; + jobopt_T opt; + int mode; + int timeout; + int id = -1; + typval_T *listtv = NULL; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID) + == FAIL) + return; + + channel = get_channel_arg(&argvars[0], TRUE); + if (channel != NULL) + { + if (opt.jo_set & JO_PART) + part = opt.jo_part; + else + part = channel_part_read(channel); + mode = channel_get_mode(channel, part); + timeout = channel_get_timeout(channel, part); + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + + if (raw || mode == MODE_RAW || mode == MODE_NL) + rettv->vval.v_string = channel_read_block(channel, part, timeout); + else + { + if (opt.jo_set & JO_ID) + id = opt.jo_id; + channel_read_json_block(channel, part, timeout, id, &listtv); + if (listtv != NULL) + { + *rettv = *listtv; + vim_free(listtv); + } + else + { + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_NONE; + } + } + } +} + # if defined(WIN32) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) \ || defined(PROTO) /* @@ -2431,6 +2572,146 @@ channel_send(channel_T *channel, int part, char_u *buf, char *fun) return OK; } +/* + * Common for "ch_sendexpr()" and "ch_sendraw()". + * Returns the channel if the caller should read the response. + * Sets "part_read" to the the read fd. + * Otherwise returns NULL. + */ + channel_T * +send_common( + typval_T *argvars, + char_u *text, + int id, + int eval, + jobopt_T *opt, + char *fun, + int *part_read) +{ + channel_T *channel; + int part_send; + + channel = get_channel_arg(&argvars[0], TRUE); + if (channel == NULL) + return NULL; + part_send = channel_part_send(channel); + *part_read = channel_part_read(channel); + + clear_job_options(opt); + if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT) == FAIL) + return NULL; + + /* Set the callback. An empty callback means no callback and not reading + * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not + * allowed. */ + if (opt->jo_callback != NULL && *opt->jo_callback != NUL) + { + if (eval) + { + EMSG2(_("E917: Cannot use a callback with %s()"), fun); + return NULL; + } + channel_set_req_callback(channel, part_send, opt->jo_callback, id); + } + + if (channel_send(channel, part_send, text, fun) == OK + && opt->jo_callback == NULL) + return channel; + return NULL; +} + +/* + * common for "ch_evalexpr()" and "ch_sendexpr()" + */ + void +ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) +{ + char_u *text; + typval_T *listtv; + channel_T *channel; + int id; + ch_mode_T ch_mode; + int part_send; + int part_read; + jobopt_T opt; + int timeout; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + channel = get_channel_arg(&argvars[0], TRUE); + if (channel == NULL) + return; + part_send = channel_part_send(channel); + + ch_mode = channel_get_mode(channel, part_send); + if (ch_mode == MODE_RAW || ch_mode == MODE_NL) + { + EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel")); + return; + } + + id = channel_get_id(); + text = json_encode_nr_expr(id, &argvars[1], + ch_mode == MODE_JS ? JSON_JS : 0); + if (text == NULL) + return; + + channel = send_common(argvars, text, id, eval, &opt, + eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); + vim_free(text); + if (channel != NULL && eval) + { + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + else + timeout = channel_get_timeout(channel, part_read); + timeout = channel_get_timeout(channel, part_read); + if (channel_read_json_block(channel, part_read, timeout, id, &listtv) + == OK) + { + list_T *list = listtv->vval.v_list; + + /* Move the item from the list and then change the type to + * avoid the value being freed. */ + *rettv = list->lv_last->li_tv; + list->lv_last->li_tv.v_type = VAR_NUMBER; + free_tv(listtv); + } + } +} + +/* + * common for "ch_evalraw()" and "ch_sendraw()" + */ + void +ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) +{ + char_u buf[NUMBUFLEN]; + char_u *text; + channel_T *channel; + int part_read; + jobopt_T opt; + int timeout; + + /* return an empty string by default */ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + text = get_tv_string_buf(&argvars[1], buf); + channel = send_common(argvars, text, 0, eval, &opt, + eval ? "ch_evalraw" : "ch_sendraw", &part_read); + if (channel != NULL && eval) + { + if (opt.jo_set & JO_TIMEOUT) + timeout = opt.jo_timeout; + else + timeout = channel_get_timeout(channel, part_read); + rettv->vval.v_string = channel_read_block(channel, part_read, timeout); + } +} + # if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) /* * Add open channels to the poll struct. @@ -2714,4 +2995,826 @@ channel_get_timeout(channel_T *channel, int part) return channel->ch_part[part].ch_timeout; } +/* + * Get a callback from "arg". It can be a Funcref or a function name. + * When "arg" is zero return an empty string. + * Return NULL for an invalid argument. + */ + static char_u * +get_callback(typval_T *arg) +{ + if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) + return arg->vval.v_string; + if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) + return (char_u *)""; + EMSG(_("E999: Invalid callback argument")); + return NULL; +} + + static int +handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) +{ + char_u *val = get_tv_string(item); + + opt->jo_set |= jo; + if (STRCMP(val, "nl") == 0) + *modep = MODE_NL; + else if (STRCMP(val, "raw") == 0) + *modep = MODE_RAW; + else if (STRCMP(val, "js") == 0) + *modep = MODE_JS; + else if (STRCMP(val, "json") == 0) + *modep = MODE_JSON; + else + { + EMSG2(_(e_invarg2), val); + return FAIL; + } + return OK; +} + + static int +handle_io(typval_T *item, int part, jobopt_T *opt) +{ + char_u *val = get_tv_string(item); + + opt->jo_set |= JO_OUT_IO << (part - PART_OUT); + if (STRCMP(val, "null") == 0) + opt->jo_io[part] = JIO_NULL; + else if (STRCMP(val, "pipe") == 0) + opt->jo_io[part] = JIO_PIPE; + else if (STRCMP(val, "file") == 0) + opt->jo_io[part] = JIO_FILE; + else if (STRCMP(val, "buffer") == 0) + opt->jo_io[part] = JIO_BUFFER; + else if (STRCMP(val, "out") == 0 && part == PART_ERR) + opt->jo_io[part] = JIO_OUT; + else + { + EMSG2(_(e_invarg2), val); + return FAIL; + } + return OK; +} + + void +clear_job_options(jobopt_T *opt) +{ + vim_memset(opt, 0, sizeof(jobopt_T)); +} + +/* + * Get the PART_ number from the first character of an option name. + */ + static int +part_from_char(int c) +{ + return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR; +} + +/* + * Get the option entries from the dict in "tv", parse them and put the result + * in "opt". + * Only accept options in "supported". + * If an option value is invalid return FAIL. + */ + int +get_job_options(typval_T *tv, jobopt_T *opt, int supported) +{ + typval_T *item; + char_u *val; + dict_T *dict; + int todo; + hashitem_T *hi; + int part; + + opt->jo_set = 0; + if (tv->v_type == VAR_UNKNOWN) + return OK; + if (tv->v_type != VAR_DICT) + { + EMSG(_(e_invarg)); + return FAIL; + } + dict = tv->vval.v_dict; + if (dict == NULL) + return OK; + + todo = (int)dict->dv_hashtab.ht_used; + for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + item = &dict_lookup(hi)->di_tv; + + if (STRCMP(hi->hi_key, "mode") == 0) + { + if (!(supported & JO_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "in-mode") == 0) + { + if (!(supported & JO_IN_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "out-mode") == 0) + { + if (!(supported & JO_OUT_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "err-mode") == 0) + { + if (!(supported & JO_ERR_MODE)) + break; + if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE) + == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "in-io") == 0 + || STRCMP(hi->hi_key, "out-io") == 0 + || STRCMP(hi->hi_key, "err-io") == 0) + { + if (!(supported & JO_OUT_IO)) + break; + if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL) + return FAIL; + } + else if (STRCMP(hi->hi_key, "in-name") == 0 + || STRCMP(hi->hi_key, "out-name") == 0 + || STRCMP(hi->hi_key, "err-name") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_NAME << (part - PART_OUT); + opt->jo_io_name[part] = + get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]); + } + else if (STRCMP(hi->hi_key, "in-buf") == 0 + || STRCMP(hi->hi_key, "out-buf") == 0 + || STRCMP(hi->hi_key, "err-buf") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_BUF << (part - PART_OUT); + opt->jo_io_buf[part] = get_tv_number(item); + if (opt->jo_io_buf[part] <= 0) + { + EMSG2(_(e_invarg2), get_tv_string(item)); + return FAIL; + } + if (buflist_findnr(opt->jo_io_buf[part]) == NULL) + { + EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[part]); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "in-top") == 0 + || STRCMP(hi->hi_key, "in-bot") == 0) + { + linenr_T *lp; + + if (!(supported & JO_OUT_IO)) + break; + if (hi->hi_key[3] == 't') + { + lp = &opt->jo_in_top; + opt->jo_set |= JO_IN_TOP; + } + else + { + lp = &opt->jo_in_bot; + opt->jo_set |= JO_IN_BOT; + } + *lp = get_tv_number(item); + if (*lp < 0) + { + EMSG2(_(e_invarg2), get_tv_string(item)); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "channel") == 0) + { + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_CHANNEL; + if (item->v_type != VAR_CHANNEL) + { + EMSG2(_(e_invarg2), "channel"); + return FAIL; + } + opt->jo_channel = item->vval.v_channel; + } + else if (STRCMP(hi->hi_key, "callback") == 0) + { + if (!(supported & JO_CALLBACK)) + break; + opt->jo_set |= JO_CALLBACK; + opt->jo_callback = get_callback(item); + if (opt->jo_callback == NULL) + { + EMSG2(_(e_invarg2), "callback"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "out-cb") == 0) + { + if (!(supported & JO_OUT_CALLBACK)) + break; + opt->jo_set |= JO_OUT_CALLBACK; + opt->jo_out_cb = get_callback(item); + if (opt->jo_out_cb == NULL) + { + EMSG2(_(e_invarg2), "out-cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "err-cb") == 0) + { + if (!(supported & JO_ERR_CALLBACK)) + break; + opt->jo_set |= JO_ERR_CALLBACK; + opt->jo_err_cb = get_callback(item); + if (opt->jo_err_cb == NULL) + { + EMSG2(_(e_invarg2), "err-cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "close-cb") == 0) + { + if (!(supported & JO_CLOSE_CALLBACK)) + break; + opt->jo_set |= JO_CLOSE_CALLBACK; + opt->jo_close_cb = get_callback(item); + if (opt->jo_close_cb == NULL) + { + EMSG2(_(e_invarg2), "close-cb"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "waittime") == 0) + { + if (!(supported & JO_WAITTIME)) + break; + opt->jo_set |= JO_WAITTIME; + opt->jo_waittime = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "timeout") == 0) + { + if (!(supported & JO_TIMEOUT)) + break; + opt->jo_set |= JO_TIMEOUT; + opt->jo_timeout = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "out-timeout") == 0) + { + if (!(supported & JO_OUT_TIMEOUT)) + break; + opt->jo_set |= JO_OUT_TIMEOUT; + opt->jo_out_timeout = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "err-timeout") == 0) + { + if (!(supported & JO_ERR_TIMEOUT)) + break; + opt->jo_set |= JO_ERR_TIMEOUT; + opt->jo_err_timeout = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "part") == 0) + { + if (!(supported & JO_PART)) + break; + opt->jo_set |= JO_PART; + val = get_tv_string(item); + if (STRCMP(val, "err") == 0) + opt->jo_part = PART_ERR; + else + { + EMSG2(_(e_invarg2), val); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "id") == 0) + { + if (!(supported & JO_ID)) + break; + opt->jo_set |= JO_ID; + opt->jo_id = get_tv_number(item); + } + else if (STRCMP(hi->hi_key, "stoponexit") == 0) + { + if (!(supported & JO_STOPONEXIT)) + break; + opt->jo_set |= JO_STOPONEXIT; + opt->jo_stoponexit = get_tv_string_buf_chk(item, + opt->jo_soe_buf); + if (opt->jo_stoponexit == NULL) + { + EMSG2(_(e_invarg2), "stoponexit"); + return FAIL; + } + } + else if (STRCMP(hi->hi_key, "exit-cb") == 0) + { + if (!(supported & JO_EXIT_CB)) + break; + opt->jo_set |= JO_EXIT_CB; + opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf); + if (opt->jo_exit_cb == NULL) + { + EMSG2(_(e_invarg2), "exit-cb"); + return FAIL; + } + } + else + break; + --todo; + } + if (todo > 0) + { + EMSG2(_(e_invarg2), hi->hi_key); + return FAIL; + } + + return OK; +} + +/* + * Get the channel from the argument. + * Returns NULL if the handle is invalid. + */ + channel_T * +get_channel_arg(typval_T *tv, int check_open) +{ + channel_T *channel = NULL; + + if (tv->v_type == VAR_JOB) + { + if (tv->vval.v_job != NULL) + channel = tv->vval.v_job->jv_channel; + } + else if (tv->v_type == VAR_CHANNEL) + { + channel = tv->vval.v_channel; + } + else + { + EMSG2(_(e_invarg2), get_tv_string(tv)); + return NULL; + } + + if (check_open && (channel == NULL || !channel_is_open(channel))) + { + EMSG(_("E906: not an open channel")); + return NULL; + } + return channel; +} + +static job_T *first_job = NULL; + + static void +job_free(job_T *job) +{ + ch_log(job->jv_channel, "Freeing job"); + if (job->jv_channel != NULL) + { + /* The link from the channel to the job doesn't count as a reference, + * thus don't decrement the refcount of the job. The reference from + * the job to the channel does count the refrence, decrement it and + * NULL the reference. We don't set ch_job_killed, unreferencing the + * job doesn't mean it stops running. */ + job->jv_channel->ch_job = NULL; + channel_unref(job->jv_channel); + } + mch_clear_job(job); + + if (job->jv_next != NULL) + job->jv_next->jv_prev = job->jv_prev; + if (job->jv_prev == NULL) + first_job = job->jv_next; + else + job->jv_prev->jv_next = job->jv_next; + + vim_free(job->jv_stoponexit); + vim_free(job->jv_exit_cb); + vim_free(job); +} + + void +job_unref(job_T *job) +{ + if (job != NULL && --job->jv_refcount <= 0) + { + /* Do not free the job when it has not ended yet and there is a + * "stoponexit" flag or an exit callback. */ + if (job->jv_status != JOB_STARTED + || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL)) + { + job_free(job); + } + else if (job->jv_channel != NULL) + { + /* Do remove the link to the channel, otherwise it hangs + * around until Vim exits. See job_free() for refcount. */ + job->jv_channel->ch_job = NULL; + channel_unref(job->jv_channel); + job->jv_channel = NULL; + } + } +} + +/* + * Allocate a job. Sets the refcount to one and sets options default. + */ + static job_T * +job_alloc(void) +{ + job_T *job; + + job = (job_T *)alloc_clear(sizeof(job_T)); + if (job != NULL) + { + job->jv_refcount = 1; + job->jv_stoponexit = vim_strsave((char_u *)"term"); + + if (first_job != NULL) + { + first_job->jv_prev = job; + job->jv_next = first_job; + } + first_job = job; + } + return job; +} + + void +job_set_options(job_T *job, jobopt_T *opt) +{ + if (opt->jo_set & JO_STOPONEXIT) + { + vim_free(job->jv_stoponexit); + if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL) + job->jv_stoponexit = NULL; + else + job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); + } + if (opt->jo_set & JO_EXIT_CB) + { + vim_free(job->jv_exit_cb); + if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) + job->jv_exit_cb = NULL; + else + job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); + } +} + +/* + * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag. + */ + void +job_stop_on_exit() +{ + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL) + mch_stop_job(job, job->jv_stoponexit); +} + +/* + * Called once in a while: check if any jobs with an "exit-cb" have ended. + */ + void +job_check_ended(void) +{ + static time_t last_check = 0; + time_t now; + job_T *job; + job_T *next; + + /* Only do this once in 10 seconds. */ + now = time(NULL); + if (last_check + 10 < now) + { + last_check = now; + for (job = first_job; job != NULL; job = next) + { + next = job->jv_next; + if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL) + job_status(job); /* may free "job" */ + } + } +} + +/* + * "job_start()" function + */ + job_T * +job_start(typval_T *argvars) +{ + job_T *job; + char_u *cmd = NULL; +#if defined(UNIX) +# define USE_ARGV + char **argv = NULL; + int argc = 0; +#else + garray_T ga; +#endif + jobopt_T opt; + int part; + + job = job_alloc(); + if (job == NULL) + return NULL; + + job->jv_status = JOB_FAILED; + + /* Default mode is NL. */ + clear_job_options(&opt); + opt.jo_mode = MODE_NL; + if (get_job_options(&argvars[1], &opt, + JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL) + return job; + + /* Check that when io is "file" that there is a file name. */ + for (part = PART_OUT; part <= PART_IN; ++part) + if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT))) + && opt.jo_io[part] == JIO_FILE + && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT))) + || *opt.jo_io_name[part] == NUL)) + { + EMSG(_("E920: -io file requires -name to be set")); + return job; + } + + if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER) + { + buf_T *buf = NULL; + + /* check that we can find the buffer before starting the job */ + if (opt.jo_set & JO_IN_BUF) + { + buf = buflist_findnr(opt.jo_io_buf[PART_IN]); + if (buf == NULL) + EMSGN(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]); + } + else if (!(opt.jo_set & JO_IN_NAME)) + { + EMSG(_("E915: in-io buffer requires in-buf or in-name to be set")); + } + else + buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE); + if (buf == NULL) + return job; + if (buf->b_ml.ml_mfp == NULL) + { + char_u numbuf[NUMBUFLEN]; + char_u *s; + + if (opt.jo_set & JO_IN_BUF) + { + sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]); + s = numbuf; + } + else + s = opt.jo_io_name[PART_IN]; + EMSG2(_("E918: buffer must be loaded: %s"), s); + return job; + } + job->jv_in_buf = buf; + } + + job_set_options(job, &opt); + +#ifndef USE_ARGV + ga_init2(&ga, (int)sizeof(char*), 20); +#endif + + if (argvars[0].v_type == VAR_STRING) + { + /* Command is a string. */ + cmd = argvars[0].vval.v_string; +#ifdef USE_ARGV + if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) + return job; + argv[argc] = NULL; +#endif + } + else if (argvars[0].v_type != VAR_LIST + || argvars[0].vval.v_list == NULL + || argvars[0].vval.v_list->lv_len < 1) + { + EMSG(_(e_invarg)); + return job; + } + else + { + list_T *l = argvars[0].vval.v_list; + listitem_T *li; + char_u *s; + +#ifdef USE_ARGV + /* Pass argv[] to mch_call_shell(). */ + argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1)); + if (argv == NULL) + return job; +#endif + for (li = l->lv_first; li != NULL; li = li->li_next) + { + s = get_tv_string_chk(&li->li_tv); + if (s == NULL) + goto theend; +#ifdef USE_ARGV + argv[argc++] = (char *)s; +#else + /* Only escape when needed, double quotes are not always allowed. */ + if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL) + { +# ifdef WIN32 + int old_ssl = p_ssl; + + /* This is using CreateProcess, not cmd.exe. Always use + * double quote and backslashes. */ + p_ssl = 0; +# endif + s = vim_strsave_shellescape(s, FALSE, TRUE); +# ifdef WIN32 + p_ssl = old_ssl; +# endif + if (s == NULL) + goto theend; + ga_concat(&ga, s); + vim_free(s); + } + else + ga_concat(&ga, s); + if (li->li_next != NULL) + ga_append(&ga, ' '); +#endif + } +#ifdef USE_ARGV + argv[argc] = NULL; +#else + cmd = ga.ga_data; +#endif + } + +#ifdef USE_ARGV + if (ch_log_active()) + { + garray_T ga; + int i; + + ga_init2(&ga, (int)sizeof(char), 200); + for (i = 0; i < argc; ++i) + { + if (i > 0) + ga_concat(&ga, (char_u *)" "); + ga_concat(&ga, (char_u *)argv[i]); + } + ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data); + ga_clear(&ga); + } + mch_start_job(argv, job, &opt); +#else + ch_logs(NULL, "Starting job: %s", (char *)cmd); + mch_start_job((char *)cmd, job, &opt); +#endif + + /* If the channel is reading from a buffer, write lines now. */ + if (job->jv_channel != NULL) + channel_write_in(job->jv_channel); + +theend: +#ifdef USE_ARGV + vim_free(argv); +#else + vim_free(ga.ga_data); +#endif + return job; +} + +/* + * Get the status of "job" and invoke the exit callback when needed. + * The returned string is not allocated. + */ + char * +job_status(job_T *job) +{ + char *result; + + if (job->jv_status == JOB_ENDED) + /* No need to check, dead is dead. */ + result = "dead"; + else if (job->jv_status == JOB_FAILED) + result = "fail"; + else + { + result = mch_job_status(job); + if (job->jv_status == JOB_ENDED) + ch_log(job->jv_channel, "Job ended"); + if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL) + { + typval_T argv[3]; + typval_T rettv; + int dummy; + + /* invoke the exit callback; make sure the refcount is > 0 */ + ++job->jv_refcount; + argv[0].v_type = VAR_JOB; + argv[0].vval.v_job = job; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = job->jv_exitval; + call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); + clear_tv(&rettv); + --job->jv_refcount; + } + if (job->jv_status == JOB_ENDED && job->jv_refcount == 0) + { + /* The job was already unreferenced, now that it ended it can be + * freed. Careful: caller must not use "job" after this! */ + job_free(job); + } + } + return result; +} + +/* + * Implementation of job_info(). + */ + void +job_info(job_T *job, dict_T *dict) +{ + dictitem_T *item; + varnumber_T nr; + + dict_add_nr_str(dict, "status", 0L, (char_u *)job_status(job)); + + item = dictitem_alloc((char_u *)"channel"); + if (item == NULL) + return; + item->di_tv.v_lock = 0; + item->di_tv.v_type = VAR_CHANNEL; + item->di_tv.vval.v_channel = job->jv_channel; + if (job->jv_channel != NULL) + ++job->jv_channel->ch_refcount; + if (dict_add(dict, item) == FAIL) + dictitem_free(item); + +#ifdef UNIX + nr = job->jv_pid; +#else + nr = job->jv_proc_info.dwProcessId; +#endif + dict_add_nr_str(dict, "process", nr, NULL); + + dict_add_nr_str(dict, "exitval", job->jv_exitval, NULL); + dict_add_nr_str(dict, "exit-cb", 0L, job->jv_exit_cb); + dict_add_nr_str(dict, "stoponexit", 0L, job->jv_stoponexit); +} + + int +job_stop(job_T *job, typval_T *argvars) +{ + char_u *arg; + + if (argvars[1].v_type == VAR_UNKNOWN) + arg = (char_u *)""; + else + { + arg = get_tv_string_chk(&argvars[1]); + if (arg == NULL) + { + EMSG(_(e_invarg)); + return 0; + } + } + ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg); + if (mch_stop_job(job, arg) == FAIL) + return 0; + + /* Assume that "hup" does not kill the job. */ + if (job->jv_channel != NULL && STRCMP(arg, "hup") != 0) + job->jv_channel->ch_job_killed = TRUE; + + /* We don't try freeing the job, obviously the caller still has a + * reference to it. */ + return 1; +} + #endif /* FEAT_JOB_CHANNEL */ diff --git a/src/eval.c b/src/eval.c index 378d553b1b..5529ab2727 100644 --- a/src/eval.c +++ b/src/eval.c @@ -632,6 +632,7 @@ static void f_isnan(typval_T *argvars, typval_T *rettv); static void f_items(typval_T *argvars, typval_T *rettv); #ifdef FEAT_JOB_CHANNEL static void f_job_getchannel(typval_T *argvars, typval_T *rettv); +static void f_job_info(typval_T *argvars, typval_T *rettv); static void f_job_setoptions(typval_T *argvars, typval_T *rettv); static void f_job_start(typval_T *argvars, typval_T *rettv); static void f_job_stop(typval_T *argvars, typval_T *rettv); @@ -836,14 +837,11 @@ static int get_var_tv(char_u *name, int len, typval_T *rettv, dictitem_T **dip, static int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int verbose); static typval_T *alloc_string_tv(char_u *string); static void init_tv(typval_T *varp); -static long get_tv_number(typval_T *varp); #ifdef FEAT_FLOAT static float_T get_tv_float(typval_T *varp); #endif static linenr_T get_tv_lnum(typval_T *argvars); static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf); -static char_u *get_tv_string(typval_T *varp); -static char_u *get_tv_string_buf(typval_T *varp, char_u *buf); static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); static hashtab_T *find_var_ht(char_u *name, char_u **varname); @@ -7735,129 +7733,6 @@ failret: } #if defined(FEAT_JOB_CHANNEL) || defined(PROTO) -/* - * Decrement the reference count on "channel" and maybe free it when it goes - * down to zero. Don't free it if there is a pending action. - * Returns TRUE when the channel is no longer referenced. - */ - int -channel_unref(channel_T *channel) -{ - if (channel != NULL && --channel->ch_refcount <= 0) - return channel_may_free(channel); - return FALSE; -} - -static job_T *first_job = NULL; - - static void -job_free(job_T *job) -{ - ch_log(job->jv_channel, "Freeing job"); - if (job->jv_channel != NULL) - { - /* The link from the channel to the job doesn't count as a reference, - * thus don't decrement the refcount of the job. The reference from - * the job to the channel does count the refrence, decrement it and - * NULL the reference. We don't set ch_job_killed, unreferencing the - * job doesn't mean it stops running. */ - job->jv_channel->ch_job = NULL; - channel_unref(job->jv_channel); - } - mch_clear_job(job); - - if (job->jv_next != NULL) - job->jv_next->jv_prev = job->jv_prev; - if (job->jv_prev == NULL) - first_job = job->jv_next; - else - job->jv_prev->jv_next = job->jv_next; - - vim_free(job->jv_stoponexit); - vim_free(job->jv_exit_cb); - vim_free(job); -} - - static void -job_unref(job_T *job) -{ - if (job != NULL && --job->jv_refcount <= 0) - { - /* Do not free the job when it has not ended yet and there is a - * "stoponexit" flag or an exit callback. */ - if (job->jv_status != JOB_STARTED - || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL)) - { - job_free(job); - } - else if (job->jv_channel != NULL) - { - /* Do remove the link to the channel, otherwise it hangs - * around until Vim exits. See job_free() for refcount. */ - job->jv_channel->ch_job = NULL; - channel_unref(job->jv_channel); - job->jv_channel = NULL; - } - } -} - -/* - * Allocate a job. Sets the refcount to one and sets options default. - */ - static job_T * -job_alloc(void) -{ - job_T *job; - - job = (job_T *)alloc_clear(sizeof(job_T)); - if (job != NULL) - { - job->jv_refcount = 1; - job->jv_stoponexit = vim_strsave((char_u *)"term"); - - if (first_job != NULL) - { - first_job->jv_prev = job; - job->jv_next = first_job; - } - first_job = job; - } - return job; -} - - static void -job_set_options(job_T *job, jobopt_T *opt) -{ - if (opt->jo_set & JO_STOPONEXIT) - { - vim_free(job->jv_stoponexit); - if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL) - job->jv_stoponexit = NULL; - else - job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); - } - if (opt->jo_set & JO_EXIT_CB) - { - vim_free(job->jv_exit_cb); - if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) - job->jv_exit_cb = NULL; - else - job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); - } -} - -/* - * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag. - */ - void -job_stop_on_exit() -{ - job_T *job; - - for (job = first_job; job != NULL; job = job->jv_next) - if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL) - mch_stop_job(job, job->jv_stoponexit); -} #endif static char * @@ -8334,6 +8209,7 @@ static struct fst {"items", 1, 1, f_items}, #ifdef FEAT_JOB_CHANNEL {"job_getchannel", 1, 1, f_job_getchannel}, + {"job_info", 1, 1, f_job_info}, {"job_setoptions", 2, 2, f_job_setoptions}, {"job_start", 1, 2, f_job_start}, {"job_status", 1, 1, f_job_status}, @@ -9650,7 +9526,7 @@ f_bufloaded(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); } - static buf_T * + buf_T * buflist_find_by_name(char_u *name, int curtab_only) { int save_magic; @@ -9941,396 +9817,7 @@ f_ceil(typval_T *argvars, typval_T *rettv) } #endif -#if defined(FEAT_JOB_CHANNEL) -/* - * Get a callback from "arg". It can be a Funcref or a function name. - * When "arg" is zero return an empty string. - * Return NULL for an invalid argument. - */ - static char_u * -get_callback(typval_T *arg) -{ - if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) - return arg->vval.v_string; - if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) - return (char_u *)""; - EMSG(_("E999: Invalid callback argument")); - return NULL; -} - - static int -handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) -{ - char_u *val = get_tv_string(item); - - opt->jo_set |= jo; - if (STRCMP(val, "nl") == 0) - *modep = MODE_NL; - else if (STRCMP(val, "raw") == 0) - *modep = MODE_RAW; - else if (STRCMP(val, "js") == 0) - *modep = MODE_JS; - else if (STRCMP(val, "json") == 0) - *modep = MODE_JSON; - else - { - EMSG2(_(e_invarg2), val); - return FAIL; - } - return OK; -} - - static int -handle_io(typval_T *item, int part, jobopt_T *opt) -{ - char_u *val = get_tv_string(item); - - opt->jo_set |= JO_OUT_IO << (part - PART_OUT); - if (STRCMP(val, "null") == 0) - opt->jo_io[part] = JIO_NULL; - else if (STRCMP(val, "pipe") == 0) - opt->jo_io[part] = JIO_PIPE; - else if (STRCMP(val, "file") == 0) - opt->jo_io[part] = JIO_FILE; - else if (STRCMP(val, "buffer") == 0) - opt->jo_io[part] = JIO_BUFFER; - else if (STRCMP(val, "out") == 0 && part == PART_ERR) - opt->jo_io[part] = JIO_OUT; - else - { - EMSG2(_(e_invarg2), val); - return FAIL; - } - return OK; -} - - static void -clear_job_options(jobopt_T *opt) -{ - vim_memset(opt, 0, sizeof(jobopt_T)); -} - -/* - * Get the PART_ number from the first character of an option name. - */ - static int -part_from_char(int c) -{ - return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR; -} - -/* - * Get the option entries from the dict in "tv", parse them and put the result - * in "opt". - * Only accept options in "supported". - * If an option value is invalid return FAIL. - */ - static int -get_job_options(typval_T *tv, jobopt_T *opt, int supported) -{ - typval_T *item; - char_u *val; - dict_T *dict; - int todo; - hashitem_T *hi; - int part; - - opt->jo_set = 0; - if (tv->v_type == VAR_UNKNOWN) - return OK; - if (tv->v_type != VAR_DICT) - { - EMSG(_(e_invarg)); - return FAIL; - } - dict = tv->vval.v_dict; - if (dict == NULL) - return OK; - - todo = (int)dict->dv_hashtab.ht_used; - for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi) - if (!HASHITEM_EMPTY(hi)) - { - item = &HI2DI(hi)->di_tv; - - if (STRCMP(hi->hi_key, "mode") == 0) - { - if (!(supported & JO_MODE)) - break; - if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL) - return FAIL; - } - else if (STRCMP(hi->hi_key, "in-mode") == 0) - { - if (!(supported & JO_IN_MODE)) - break; - if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE) - == FAIL) - return FAIL; - } - else if (STRCMP(hi->hi_key, "out-mode") == 0) - { - if (!(supported & JO_OUT_MODE)) - break; - if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE) - == FAIL) - return FAIL; - } - else if (STRCMP(hi->hi_key, "err-mode") == 0) - { - if (!(supported & JO_ERR_MODE)) - break; - if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE) - == FAIL) - return FAIL; - } - else if (STRCMP(hi->hi_key, "in-io") == 0 - || STRCMP(hi->hi_key, "out-io") == 0 - || STRCMP(hi->hi_key, "err-io") == 0) - { - if (!(supported & JO_OUT_IO)) - break; - if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL) - return FAIL; - } - else if (STRCMP(hi->hi_key, "in-name") == 0 - || STRCMP(hi->hi_key, "out-name") == 0 - || STRCMP(hi->hi_key, "err-name") == 0) - { - part = part_from_char(*hi->hi_key); - - if (!(supported & JO_OUT_IO)) - break; - opt->jo_set |= JO_OUT_NAME << (part - PART_OUT); - opt->jo_io_name[part] = - get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]); - } - else if (STRCMP(hi->hi_key, "in-buf") == 0 - || STRCMP(hi->hi_key, "out-buf") == 0 - || STRCMP(hi->hi_key, "err-buf") == 0) - { - part = part_from_char(*hi->hi_key); - - if (!(supported & JO_OUT_IO)) - break; - opt->jo_set |= JO_OUT_BUF << (part - PART_OUT); - opt->jo_io_buf[part] = get_tv_number(item); - if (opt->jo_io_buf[part] <= 0) - { - EMSG2(_(e_invarg2), get_tv_string(item)); - return FAIL; - } - if (buflist_findnr(opt->jo_io_buf[part]) == NULL) - { - EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[part]); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "in-top") == 0 - || STRCMP(hi->hi_key, "in-bot") == 0) - { - linenr_T *lp; - - if (!(supported & JO_OUT_IO)) - break; - if (hi->hi_key[3] == 't') - { - lp = &opt->jo_in_top; - opt->jo_set |= JO_IN_TOP; - } - else - { - lp = &opt->jo_in_bot; - opt->jo_set |= JO_IN_BOT; - } - *lp = get_tv_number(item); - if (*lp < 0) - { - EMSG2(_(e_invarg2), get_tv_string(item)); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "channel") == 0) - { - if (!(supported & JO_OUT_IO)) - break; - opt->jo_set |= JO_CHANNEL; - if (item->v_type != VAR_CHANNEL) - { - EMSG2(_(e_invarg2), "channel"); - return FAIL; - } - opt->jo_channel = item->vval.v_channel; - } - else if (STRCMP(hi->hi_key, "callback") == 0) - { - if (!(supported & JO_CALLBACK)) - break; - opt->jo_set |= JO_CALLBACK; - opt->jo_callback = get_callback(item); - if (opt->jo_callback == NULL) - { - EMSG2(_(e_invarg2), "callback"); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "out-cb") == 0) - { - if (!(supported & JO_OUT_CALLBACK)) - break; - opt->jo_set |= JO_OUT_CALLBACK; - opt->jo_out_cb = get_callback(item); - if (opt->jo_out_cb == NULL) - { - EMSG2(_(e_invarg2), "out-cb"); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "err-cb") == 0) - { - if (!(supported & JO_ERR_CALLBACK)) - break; - opt->jo_set |= JO_ERR_CALLBACK; - opt->jo_err_cb = get_callback(item); - if (opt->jo_err_cb == NULL) - { - EMSG2(_(e_invarg2), "err-cb"); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "close-cb") == 0) - { - if (!(supported & JO_CLOSE_CALLBACK)) - break; - opt->jo_set |= JO_CLOSE_CALLBACK; - opt->jo_close_cb = get_callback(item); - if (opt->jo_close_cb == NULL) - { - EMSG2(_(e_invarg2), "close-cb"); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "waittime") == 0) - { - if (!(supported & JO_WAITTIME)) - break; - opt->jo_set |= JO_WAITTIME; - opt->jo_waittime = get_tv_number(item); - } - else if (STRCMP(hi->hi_key, "timeout") == 0) - { - if (!(supported & JO_TIMEOUT)) - break; - opt->jo_set |= JO_TIMEOUT; - opt->jo_timeout = get_tv_number(item); - } - else if (STRCMP(hi->hi_key, "out-timeout") == 0) - { - if (!(supported & JO_OUT_TIMEOUT)) - break; - opt->jo_set |= JO_OUT_TIMEOUT; - opt->jo_out_timeout = get_tv_number(item); - } - else if (STRCMP(hi->hi_key, "err-timeout") == 0) - { - if (!(supported & JO_ERR_TIMEOUT)) - break; - opt->jo_set |= JO_ERR_TIMEOUT; - opt->jo_err_timeout = get_tv_number(item); - } - else if (STRCMP(hi->hi_key, "part") == 0) - { - if (!(supported & JO_PART)) - break; - opt->jo_set |= JO_PART; - val = get_tv_string(item); - if (STRCMP(val, "err") == 0) - opt->jo_part = PART_ERR; - else - { - EMSG2(_(e_invarg2), val); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "id") == 0) - { - if (!(supported & JO_ID)) - break; - opt->jo_set |= JO_ID; - opt->jo_id = get_tv_number(item); - } - else if (STRCMP(hi->hi_key, "stoponexit") == 0) - { - if (!(supported & JO_STOPONEXIT)) - break; - opt->jo_set |= JO_STOPONEXIT; - opt->jo_stoponexit = get_tv_string_buf_chk(item, - opt->jo_soe_buf); - if (opt->jo_stoponexit == NULL) - { - EMSG2(_(e_invarg2), "stoponexit"); - return FAIL; - } - } - else if (STRCMP(hi->hi_key, "exit-cb") == 0) - { - if (!(supported & JO_EXIT_CB)) - break; - opt->jo_set |= JO_EXIT_CB; - opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf); - if (opt->jo_exit_cb == NULL) - { - EMSG2(_(e_invarg2), "exit-cb"); - return FAIL; - } - } - else - break; - --todo; - } - if (todo > 0) - { - EMSG2(_(e_invarg2), hi->hi_key); - return FAIL; - } - - return OK; -} -#endif - #ifdef FEAT_JOB_CHANNEL -/* - * Get the channel from the argument. - * Returns NULL if the handle is invalid. - */ - static channel_T * -get_channel_arg(typval_T *tv, int check_open) -{ - channel_T *channel = NULL; - - if (tv->v_type == VAR_JOB) - { - if (tv->vval.v_job != NULL) - channel = tv->vval.v_job->jv_channel; - } - else if (tv->v_type == VAR_CHANNEL) - { - channel = tv->vval.v_channel; - } - else - { - EMSG2(_(e_invarg2), get_tv_string(tv)); - return NULL; - } - - if (check_open && (channel == NULL || !channel_is_open(channel))) - { - EMSG(_("E906: not an open channel")); - return NULL; - } - return channel; -} - /* * "ch_close()" function */ @@ -10414,21 +9901,11 @@ f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED) char_u *fname; char_u *opt = (char_u *)""; char_u buf[NUMBUFLEN]; - FILE *file = NULL; fname = get_tv_string(&argvars[0]); if (argvars[1].v_type == VAR_STRING) opt = get_tv_string_buf(&argvars[1], buf); - if (*fname != NUL) - { - file = fopen((char *)fname, *opt == 'w' ? "w" : "a"); - if (file == NULL) - { - EMSG2(_(e_notopen), fname); - return; - } - } - ch_logfile(file); + ch_logfile(fname, opt); } /* @@ -10437,117 +9914,8 @@ f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED) static void f_ch_open(typval_T *argvars, typval_T *rettv) { - char_u *address; - char_u *p; - char *rest; - int port; - jobopt_T opt; - channel_T *channel; - - /* default: fail */ rettv->v_type = VAR_CHANNEL; - rettv->vval.v_channel = NULL; - - address = get_tv_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN - && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)) - { - EMSG(_(e_invarg)); - return; - } - - /* parse address */ - p = vim_strchr(address, ':'); - if (p == NULL) - { - EMSG2(_(e_invarg2), address); - return; - } - *p++ = NUL; - port = strtol((char *)p, &rest, 10); - if (*address == NUL || port <= 0 || *rest != NUL) - { - p[-1] = ':'; - EMSG2(_(e_invarg2), address); - return; - } - - /* parse options */ - clear_job_options(&opt); - opt.jo_mode = MODE_JSON; - opt.jo_timeout = 2000; - if (get_job_options(&argvars[1], &opt, - JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL) - return; - if (opt.jo_timeout < 0) - { - EMSG(_(e_invarg)); - return; - } - - channel = channel_open((char *)address, port, opt.jo_waittime, NULL); - if (channel != NULL) - { - rettv->vval.v_channel = channel; - opt.jo_set = JO_ALL; - channel_set_options(channel, &opt); - } -} - -/* - * Common for ch_read() and ch_readraw(). - */ - static void -common_channel_read(typval_T *argvars, typval_T *rettv, int raw) -{ - channel_T *channel; - int part; - jobopt_T opt; - int mode; - int timeout; - int id = -1; - typval_T *listtv = NULL; - - /* return an empty string by default */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - clear_job_options(&opt); - if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID) - == FAIL) - return; - - channel = get_channel_arg(&argvars[0], TRUE); - if (channel != NULL) - { - if (opt.jo_set & JO_PART) - part = opt.jo_part; - else - part = channel_part_read(channel); - mode = channel_get_mode(channel, part); - timeout = channel_get_timeout(channel, part); - if (opt.jo_set & JO_TIMEOUT) - timeout = opt.jo_timeout; - - if (raw || mode == MODE_RAW || mode == MODE_NL) - rettv->vval.v_string = channel_read_block(channel, part, timeout); - else - { - if (opt.jo_set & JO_ID) - id = opt.jo_id; - channel_read_json_block(channel, part, timeout, id, &listtv); - if (listtv != NULL) - { - *rettv = *listtv; - vim_free(listtv); - } - else - { - rettv->v_type = VAR_SPECIAL; - rettv->vval.v_number = VVAL_NONE; - } - } - } + rettv->vval.v_channel = channel_open_func(argvars); } /* @@ -10568,116 +9936,6 @@ f_ch_readraw(typval_T *argvars, typval_T *rettv) common_channel_read(argvars, rettv, TRUE); } -/* - * common for "sendexpr()" and "sendraw()" - * Returns the channel if the caller should read the response. - * Sets "part_read" to the the read fd. - * Otherwise returns NULL. - */ - static channel_T * -send_common( - typval_T *argvars, - char_u *text, - int id, - int eval, - jobopt_T *opt, - char *fun, - int *part_read) -{ - channel_T *channel; - int part_send; - - channel = get_channel_arg(&argvars[0], TRUE); - if (channel == NULL) - return NULL; - part_send = channel_part_send(channel); - *part_read = channel_part_read(channel); - - clear_job_options(opt); - if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT) == FAIL) - return NULL; - - /* Set the callback. An empty callback means no callback and not reading - * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not - * allowed. */ - if (opt->jo_callback != NULL && *opt->jo_callback != NUL) - { - if (eval) - { - EMSG2(_("E917: Cannot use a callback with %s()"), fun); - return NULL; - } - channel_set_req_callback(channel, part_send, opt->jo_callback, id); - } - - if (channel_send(channel, part_send, text, fun) == OK - && opt->jo_callback == NULL) - return channel; - return NULL; -} - -/* - * common for "ch_evalexpr()" and "ch_sendexpr()" - */ - static void -ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) -{ - char_u *text; - typval_T *listtv; - channel_T *channel; - int id; - ch_mode_T ch_mode; - int part_send; - int part_read; - jobopt_T opt; - int timeout; - - /* return an empty string by default */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - channel = get_channel_arg(&argvars[0], TRUE); - if (channel == NULL) - return; - part_send = channel_part_send(channel); - - ch_mode = channel_get_mode(channel, part_send); - if (ch_mode == MODE_RAW || ch_mode == MODE_NL) - { - EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel")); - return; - } - - id = channel_get_id(); - text = json_encode_nr_expr(id, &argvars[1], - ch_mode == MODE_JS ? JSON_JS : 0); - if (text == NULL) - return; - - channel = send_common(argvars, text, id, eval, &opt, - eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); - vim_free(text); - if (channel != NULL && eval) - { - if (opt.jo_set & JO_TIMEOUT) - timeout = opt.jo_timeout; - else - timeout = channel_get_timeout(channel, part_read); - timeout = channel_get_timeout(channel, part_read); - if (channel_read_json_block(channel, part_read, timeout, id, &listtv) - == OK) - { - list_T *list = listtv->vval.v_list; - - /* Move the item from the list and then change the type to - * avoid the value being freed. */ - *rettv = list->lv_last->li_tv; - list->lv_last->li_tv.v_type = VAR_NUMBER; - free_tv(listtv); - } - } -} - /* * "ch_evalexpr()" function */ @@ -10696,36 +9954,6 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv) ch_expr_common(argvars, rettv, FALSE); } -/* - * common for "ch_evalraw()" and "ch_sendraw()" - */ - static void -ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) -{ - char_u buf[NUMBUFLEN]; - char_u *text; - channel_T *channel; - int part_read; - jobopt_T opt; - int timeout; - - /* return an empty string by default */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - text = get_tv_string_buf(&argvars[1], buf); - channel = send_common(argvars, text, 0, eval, &opt, - eval ? "ch_evalraw" : "ch_sendraw", &part_read); - if (channel != NULL && eval) - { - if (opt.jo_set & JO_TIMEOUT) - timeout = opt.jo_timeout; - else - timeout = channel_get_timeout(channel, part_read); - rettv->vval.v_string = channel_read_block(channel, part_read, timeout); - } -} - /* * "ch_evalraw()" function */ @@ -15134,6 +14362,18 @@ f_job_getchannel(typval_T *argvars, typval_T *rettv) } } +/* + * "job_info()" function + */ + static void +f_job_info(typval_T *argvars, typval_T *rettv) +{ + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL && rettv_dict_alloc(rettv) != FAIL) + job_info(job, rettv->vval.v_dict); +} + /* * "job_setoptions()" function */ @@ -15157,250 +14397,8 @@ f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED) static void f_job_start(typval_T *argvars, typval_T *rettv) { - job_T *job; - char_u *cmd = NULL; -#if defined(UNIX) -# define USE_ARGV - char **argv = NULL; - int argc = 0; -#else - garray_T ga; -#endif - jobopt_T opt; - int part; - rettv->v_type = VAR_JOB; - job = job_alloc(); - rettv->vval.v_job = job; - if (job == NULL) - return; - - rettv->vval.v_job->jv_status = JOB_FAILED; - - /* Default mode is NL. */ - clear_job_options(&opt); - opt.jo_mode = MODE_NL; - if (get_job_options(&argvars[1], &opt, - JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL - + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL) - return; - - /* Check that when io is "file" that there is a file name. */ - for (part = PART_OUT; part <= PART_IN; ++part) - if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT))) - && opt.jo_io[part] == JIO_FILE - && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT))) - || *opt.jo_io_name[part] == NUL)) - { - EMSG(_("E920: -io file requires -name to be set")); - return; - } - - if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER) - { - buf_T *buf = NULL; - - /* check that we can find the buffer before starting the job */ - if (opt.jo_set & JO_IN_BUF) - { - buf = buflist_findnr(opt.jo_io_buf[PART_IN]); - if (buf == NULL) - EMSGN(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]); - } - else if (!(opt.jo_set & JO_IN_NAME)) - { - EMSG(_("E915: in-io buffer requires in-buf or in-name to be set")); - } - else - buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE); - if (buf == NULL) - return; - if (buf->b_ml.ml_mfp == NULL) - { - char_u numbuf[NUMBUFLEN]; - char_u *s; - - if (opt.jo_set & JO_IN_BUF) - { - sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]); - s = numbuf; - } - else - s = opt.jo_io_name[PART_IN]; - EMSG2(_("E918: buffer must be loaded: %s"), s); - return; - } - job->jv_in_buf = buf; - } - - job_set_options(job, &opt); - -#ifndef USE_ARGV - ga_init2(&ga, (int)sizeof(char*), 20); -#endif - - if (argvars[0].v_type == VAR_STRING) - { - /* Command is a string. */ - cmd = argvars[0].vval.v_string; -#ifdef USE_ARGV - if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) - return; - argv[argc] = NULL; -#endif - } - else if (argvars[0].v_type != VAR_LIST - || argvars[0].vval.v_list == NULL - || argvars[0].vval.v_list->lv_len < 1) - { - EMSG(_(e_invarg)); - return; - } - else - { - list_T *l = argvars[0].vval.v_list; - listitem_T *li; - char_u *s; - -#ifdef USE_ARGV - /* Pass argv[] to mch_call_shell(). */ - argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1)); - if (argv == NULL) - return; -#endif - for (li = l->lv_first; li != NULL; li = li->li_next) - { - s = get_tv_string_chk(&li->li_tv); - if (s == NULL) - goto theend; -#ifdef USE_ARGV - argv[argc++] = (char *)s; -#else - /* Only escape when needed, double quotes are not always allowed. */ - if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL) - { - s = vim_strsave_shellescape(s, FALSE, TRUE); - if (s == NULL) - goto theend; - ga_concat(&ga, s); - vim_free(s); - } - else - ga_concat(&ga, s); - if (li->li_next != NULL) - ga_append(&ga, ' '); -#endif - } -#ifdef USE_ARGV - argv[argc] = NULL; -#else - cmd = ga.ga_data; -#endif - } - -#ifdef USE_ARGV - if (ch_log_active()) - { - garray_T ga; - int i; - - ga_init2(&ga, (int)sizeof(char), 200); - for (i = 0; i < argc; ++i) - { - if (i > 0) - ga_concat(&ga, (char_u *)" "); - ga_concat(&ga, (char_u *)argv[i]); - } - ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data); - ga_clear(&ga); - } - mch_start_job(argv, job, &opt); -#else - ch_logs(NULL, "Starting job: %s", (char *)cmd); - mch_start_job((char *)cmd, job, &opt); -#endif - - /* If the channel is reading from a buffer, write lines now. */ - if (job->jv_channel != NULL) - channel_write_in(job->jv_channel); - -theend: -#ifdef USE_ARGV - vim_free(argv); -#else - vim_free(ga.ga_data); -#endif -} - -/* - * Get the status of "job" and invoke the exit callback when needed. - * The returned string is not allocated. - */ - static char * -job_status(job_T *job) -{ - char *result; - - if (job->jv_status == JOB_ENDED) - /* No need to check, dead is dead. */ - result = "dead"; - else if (job->jv_status == JOB_FAILED) - result = "fail"; - else - { - result = mch_job_status(job); - if (job->jv_status == JOB_ENDED) - ch_log(job->jv_channel, "Job ended"); - if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL) - { - typval_T argv[3]; - typval_T rettv; - int dummy; - - /* invoke the exit callback; make sure the refcount is > 0 */ - ++job->jv_refcount; - argv[0].v_type = VAR_JOB; - argv[0].vval.v_job = job; - argv[1].v_type = VAR_NUMBER; - argv[1].vval.v_number = job->jv_exitval; - call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); - clear_tv(&rettv); - --job->jv_refcount; - } - if (job->jv_status == JOB_ENDED && job->jv_refcount == 0) - { - /* The job was already unreferenced, now that it ended it can be - * freed. Careful: caller must not use "job" after this! */ - job_free(job); - } - } - return result; -} - -/* - * Called once in a while: check if any jobs with an "exit-cb" have ended. - */ - void -job_check_ended(void) -{ - static time_t last_check = 0; - time_t now; - job_T *job; - job_T *next; - - /* Only do this once in 10 seconds. */ - now = time(NULL); - if (last_check + 10 < now) - { - last_check = now; - for (job = first_job; job != NULL; job = next) - { - next = job->jv_next; - if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL) - job_status(job); /* may free "job" */ - } - } + rettv->vval.v_job = job_start(argvars); } /* @@ -15410,13 +14408,11 @@ job_check_ended(void) f_job_status(typval_T *argvars, typval_T *rettv) { job_T *job = get_job_arg(&argvars[0]); - char *result; if (job != NULL) { - result = job_status(job); rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave((char_u *)result); + rettv->vval.v_string = vim_strsave((char_u *)job_status(job)); } } @@ -15424,38 +14420,12 @@ f_job_status(typval_T *argvars, typval_T *rettv) * "job_stop()" function */ static void -f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +f_job_stop(typval_T *argvars, typval_T *rettv) { job_T *job = get_job_arg(&argvars[0]); if (job != NULL) - { - char_u *arg; - - if (argvars[1].v_type == VAR_UNKNOWN) - arg = (char_u *)""; - else - { - arg = get_tv_string_chk(&argvars[1]); - if (arg == NULL) - { - EMSG(_(e_invarg)); - return; - } - } - ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg); - if (mch_stop_job(job, arg) == FAIL) - rettv->vval.v_number = 0; - else - { - rettv->vval.v_number = 1; - /* Assume that "hup" does not kill the job. */ - if (job->jv_channel != NULL && STRCMP(arg, "hup") != 0) - job->jv_channel->ch_job_killed = TRUE; - } - /* We don't try freeing the job, obviously the caller still has a - * reference to it. */ - } + rettv->vval.v_number = job_stop(job, argvars); } #endif @@ -22502,7 +21472,7 @@ init_tv(typval_T *varp) * caller of incompatible types: it sets *denote to TRUE if "denote" * is not NULL or returns -1 otherwise. */ - static long + long get_tv_number(typval_T *varp) { int error = FALSE; @@ -22653,7 +21623,7 @@ get_tv_lnum_buf(typval_T *argvars, buf_T *buf) * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return * NULL on error. */ - static char_u * + char_u * get_tv_string(typval_T *varp) { static char_u mybuf[NUMBUFLEN]; @@ -22661,7 +21631,7 @@ get_tv_string(typval_T *varp) return get_tv_string_buf(varp, mybuf); } - static char_u * + char_u * get_tv_string_buf(typval_T *varp, char_u *buf) { char_u *res = get_tv_string_buf_chk(varp, buf); diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 359bf64fe9..aa4137ac5f 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -636,7 +636,10 @@ gui_gtk3_update_cursor(cairo_t *cr) if (gui.row == gui.cursor_row) { gui.by_signal = TRUE; - gui_update_cursor(TRUE, TRUE); + if (State & CMDLINE) + gui_update_cursor(TRUE, FALSE); + else + gui_update_cursor(TRUE, TRUE); gui.by_signal = FALSE; cairo_paint(cr); } @@ -647,7 +650,8 @@ gui_gtk3_should_draw_cursor(void) { unsigned int cond = 0; cond |= gui_gtk_is_blink_on(); - cond |= is_key_pressed; + if (gui.cursor_col >= gui.col) + cond |= is_key_pressed; cond |= gui.in_focus == FALSE; return cond; } @@ -683,17 +687,29 @@ draw_event(GtkWidget *widget, if (blink_mode) gui_gtk3_redraw(rect.x, rect.y, rect.width, rect.height); else - gui_redraw(rect.x, rect.y, rect.width, rect.height); + { + if (get_real_state() & VISUAL) + gui_gtk3_redraw(rect.x, rect.y, + rect.width, rect.height); + else + gui_redraw(rect.x, rect.y, rect.width, rect.height); + } } } cairo_rectangle_list_destroy(list); + if (get_real_state() & VISUAL) + { + if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col) + gui_update_cursor(TRUE, TRUE); + } + cairo_paint(cr); } gui.by_signal = FALSE; /* Add the cursor to the window if necessary.*/ - if (gui_gtk3_should_draw_cursor()) + if (gui_gtk3_should_draw_cursor() && blink_mode) gui_gtk3_update_cursor(cr); return FALSE; @@ -6310,8 +6326,25 @@ gui_mch_flash(int msec) gui_mch_invert_rectangle(int r, int c, int nr, int nc) { #if GTK_CHECK_VERSION(3,0,0) - /* TODO Replace GdkGC with Cairo */ - (void)r; (void)c; (void)nr; (void)nc; + const GdkRectangle rect = { + FILL_X(c), FILL_Y(r), nc * gui.char_width, nr * gui.char_height + }; + cairo_t * const cr = cairo_create(gui.surface); + + set_cairo_source_rgb_from_pixel(cr, gui.norm_pixel ^ gui.back_pixel); +# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,9,2) + cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); +# else + /* Give an implementation for older cairo versions if necessary. */ +# endif + gdk_cairo_rectangle(cr, &rect); + cairo_fill(cr); + + cairo_destroy(cr); + + if (!gui.by_signal) + gtk_widget_queue_draw_area(gui.drawarea, rect.x, rect.y, + rect.width, rect.height); #else GdkGCValues values; GdkGC *invert_gc; diff --git a/src/proto/channel.pro b/src/proto/channel.pro index bb7bab31c0..eb50b2f3bd 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -1,13 +1,14 @@ /* channel.c */ -void ch_logfile(FILE *file); +void ch_logfile(char_u *fname, char_u *opt); int ch_log_active(void); void ch_log(channel_T *ch, char *msg); void ch_logs(channel_T *ch, char *msg, char *name); channel_T *add_channel(void); -int channel_may_free(channel_T *channel); +int channel_unref(channel_T *channel); void channel_free(channel_T *channel); void channel_gui_register_all(void); channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_close_cb)(void)); +channel_T *channel_open_func(typval_T *argvars); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_set_options(channel_T *channel, jobopt_T *opt); @@ -27,9 +28,13 @@ int channel_get_id(void); void channel_read(channel_T *channel, int part, char *func); char_u *channel_read_block(channel_T *channel, int part, int timeout); int channel_read_json_block(channel_T *channel, int part, int timeout, int id, typval_T **rettv); +void common_channel_read(typval_T *argvars, typval_T *rettv, int raw); channel_T *channel_fd2channel(sock_T fd, int *partp); void channel_handle_events(void); int channel_send(channel_T *channel, int part, char_u *buf, char *fun); +channel_T *send_common(typval_T *argvars, char_u *text, int id, int eval, jobopt_T *opt, char *fun, int *part_read); +void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval); +void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval); int channel_poll_setup(int nfd_in, void *fds_in); int channel_poll_check(int ret_in, void *fds_in); int channel_select_setup(int maxfd_in, void *rfds_in); @@ -40,4 +45,15 @@ int channel_part_send(channel_T *channel); int channel_part_read(channel_T *channel); ch_mode_T channel_get_mode(channel_T *channel, int part); int channel_get_timeout(channel_T *channel, int part); +void clear_job_options(jobopt_T *opt); +int get_job_options(typval_T *tv, jobopt_T *opt, int supported); +channel_T *get_channel_arg(typval_T *tv, int check_open); +void job_unref(job_T *job); +void job_set_options(job_T *job, jobopt_T *opt); +void job_stop_on_exit(void); +void job_check_ended(void); +job_T *job_start(typval_T *argvars); +char *job_status(job_T *job); +void job_info(job_T *job, dict_T *dict); +int job_stop(job_T *job, typval_T *argvars); /* vim: set ft=c : */ diff --git a/src/proto/eval.pro b/src/proto/eval.pro index a511b813ca..f5e0d19504 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -79,15 +79,13 @@ int dict_add_list(dict_T *d, char *key, list_T *list); dictitem_T *dict_find(dict_T *d, char_u *key, int len); char_u *get_dict_string(dict_T *d, char_u *key, int save); long get_dict_number(dict_T *d, char_u *key); -int channel_unref(channel_T *channel); -void job_stop_on_exit(void); int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict); +buf_T *buflist_find_by_name(char_u *name, int curtab_only); int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); -void job_check_ended(void); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); @@ -107,7 +105,10 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); typval_T *alloc_tv(void); void free_tv(typval_T *varp); void clear_tv(typval_T *varp); +long get_tv_number(typval_T *varp); long get_tv_number_chk(typval_T *varp, int *denote); +char_u *get_tv_string(typval_T *varp); +char_u *get_tv_string_buf(typval_T *varp, char_u *buf); char_u *get_tv_string_chk(typval_T *varp); char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf); char_u *get_var_value(char_u *name); diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 5f2b97f70f..c50c1df42c 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -442,10 +442,11 @@ func Test_connect_waittime() " Oops, port does exists. call ch_close(handle) else - " Failed connection should wait about 500 msec. + " Failed connection should wait about 500 msec. Can be longer if the + " computer is busy with other things. let elapsed = reltime(start) call assert_true(reltimefloat(elapsed) > 0.3) - call assert_true(reltimefloat(elapsed) < 1.0) + call assert_true(reltimefloat(elapsed) < 1.5) endif catch if v:exception !~ 'Connection reset by peer' @@ -478,6 +479,12 @@ func Test_raw_pipe() finally call job_stop(job) endtry + + let s:job = job + call s:waitFor('"dead" == job_status(s:job)') + let info = job_info(job) + call assert_equal("dead", info.status) + call assert_equal("term", info.stoponexit) endfunc func Test_nl_pipe() @@ -485,7 +492,7 @@ func Test_nl_pipe() return endif call ch_log('Test_nl_pipe()') - let job = job_start(s:python . " test_channel_pipe.py") + let job = job_start([s:python, "test_channel_pipe.py"]) call assert_equal("run", job_status(job)) try let handle = job_getchannel(job) @@ -510,6 +517,7 @@ func Test_nl_err_to_out_pipe() if !has('job') return endif + call ch_logfile('Xlog') call ch_log('Test_nl_err_to_out_pipe()') let job = job_start(s:python . " test_channel_pipe.py", {'err-io': 'out'}) call assert_equal("run", job_status(job)) @@ -522,6 +530,32 @@ func Test_nl_err_to_out_pipe() call assert_equal("wrong", ch_readraw(handle)) finally call job_stop(job) + call ch_logfile('') + let loglines = readfile('Xlog') + call assert_true(len(loglines) > 10) + let found_test = 0 + let found_send = 0 + let found_recv = 0 + let found_stop = 0 + for l in loglines + if l =~ 'Test_nl_err_to_out_pipe' + let found_test = 1 + endif + if l =~ 'SEND on.*echo something' + let found_send = 1 + endif + if l =~ 'RECV on.*something' + let found_recv = 1 + endif + if l =~ 'Stopping job with' + let found_stop = 1 + endif + endfor + call assert_equal(1, found_test) + call assert_equal(1, found_send) + call assert_equal(1, found_recv) + call assert_equal(1, found_stop) + call delete('Xlog') endtry endfunc @@ -1050,6 +1084,7 @@ endfunc function s:test_exit_callback(port) call job_setoptions(s:job, {'exit-cb': 'MyExitCb'}) let s:exit_job = s:job + call assert_equal('MyExitCb', job_info(s:job)['exit-cb']) endfunc func Test_exit_callback() @@ -1068,6 +1103,7 @@ func Test_exit_callback() endfor call assert_equal('done', s:job_exit_ret) + call assert_equal('dead', job_info(s:exit_job).status) unlet s:exit_job endif endfunc diff --git a/src/version.c b/src/version.c index 75632e7458..312587b5b5 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,22 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1545, +/**/ + 1544, +/**/ + 1543, +/**/ + 1542, +/**/ + 1541, +/**/ + 1540, +/**/ + 1539, +/**/ + 1538, /**/ 1537, /**/