mirror of
https://github.com/zoriya/vim.git
synced 2025-12-27 09:28:16 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fc3e14282 | ||
|
|
a02b321686 | ||
|
|
681baaf4a4 | ||
|
|
e24692573a | ||
|
|
b3e2f00f39 | ||
|
|
66624ff0d9 | ||
|
|
3b05b135e3 | ||
|
|
608a8919ca | ||
|
|
e7bed627c8 | ||
|
|
bf087cead9 | ||
|
|
2212c4154c | ||
|
|
fcb1e3d168 | ||
|
|
f92591f7f9 | ||
|
|
a0f9cd148e | ||
|
|
7c764f7bbf | ||
|
|
d7ece1008e | ||
|
|
d087566a41 | ||
|
|
f57969a20a | ||
|
|
fbf9c6b6c3 | ||
|
|
8d8c509ac8 | ||
|
|
04b08c3de6 | ||
|
|
bc07309225 | ||
|
|
df5b27b20e | ||
|
|
56ead341a7 |
2
Filelist
2
Filelist
@@ -41,6 +41,7 @@ SRC_ALL = \
|
||||
src/hardcopy.c \
|
||||
src/hashtab.c \
|
||||
src/json.c \
|
||||
src/json_test.c \
|
||||
src/keymap.h \
|
||||
src/macros.h \
|
||||
src/main.c \
|
||||
@@ -90,6 +91,7 @@ SRC_ALL = \
|
||||
src/testdir/README.txt \
|
||||
src/testdir/Make_all.mak \
|
||||
src/testdir/*.in \
|
||||
src/testdir/*.py \
|
||||
src/testdir/sautest/autoload/*.vim \
|
||||
src/testdir/runtest.vim \
|
||||
src/testdir/test[0-9]*.ok \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Jan 31
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 04
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -11,7 +11,7 @@ DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT
|
||||
Vim uses channels to communicate with other processes.
|
||||
A channel uses a socket. *socket-interface*
|
||||
|
||||
Vim current supports up to 10 simultanious channels.
|
||||
Vim current supports up to 10 simultaneous channels.
|
||||
The Netbeans interface also uses a channel. |netbeans|
|
||||
|
||||
1. Demo |channel-demo|
|
||||
@@ -32,13 +32,13 @@ $VIMRUNTIME/tools/demoserver.py
|
||||
Run it in one terminal. We will call this T1.
|
||||
|
||||
Run Vim in another terminal. Connect to the demo server with: >
|
||||
let handle = connect('localhost:8765', 'json')
|
||||
let handle = ch_open('localhost:8765', 'json')
|
||||
|
||||
In T1 you should see:
|
||||
=== socket opened === ~
|
||||
|
||||
You can now send a message to the server: >
|
||||
echo sendexpr(handle, 'hello!')
|
||||
echo ch_sendexpr(handle, 'hello!')
|
||||
|
||||
The message is received in T1 and a response is sent back to Vim.
|
||||
You can see the raw messages in T1. What Vim sends is:
|
||||
@@ -57,19 +57,19 @@ To handle asynchronous communication a callback needs to be used: >
|
||||
func MyHandler(handle, msg)
|
||||
echo "from the handler: " . a:msg
|
||||
endfunc
|
||||
call sendexpr(handle, 'hello!', "MyHandler")
|
||||
call ch_sendexpr(handle, 'hello!', "MyHandler")
|
||||
|
||||
Instead of giving a callback with every send call, it can also be specified
|
||||
when opening the channel: >
|
||||
call disconnect(handle)
|
||||
let handle = connect('localhost:8765', 'json', "MyHandler")
|
||||
call sendexpr(handle, 'hello!', 0)
|
||||
call ch_close(handle)
|
||||
let handle = ch_open('localhost:8765', 'json', "MyHandler")
|
||||
call ch_sendexpr(handle, 'hello!', 0)
|
||||
|
||||
==============================================================================
|
||||
2. Opening a channel *channel-open*
|
||||
|
||||
To open a channel:
|
||||
let handle = connect({address}, {mode}, {callback})
|
||||
To open a channel: >
|
||||
let handle = ch_open({address}, {mode}, {callback})
|
||||
|
||||
{address} has the form "hostname:port". E.g., "localhost:8765".
|
||||
|
||||
@@ -84,7 +84,7 @@ message. Example: >
|
||||
func Handle(handle, msg)
|
||||
echo 'Received: ' . a:msg
|
||||
endfunc
|
||||
let handle = connect("localhost:8765", 'json', "Handle")
|
||||
let handle = ch_open("localhost:8765", 'json', "Handle")
|
||||
|
||||
When {mode} is "json" the "msg" argument is the body of the received message,
|
||||
converted to Vim types.
|
||||
@@ -94,11 +94,11 @@ When {mode} is "json" the {callback} is optional. When omitted it is only
|
||||
possible to receive a message after sending one.
|
||||
|
||||
The handler can be added or changed later: >
|
||||
call sethandler(handle, {callback})
|
||||
call ch_setcallback(handle, {callback})
|
||||
When {callback} is empty (zero or an empty string) the handler is removed.
|
||||
|
||||
Once done with the channel, disconnect it like this: >
|
||||
call disconnect(handle)
|
||||
call ch_close(handle)
|
||||
|
||||
Currently up to 10 channels can be in use at the same time. *E897*
|
||||
|
||||
@@ -112,15 +112,15 @@ If there is an error reading or writing a channel it will be closed.
|
||||
3. Using a JSON channel *channel-use*
|
||||
|
||||
If {mode} is "json" then a message can be sent synchronously like this: >
|
||||
let response = sendexpr(handle, {expr})
|
||||
let response = ch_sendexpr(handle, {expr})
|
||||
This awaits a response from the other side.
|
||||
|
||||
To send a message, without handling a response: >
|
||||
call sendexpr(handle, {expr}, 0)
|
||||
call ch_sendexpr(handle, {expr}, 0)
|
||||
|
||||
To send a message and letting the response handled by a specific function,
|
||||
asynchronously: >
|
||||
call sendexpr(handle, {expr}, {callback})
|
||||
call ch_sendexpr(handle, {expr}, {callback})
|
||||
|
||||
The {expr} is converted to JSON and wrapped in an array. An example of the
|
||||
message that the receiver will get when {expr} is the string "hello":
|
||||
@@ -148,7 +148,7 @@ message, it must use the number zero:
|
||||
Then channel handler will then get {response} converted to Vim types. If the
|
||||
channel does not have a handler the message is dropped.
|
||||
|
||||
On read error or disconnect() the string "DETACH" is sent, if still possible.
|
||||
On read error or ch_close() the string "DETACH" is sent, if still possible.
|
||||
The channel will then be inactive.
|
||||
|
||||
==============================================================================
|
||||
@@ -200,7 +200,7 @@ You can also use "call |feedkeys()|" to insert any key sequence.
|
||||
|
||||
Command "normal" ~
|
||||
|
||||
The "normal" command is executed like with |:normal!|, commands are not
|
||||
The "normal" command is executed like with ":normal!", commands are not
|
||||
mapped. Example to open the folds under the cursor:
|
||||
["normal" "zO"]
|
||||
|
||||
@@ -230,19 +230,19 @@ Example:
|
||||
5. Using a raw channel *channel-raw*
|
||||
|
||||
If {mode} is "raw" then a message can be send like this: >
|
||||
let response = sendraw(handle, {string})
|
||||
let response = ch_sendraw(handle, {string})
|
||||
The {string} is sent as-is. The response will be what can be read from the
|
||||
channel right away. Since Vim doesn't know how to recognize the end of the
|
||||
message you need to take care of it yourself.
|
||||
|
||||
To send a message, without expecting a response: >
|
||||
call sendraw(handle, {string}, 0)
|
||||
call ch_sendraw(handle, {string}, 0)
|
||||
The process can send back a response, the channel handler will be called with
|
||||
it.
|
||||
|
||||
To send a message and letting the response handled by a specific function,
|
||||
asynchronously: >
|
||||
call sendraw(handle, {string}, {callback})
|
||||
call ch_sendraw(handle, {string}, {callback})
|
||||
|
||||
This {string} can also be JSON, use |jsonencode()| to create it and
|
||||
|jsondecode()| to handle a received JSON message.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 01
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 04
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -1810,6 +1810,13 @@ byteidxcomp( {expr}, {nr}) Number byte index of {nr}'th char in {expr}
|
||||
call( {func}, {arglist} [, {dict}])
|
||||
any call {func} with arguments {arglist}
|
||||
ceil( {expr}) Float round {expr} up
|
||||
ch_close( {handle}) none close a channel
|
||||
ch_open( {address}, {mode} [, {callback}])
|
||||
Number open a channel
|
||||
ch_sendexpr( {handle}, {expr} [, {callback}])
|
||||
any send {expr} over JSON channel {handle}
|
||||
ch_sendraw( {handle}, {string} [, {callback}])
|
||||
any send {string} over raw channel {handle}
|
||||
changenr() Number current change number
|
||||
char2nr( {expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
|
||||
cindent( {lnum}) Number C indent for line {lnum}
|
||||
@@ -1820,8 +1827,6 @@ complete_add( {expr}) Number add completion match
|
||||
complete_check() Number check for key typed during completion
|
||||
confirm( {msg} [, {choices} [, {default} [, {type}]]])
|
||||
Number number of choice picked by user
|
||||
connect( {address}, {mode} [, {callback}])
|
||||
Number open a channel
|
||||
copy( {expr}) any make a shallow copy of {expr}
|
||||
cos( {expr}) Float cosine of {expr}
|
||||
cosh( {expr}) Float hyperbolic cosine of {expr}
|
||||
@@ -2029,10 +2034,6 @@ searchpairpos( {start}, {middle}, {end} [, {flags} [, {skip} [...]]])
|
||||
List search for other end of start/end pair
|
||||
searchpos( {pattern} [, {flags} [, {stopline} [, {timeout}]]])
|
||||
List search for {pattern}
|
||||
sendexpr( {handle}, {expr} [, {callback}])
|
||||
any send {expr} over JSON channel {handle}
|
||||
sendraw( {handle}, {string} [, {callback}])
|
||||
any send {string} over raw channel {handle}
|
||||
server2client( {clientid}, {string})
|
||||
Number send reply string
|
||||
serverlist() String get a list of available servers
|
||||
@@ -2666,7 +2667,10 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
|
||||
don't fit, a vertical layout is used anyway. For some systems
|
||||
the horizontal layout is always used.
|
||||
|
||||
connect({address}, {mode} [, {callback}]) *connect()*
|
||||
ch_close({handle}) *ch_close()*
|
||||
Close channel {handle}. See |channel|.
|
||||
|
||||
ch_open({address}, {mode} [, {callback}]) *ch_open()*
|
||||
Open a channel to {address}. See |channel|.
|
||||
Returns the channel handle on success. Returns a negative
|
||||
number for failure.
|
||||
@@ -2680,6 +2684,23 @@ connect({address}, {mode} [, {callback}]) *connect()*
|
||||
{callback} is a function that handles received messages on the
|
||||
channel. See |channel-callback|.
|
||||
|
||||
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||
Send {expr} over JSON channel {handle}. See |channel-use|.
|
||||
|
||||
When {callback} is given returns immediately. Without
|
||||
{callback} waits for a JSON response and returns the decoded
|
||||
expression. When there is an error or timeout returns an
|
||||
empty string.
|
||||
|
||||
When {callback} is zero no response is expected.
|
||||
Otherwise {callback} must be a Funcref or the name of a
|
||||
function. It is called when the response is received. See
|
||||
|channel-callback|.
|
||||
|
||||
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
|
||||
Send {string} over raw channel {handle}. See |channel-raw|.
|
||||
Works like |ch_sendexpr()|, but does not decode the response.
|
||||
|
||||
*copy()*
|
||||
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
||||
different from using {expr} directly.
|
||||
@@ -5615,23 +5636,6 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *searchpos()*
|
||||
< In this example "submatch" is 2 when a lowercase letter is
|
||||
found |/\l|, 3 when an uppercase letter is found |/\u|.
|
||||
|
||||
sendexpr({handle}, {expr} [, {callback}]) *sendexpr()*
|
||||
Send {expr} over JSON channel {handle}. See |channel-use|.
|
||||
|
||||
When {callback} is given returns immediately. Without
|
||||
{callback} waits for a JSON response and returns the decoded
|
||||
expression. When there is an error or timeout returns an
|
||||
empty string.
|
||||
|
||||
When {callback} is zero no response is expected.
|
||||
Otherwise {callback} must be a Funcref or the name of a
|
||||
function. It is called when the response is received. See
|
||||
|channel-callback|.
|
||||
|
||||
sendraw({handle}, {string} [, {callback}]) *sendraw()*
|
||||
Send {string} over raw channel {handle}. See |channel-raw|.
|
||||
Works like |sendexpr()|, but does not decode the response.
|
||||
|
||||
server2client( {clientid}, {string}) *server2client()*
|
||||
Send a reply string to {clientid}. The most recent {clientid}
|
||||
that sent a string can be retrieved with expand("<client>").
|
||||
|
||||
@@ -5156,6 +5156,10 @@ catch-text eval.txt /*catch-text*
|
||||
cc change.txt /*cc*
|
||||
ceil() eval.txt /*ceil()*
|
||||
ch.vim syntax.txt /*ch.vim*
|
||||
ch_close() eval.txt /*ch_close()*
|
||||
ch_open() eval.txt /*ch_open()*
|
||||
ch_sendexpr() eval.txt /*ch_sendexpr()*
|
||||
ch_sendraw() eval.txt /*ch_sendraw()*
|
||||
change-list-jumps motion.txt /*change-list-jumps*
|
||||
change-name tips.txt /*change-name*
|
||||
change-tabs change.txt /*change-tabs*
|
||||
@@ -5321,7 +5325,6 @@ complex-repeat repeat.txt /*complex-repeat*
|
||||
compress pi_gzip.txt /*compress*
|
||||
conceal syntax.txt /*conceal*
|
||||
confirm() eval.txt /*confirm()*
|
||||
connect() eval.txt /*connect()*
|
||||
connection-refused message.txt /*connection-refused*
|
||||
console-menus gui.txt /*console-menus*
|
||||
control intro.txt /*control*
|
||||
@@ -7917,8 +7920,6 @@ sed.vim syntax.txt /*sed.vim*
|
||||
self eval.txt /*self*
|
||||
send-money sponsor.txt /*send-money*
|
||||
send-to-menu gui_w32.txt /*send-to-menu*
|
||||
sendexpr() eval.txt /*sendexpr()*
|
||||
sendraw() eval.txt /*sendraw()*
|
||||
sendto gui_w32.txt /*sendto*
|
||||
sentence motion.txt /*sentence*
|
||||
server-functions usr_41.txt /*server-functions*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*todo.txt* For Vim version 7.4. Last change: 2016 Feb 01
|
||||
*todo.txt* For Vim version 7.4. Last change: 2016 Feb 04
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -81,19 +81,36 @@ Regexp problems:
|
||||
Patch by Christian, 2016 Jan 29.
|
||||
|
||||
+channel:
|
||||
- implement wait for receiving end of Json.
|
||||
- implement only reading up to end of Json.
|
||||
- use a timeout for connect()
|
||||
Patch from Yasuhiro Matsumoto, Feb 2
|
||||
Change connect() second argument to a dict with items:
|
||||
mode
|
||||
timeout
|
||||
callback
|
||||
- When receiving malformed json starting with a quote it doesn't get
|
||||
discarded.
|
||||
- add ch_setcallback()
|
||||
- add ch_settimeout()
|
||||
- cleanup on exit? in mch_getout() and getout().
|
||||
- Add more contents to channel.txt
|
||||
- implement debug log
|
||||
- implement job control
|
||||
- implement job control:
|
||||
let job = job_start('command', {options})
|
||||
call job_stop(job)
|
||||
let job = job_maystart('command', {address}, {options})
|
||||
options:
|
||||
- keep running when Vim exits
|
||||
- add remark undo sync, is there a way to force it?
|
||||
- Add a test with a server that can send canned responses.
|
||||
- Add more testing in json_test.c
|
||||
- make sure errors lead to a useful error msg. ["ex","foobar"]
|
||||
- use a timeout for connect() Patch from Yasuhiro Matsumoto, Feb 1
|
||||
- set timeout for channel.
|
||||
- implement check for ID in response.
|
||||
- json: implement UTF-16 surrogate pair.
|
||||
|
||||
Patch on #608: (Ken Takata)
|
||||
https://bitbucket.org/k_takata/vim-ktakata-mq/src/479934b94fd56b064c9e4bd8737585c5df69d56a/fix-gvimext-loadlibrary.patch?fileviewer=file-view-default
|
||||
|
||||
This difference is unexpected:
|
||||
echo v:true == 1
|
||||
1
|
||||
@@ -109,6 +126,14 @@ Patch to put undo options together in undo window.
|
||||
Patch for clearing history. (Yegappan Lakshmanan, 2016 Jan 31, second message
|
||||
has tests)
|
||||
|
||||
Patch to update the GTK icon cache when installing. (Kazunobu Kuriyama, 2016
|
||||
Feb 3)
|
||||
|
||||
Patch for test86 and test87. (Roland Puntaier, #622)
|
||||
|
||||
Patch for Python: #622. (Roland Puntaier, 2016 Feb 2)
|
||||
What does it change?
|
||||
|
||||
Need to try out instructions in INSSTALLpc.txt about how to install all
|
||||
interfaces and how to build Vim with them.
|
||||
Appveyor build with self-installing executable, includes getting most
|
||||
@@ -132,6 +157,8 @@ 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.
|
||||
|
||||
@@ -161,15 +188,16 @@ Patch to avoid redrawing tabline when the popup menu is visible.
|
||||
|
||||
Win32: patch to use 64 bit stat() if possible. (Ken Takata, 2014 May 12)
|
||||
More tests May 14. Update May 29. Update Aug 10.
|
||||
Now part of large file patches. (Ken Takata, 2016 Jan 19, second one)
|
||||
Updated patches with ordering: Jan 20.
|
||||
And another update: Jan 24, then Jan 29, 30, Feb 1.
|
||||
Now part of large file patches. (Ken Takata, 2016 Feb 1)
|
||||
Two patches now?
|
||||
|
||||
Patch to support 64 bit ints for Number. (Ken Takata, 2016 Jan 21)
|
||||
|
||||
7 Add a watchpoint in the debug mode: An expression that breaks execution
|
||||
when evaluating to non-zero. Add the "watchadd expr" command, stop when
|
||||
the value of the expression changes. ":watchdel" deletes an item,
|
||||
":watchlist" lists the items. (Charles Campbell)
|
||||
Patch by Christian Brabandt, 2016 Jan 27.
|
||||
Patch by Christian Brabandt, 2016 Feb 1.
|
||||
|
||||
Patch to be able to use hex numbers with :digraph. (Lcd, 2015 Sep 6)
|
||||
Update Sep 7. Update by Christian Brabandt, 2015 Sep 8, 2016 Feb 1.
|
||||
@@ -182,6 +210,9 @@ Patch to add <restore> to :windo, :bufdo, etc. (Christian Brabandt, 2015 Jan
|
||||
6, 2nd message)
|
||||
Alternative: ":keeppos" command modifier: ":keeppos windo {cmd}".
|
||||
|
||||
Patch to fix that executable() may fail on very long filename in MS-Windows.
|
||||
(Ken Takata, 2016 Feb 1)
|
||||
|
||||
Patch to fix display of listchars on the cursorline. (Nayuri Aohime, 2013)
|
||||
Update suggested by Yasuhiro Matsumoto, 2014 Nov 25:
|
||||
https://gist.github.com/presuku/d3d6b230b9b6dcfc0477
|
||||
@@ -193,8 +224,6 @@ Gvim: when both Tab and CTRL-I are mapped, use CTRL-I not for Tab.
|
||||
Unexpected delay when using CTRL-O u. It's not timeoutlen.
|
||||
(Gary Johnson, 2015 Aug 28)
|
||||
|
||||
Patch to support 64 bit ints for Number. (Ken Takata, 2016 Jan 21)
|
||||
|
||||
Instead of separately uploading patches to the ftp site, we can get them from
|
||||
github with a URL like this:
|
||||
https://github.com/vim/vim/compare/v7.4.920%5E...v7.4.920.diff
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*usr_41.txt* For Vim version 7.4. Last change: 2016 Jan 28
|
||||
*usr_41.txt* For Vim version 7.4. Last change: 2016 Feb 02
|
||||
|
||||
VIM USER MANUAL - by Bram Moolenaar
|
||||
|
||||
@@ -894,10 +894,10 @@ Testing: *test-functions*
|
||||
assert_true() assert that an expression is true
|
||||
|
||||
Inter-process communication:
|
||||
connect() open a channel
|
||||
disconnect() close a channel
|
||||
sendexpr() send a JSON message over a channel
|
||||
sendraw() send a raw message over a channel
|
||||
ch_open() open a channel
|
||||
ch_close() close a channel
|
||||
ch_sendexpr() send a JSON message over a channel
|
||||
ch_sendraw() send a raw message over a channel
|
||||
jsonencode() encode an expression to a JSON string
|
||||
jsondecode() decode a JSON string to Vim types
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*windows.txt* For Vim version 7.4. Last change: 2015 Nov 14
|
||||
*windows.txt* For Vim version 7.4. Last change: 2016 Feb 01
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -721,7 +721,7 @@ can also get to them with the buffer list commands, like ":bnext".
|
||||
*:bufdo*
|
||||
:[range]bufdo[!] {cmd} Execute {cmd} in each buffer in the buffer list or if
|
||||
[range] is given only for buffers for which their
|
||||
buffer numer is in the [range]. It works like doing
|
||||
buffer number is in the [range]. It works like doing
|
||||
this: >
|
||||
:bfirst
|
||||
:{cmd}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
" Maintainer: Christian Brabandt <cb@256bit.org>
|
||||
" Previous Maintainer: Peter Aronoff <telemachus@arpinum.org>
|
||||
" Original Author: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2015-12-15
|
||||
" Latest Revision: 2016-01-15
|
||||
" License: Vim (see :h license)
|
||||
" Repository: https://github.com/chrisbra/vim-sh-indent
|
||||
|
||||
@@ -28,7 +28,7 @@ let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function s:buffer_shiftwidth()
|
||||
return &shiftwidth
|
||||
return shiftwidth()
|
||||
endfunction
|
||||
|
||||
let s:sh_indent_defaults = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
" Vim syntax file for the D programming language (version 1.076 and 2.063).
|
||||
" Vim syntax file for the D programming language (version 1.076 and 2.069).
|
||||
"
|
||||
" Language: D
|
||||
" Maintainer: Jesse Phillips <Jesse.K.Phillips+D@gmail.com>
|
||||
" Last Change: 2013 October 5
|
||||
" Version: 0.26
|
||||
" Last Change: 2016 Feb 2
|
||||
" Version: 0.28
|
||||
"
|
||||
" Contributors:
|
||||
" - Jason Mills: original Maintainer
|
||||
@@ -15,6 +15,7 @@
|
||||
" - Steven N. Oliver
|
||||
" - Sohgo Takeuchi
|
||||
" - Robert Clipsham
|
||||
" - Petar Kirov
|
||||
"
|
||||
" Please submit bugs/comments/suggestions to the github repo:
|
||||
" https://github.com/JesseKPhillips/d.vim
|
||||
@@ -114,17 +115,19 @@ syn keyword dTraitsIdentifier contained isIntegral isScalar isStaticArray
|
||||
syn keyword dTraitsIdentifier contained isUnsigned isVirtualFunction
|
||||
syn keyword dTraitsIdentifier contained isVirtualMethod isAbstractFunction
|
||||
syn keyword dTraitsIdentifier contained isFinalFunction isStaticFunction
|
||||
syn keyword dTraitsIdentifier contained isOverrideFunction isTemplate
|
||||
syn keyword dTraitsIdentifier contained isRef isOut isLazy hasMember
|
||||
syn keyword dTraitsIdentifier contained identifier getAttributes getMember
|
||||
syn keyword dTraitsIdentifier contained getOverloads getProtection
|
||||
syn keyword dTraitsIdentifier contained getVirtualFunctions
|
||||
syn keyword dTraitsIdentifier contained getVirtualMethods parent
|
||||
syn keyword dTraitsIdentifier contained classInstanceSize allMembers
|
||||
syn keyword dTraitsIdentifier contained identifier getAliasThis
|
||||
syn keyword dTraitsIdentifier contained getAttributes getFunctionAttributes getMember
|
||||
syn keyword dTraitsIdentifier contained getOverloads getPointerBitmap getProtection
|
||||
syn keyword dTraitsIdentifier contained getVirtualFunctions getVirtualIndex
|
||||
syn keyword dTraitsIdentifier contained getVirtualMethods getUnitTests
|
||||
syn keyword dTraitsIdentifier contained parent classInstanceSize allMembers
|
||||
syn keyword dTraitsIdentifier contained derivedMembers isSame compiles
|
||||
syn keyword dPragmaIdentifier contained lib msg startaddress GNU_asm
|
||||
syn keyword dExternIdentifier contained Windows Pascal Java System D
|
||||
syn keyword dPragmaIdentifier contained inline lib mangle msg startaddress GNU_asm
|
||||
syn keyword dExternIdentifier contained C C++ D Windows Pascal System Objective-C
|
||||
syn keyword dAttribute contained safe trusted system
|
||||
syn keyword dAttribute contained property disable
|
||||
syn keyword dAttribute contained property disable nogc
|
||||
syn keyword dVersionIdentifier contained DigitalMars GNU LDC SDC D_NET
|
||||
syn keyword dVersionIdentifier contained X86 X86_64 ARM PPC PPC64 IA64 MIPS MIPS64 Alpha
|
||||
syn keyword dVersionIdentifier contained SPARC SPARC64 S390 S390X HPPA HPPA64 SH SH64
|
||||
@@ -134,7 +137,7 @@ syn keyword dVersionIdentifier contained Cygwin MinGW
|
||||
syn keyword dVersionIdentifier contained LittleEndian BigEndian
|
||||
syn keyword dVersionIdentifier contained D_InlineAsm_X86 D_InlineAsm_X86_64
|
||||
syn keyword dVersionIdentifier contained D_Version2 D_Coverage D_Ddoc D_LP64 D_PIC
|
||||
syn keyword dVersionIdentifier contained unittest none all
|
||||
syn keyword dVersionIdentifier contained unittest assert none all
|
||||
|
||||
syn cluster dComment contains=dNestedComment,dBlockComment,dLineComment
|
||||
|
||||
@@ -168,10 +171,10 @@ syn match dExternal "\<extern\>"
|
||||
syn match dExtern "\<extern\s*([_a-zA-Z][_a-zA-Z0-9\+]*\>"he=s+6 contains=dExternIdentifier
|
||||
|
||||
" Make import a region to prevent highlighting keywords
|
||||
syn region dImport start="import\_s" end=";" contains=dExternal,@dComment
|
||||
syn region dImport start="\<import\_s" end=";" contains=dExternal,@dComment
|
||||
|
||||
" Make module a region to prevent highlighting keywords
|
||||
syn region dImport start="module\_s" end=";" contains=dExternal,@dComment
|
||||
syn region dImport start="\<module\_s" end=";" contains=dExternal,@dComment
|
||||
|
||||
" dTokens is used by the token string highlighting
|
||||
syn cluster dTokens contains=dExternal,dConditional,dBranch,dRepeat,dBoolean
|
||||
@@ -246,13 +249,17 @@ syn match dUnicode "\\u\d\{4\}"
|
||||
|
||||
" String.
|
||||
"
|
||||
syn region dString start=+"+ end=+"[cwd]\=+ skip=+\\\\\|\\"+ contains=dEscSequence,@Spell
|
||||
syn match dFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlL]\|ll\)\=\([bdiuoxXDOUfeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
|
||||
syn match dFormat display "%%" contained
|
||||
|
||||
syn region dString start=+"+ end=+"[cwd]\=+ skip=+\\\\\|\\"+ contains=dFormat,dEscSequence,@Spell
|
||||
syn region dRawString start=+`+ end=+`[cwd]\=+ contains=@Spell
|
||||
syn region dRawString start=+r"+ end=+"[cwd]\=+ contains=@Spell
|
||||
syn region dHexString start=+x"+ end=+"[cwd]\=+ contains=@Spell
|
||||
syn region dDelimString start=+q"\z(.\)+ end=+\z1"+ contains=@Spell
|
||||
syn region dHereString start=+q"\z(\I\i*\)\n+ end=+^\z1"+ contains=@Spell
|
||||
|
||||
|
||||
" Nesting delimited string contents
|
||||
"
|
||||
syn region dNestParenString start=+(+ end=+)+ contained transparent contains=dNestParenString,@Spell
|
||||
@@ -276,8 +283,8 @@ syn cluster dTokens add=dString,dRawString,dHexString,dDelimString,dNestString
|
||||
|
||||
" Token strings
|
||||
"
|
||||
syn region dNestTokenString start=+{+ end=+}+ contained contains=dNestTokenString,@dTokens
|
||||
syn region dTokenString matchgroup=dTokenStringBrack transparent start=+q{+ end=+}+ contains=dNestTokenString,@dTokens
|
||||
syn region dNestTokenString start=+{+ end=+}+ contained contains=dNestTokenString,@dTokens,dFormat
|
||||
syn region dTokenString matchgroup=dTokenStringBrack transparent start=+q{+ end=+}+ contains=dNestTokenString,@dTokens,dFormat
|
||||
|
||||
syn cluster dTokens add=dTokenString
|
||||
|
||||
@@ -357,6 +364,7 @@ hi def link dString String
|
||||
hi def link dHexString String
|
||||
hi def link dCharacter Character
|
||||
hi def link dEscSequence SpecialChar
|
||||
hi def link dFormat SpecialChar
|
||||
hi def link dSpecialCharError Error
|
||||
hi def link dOctalError Error
|
||||
hi def link dOperator Operator
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
" Language: Zsh shell script
|
||||
" Maintainer: Christian Brabandt <cb@256bit.org>
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2015-12-25
|
||||
" Latest Revision: 2016-01-25
|
||||
" License: Vim (see :h license)
|
||||
" Repository: https://github.com/chrisbra/vim-zsh
|
||||
|
||||
@@ -14,6 +14,7 @@ let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
setlocal iskeyword+=-
|
||||
setlocal foldmethod=syntax
|
||||
|
||||
syn keyword zshTodo contained TODO FIXME XXX NOTE
|
||||
|
||||
@@ -307,19 +308,19 @@ syn match zshNumber '[+-]\=\d\+\.\d\+\>'
|
||||
" TODO: $[...] is the same as $((...)), so add that as well.
|
||||
syn cluster zshSubst contains=zshSubst,zshOldSubst,zshMathSubst
|
||||
syn region zshSubst matchgroup=zshSubstDelim transparent
|
||||
\ start='\$(' skip='\\)' end=')' contains=TOP
|
||||
syn region zshParentheses transparent start='(' skip='\\)' end=')'
|
||||
\ start='\$(' skip='\\)' end=')' contains=TOP fold
|
||||
syn region zshParentheses transparent start='(' skip='\\)' end=')' fold
|
||||
syn region zshMathSubst matchgroup=zshSubstDelim transparent
|
||||
\ start='\$((' skip='\\)'
|
||||
\ matchgroup=zshSubstDelim end='))'
|
||||
\ contains=zshParentheses,@zshSubst,zshNumber,
|
||||
\ @zshDerefs,zshString keepend
|
||||
\ @zshDerefs,zshString keepend fold
|
||||
syn region zshBrackets contained transparent start='{' skip='\\}'
|
||||
\ end='}'
|
||||
\ end='}' fold
|
||||
syn region zshSubst matchgroup=zshSubstDelim start='\${' skip='\\}'
|
||||
\ end='}' contains=@zshSubst,zshBrackets,zshQuoted,zshString
|
||||
\ end='}' contains=@zshSubst,zshBrackets,zshQuoted,zshString fold
|
||||
syn region zshOldSubst matchgroup=zshSubstDelim start=+`+ skip=+\\`+
|
||||
\ end=+`+ contains=TOP,zshOldSubst
|
||||
\ end=+`+ contains=TOP,zshOldSubst fold
|
||||
|
||||
syn sync minlines=50 maxlines=90
|
||||
syn sync match zshHereDocSync grouphere NONE '<<-\=\s*\%(\\\=\S\+\|\(["']\)\S\+\1\)'
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Server that will accept connections from a Vim channel.
|
||||
# Run this server and then in Vim you can open the channel:
|
||||
# :let handle = connect('localhost:8765', 'json')
|
||||
# :let handle = ch_open('localhost:8765', 'json')
|
||||
#
|
||||
# Then Vim can send requests to the server:
|
||||
# :let response = sendexpr(handle, 'hello!')
|
||||
# :let response = ch_sendexpr(handle, 'hello!')
|
||||
#
|
||||
# And you can control Vim by typing a JSON message here, e.g.:
|
||||
# ["ex","echo 'hi there'"]
|
||||
#
|
||||
# There is no prompt, just type a line and press Enter.
|
||||
# To exit cleanly type "quit<Enter>".
|
||||
#
|
||||
# See ":help channel-demo" in Vim.
|
||||
#
|
||||
# This requires Python 2.6 or later.
|
||||
|
||||
from __future__ import print_function
|
||||
import json
|
||||
|
||||
@@ -52,7 +52,7 @@ gvimext.obj: gvimext.h
|
||||
$(cc) $(cflags) -DFEAT_GETTEXT $(cvarsmt) $*.cpp
|
||||
|
||||
gvimext.res: gvimext.rc
|
||||
$(rc) $(rcflags) $(rcvars) gvimext.rc
|
||||
$(rc) /nologo $(rcflags) $(rcvars) gvimext.rc
|
||||
|
||||
clean:
|
||||
- if exist gvimext.dll del gvimext.dll
|
||||
|
||||
@@ -1294,7 +1294,7 @@ $(OUTDIR)/xpm_w32.obj: $(OUTDIR) xpm_w32.c
|
||||
$(OUTDIR)/vim.res: $(OUTDIR) vim.rc gvim.exe.mnf version.h tools.bmp \
|
||||
tearoff.bmp vim.ico vim_error.ico \
|
||||
vim_alert.ico vim_info.ico vim_quest.ico
|
||||
$(RC) /l 0x409 /Fo$(OUTDIR)/vim.res $(RCFLAGS) vim.rc
|
||||
$(RC) /nologo /l 0x409 /Fo$(OUTDIR)/vim.res $(RCFLAGS) vim.rc
|
||||
|
||||
iid_ole.c if_ole.h vim.tlb: if_ole.idl
|
||||
midl /nologo /error none /proxy nul /iid iid_ole.c /tlb vim.tlb \
|
||||
|
||||
40
src/Makefile
40
src/Makefile
@@ -1545,11 +1545,13 @@ EXTRA_SRC = hangulin.c if_lua.c if_mzsch.c auto/if_perl.c if_perlsfio.c \
|
||||
$(GRESOURCE_SRC)
|
||||
|
||||
# Unittest files
|
||||
JSON_TEST_SRC = json_test.c
|
||||
JSON_TEST_TARGET = json_test$(EXEEXT)
|
||||
MEMFILE_TEST_SRC = memfile_test.c
|
||||
MEMFILE_TEST_TARGET = memfile_test$(EXEEXT)
|
||||
|
||||
UNITTEST_SRC = $(MEMFILE_TEST_SRC)
|
||||
UNITTEST_TARGETS = $(MEMFILE_TEST_TARGET)
|
||||
UNITTEST_SRC = $(JSON_TEST_SRC) $(MEMFILE_TEST_SRC)
|
||||
UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(MEMFILE_TEST_TARGET)
|
||||
|
||||
# All sources, also the ones that are not configured
|
||||
ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC)
|
||||
@@ -1588,9 +1590,8 @@ OBJ_COMMON = \
|
||||
$(HANGULIN_OBJ) \
|
||||
objects/if_cscope.o \
|
||||
objects/if_xcmdsrv.o \
|
||||
objects/json.o \
|
||||
objects/mark.o \
|
||||
objects/memline.o \
|
||||
objects/memline.o \
|
||||
objects/menu.o \
|
||||
objects/message.o \
|
||||
objects/misc1.o \
|
||||
@@ -1632,11 +1633,17 @@ OBJ_COMMON = \
|
||||
$(WSDEBUG_OBJ)
|
||||
|
||||
OBJ = $(OBJ_COMMON) \
|
||||
objects/json.o \
|
||||
objects/main.o \
|
||||
objects/memfile.o
|
||||
|
||||
JSON_TEST_OBJ = $(OBJ_COMMON) \
|
||||
objects/json_test.o \
|
||||
objects/memfile.o
|
||||
|
||||
MEMFILE_TEST_OBJ = $(OBJ_COMMON) \
|
||||
objects/memfile_test.o
|
||||
objects/json.o \
|
||||
objects/memfile_test.o
|
||||
|
||||
PRO_AUTO = \
|
||||
blowfish.pro \
|
||||
@@ -1914,6 +1921,7 @@ types.vim: $(TAGS_SRC) $(TAGS_INCL)
|
||||
ctags --c-kinds=gstu -o- $(TAGS_SRC) $(TAGS_INCL) |\
|
||||
awk 'BEGIN{printf("syntax keyword Type\t")}\
|
||||
{printf("%s ", $$1)}END{print ""}' > $@
|
||||
echo "syn keyword Constant OK FAIL TRUE FALSE MAYBE" >> $@
|
||||
|
||||
# Execute the test scripts. Run these after compiling Vim, before installing.
|
||||
# This doesn't depend on $(VIMTARGET), because that won't work when configure
|
||||
@@ -1948,6 +1956,12 @@ unittest unittests: $(UNITTEST_TARGETS)
|
||||
./$$t || exit 1; echo $$t passed; \
|
||||
done
|
||||
|
||||
run_json_test: $(JSON_TEST_TARGET)
|
||||
./$(JSON_TEST_TARGET)
|
||||
|
||||
run_memfile_test: $(MEMFILE_TEST_TARGET)
|
||||
./$(MEMFILE_TEST_TARGET)
|
||||
|
||||
# Run individual OLD style test, assuming that Vim was already compiled.
|
||||
test1 \
|
||||
test_autocmd_option \
|
||||
@@ -1998,6 +2012,7 @@ test_arglist \
|
||||
test_assert \
|
||||
test_backspace_opt \
|
||||
test_cdo \
|
||||
test_channel \
|
||||
test_cursor_func \
|
||||
test_delete \
|
||||
test_expand \
|
||||
@@ -2040,6 +2055,13 @@ testclean:
|
||||
|
||||
# Unittests
|
||||
# It's build just like Vim to satisfy all dependencies.
|
||||
$(JSON_TEST_TARGET): auto/config.mk objects $(JSON_TEST_OBJ)
|
||||
$(CCC) version.c -o objects/version.o
|
||||
@LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
|
||||
-o $(JSON_TEST_TARGET) $(JSON_TEST_OBJ) $(ALL_LIBS)" \
|
||||
MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \
|
||||
sh $(srcdir)/link.sh
|
||||
|
||||
$(MEMFILE_TEST_TARGET): auto/config.mk objects $(MEMFILE_TEST_OBJ)
|
||||
$(CCC) version.c -o objects/version.o
|
||||
@LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
|
||||
@@ -2531,6 +2553,7 @@ shadow: runtime pixmaps
|
||||
../../testdir/Make_all.mak \
|
||||
../../testdir/*.in \
|
||||
../../testdir/*.vim \
|
||||
../../testdir/*.py \
|
||||
../../testdir/python* \
|
||||
../../testdir/sautest \
|
||||
../../testdir/test83-tags? \
|
||||
@@ -2811,6 +2834,9 @@ objects/integration.o: integration.c
|
||||
objects/json.o: json.c
|
||||
$(CCC) -o $@ json.c
|
||||
|
||||
objects/json_test.o: json_test.c
|
||||
$(CCC) -o $@ json_test.c
|
||||
|
||||
objects/main.o: main.c
|
||||
$(CCC) -o $@ main.c
|
||||
|
||||
@@ -3301,6 +3327,10 @@ objects/gui_at_fs.o: gui_at_fs.c vim.h auto/config.h feature.h os_unix.h \
|
||||
objects/pty.o: pty.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h \
|
||||
keymap.h term.h macros.h option.h structs.h regexp.h gui.h gui_beval.h \
|
||||
proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h farsi.h arabic.h
|
||||
objects/json_test.o: json_test.c main.c vim.h auto/config.h feature.h os_unix.h \
|
||||
auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
|
||||
regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h \
|
||||
globals.h farsi.h arabic.h farsi.c arabic.c json.c
|
||||
objects/memfile_test.o: memfile_test.c main.c vim.h auto/config.h feature.h \
|
||||
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h \
|
||||
structs.h regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h \
|
||||
|
||||
168
src/channel.c
168
src/channel.c
@@ -133,22 +133,25 @@ FILE *debugfd = NULL;
|
||||
add_channel(void)
|
||||
{
|
||||
int idx;
|
||||
channel_T *new_channels;
|
||||
channel_T *ch;
|
||||
|
||||
if (channels != NULL)
|
||||
{
|
||||
for (idx = 0; idx < channel_count; ++idx)
|
||||
if (channels[idx].ch_fd < 0)
|
||||
/* re-use a closed channel slot */
|
||||
return idx;
|
||||
if (channel_count == MAX_OPEN_CHANNELS)
|
||||
return -1;
|
||||
new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1));
|
||||
if (new_channels == NULL)
|
||||
return -1;
|
||||
if (channels != NULL)
|
||||
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
|
||||
channels = new_channels;
|
||||
if (channel_count == MAX_OPEN_CHANNELS)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
channels = (channel_T *)alloc((int)sizeof(channel_T)
|
||||
* MAX_OPEN_CHANNELS);
|
||||
if (channels == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ch = &channels[channel_count];
|
||||
(void)vim_memset(ch, 0, sizeof(channel_T));
|
||||
|
||||
@@ -523,51 +526,75 @@ channel_collapse(int idx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the read buffer of channel "ch_idx" and parse JSON messages that are
|
||||
* Use the read buffer of channel "ch_idx" and parse a JSON messages that is
|
||||
* complete. The messages are added to the queue.
|
||||
* Return TRUE if there is more to read.
|
||||
*/
|
||||
void
|
||||
channel_read_json(int ch_idx)
|
||||
static int
|
||||
channel_parse_json(int ch_idx)
|
||||
{
|
||||
js_read_T reader;
|
||||
typval_T listtv;
|
||||
jsonq_T *item;
|
||||
jsonq_T *head = &channels[ch_idx].ch_json_head;
|
||||
int ret;
|
||||
|
||||
if (channel_peek(ch_idx) == NULL)
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
/* TODO: make reader work properly */
|
||||
/* reader.js_buf = channel_peek(ch_idx); */
|
||||
reader.js_buf = channel_get_all(ch_idx);
|
||||
reader.js_eof = TRUE;
|
||||
/* reader.js_eof = FALSE; */
|
||||
reader.js_used = 0;
|
||||
reader.js_fill = NULL;
|
||||
/* reader.js_fill = channel_fill; */
|
||||
reader.js_cookie = &ch_idx;
|
||||
if (json_decode(&reader, &listtv) == OK)
|
||||
ret = json_decode(&reader, &listtv);
|
||||
if (ret == OK)
|
||||
{
|
||||
item = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T));
|
||||
if (item == NULL)
|
||||
if (listtv.v_type != VAR_LIST)
|
||||
{
|
||||
/* TODO: give error */
|
||||
clear_tv(&listtv);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->value = alloc_tv();
|
||||
if (item->value == NULL)
|
||||
{
|
||||
vim_free(item);
|
||||
item = (jsonq_T *)alloc((unsigned)sizeof(jsonq_T));
|
||||
if (item == NULL)
|
||||
clear_tv(&listtv);
|
||||
}
|
||||
else
|
||||
{
|
||||
*item->value = listtv;
|
||||
item->prev = head->prev;
|
||||
head->prev = item;
|
||||
item->next = head;
|
||||
item->prev->next = item;
|
||||
item->value = alloc_tv();
|
||||
if (item->value == NULL)
|
||||
{
|
||||
vim_free(item);
|
||||
clear_tv(&listtv);
|
||||
}
|
||||
else
|
||||
{
|
||||
*item->value = listtv;
|
||||
item->prev = head->prev;
|
||||
head->prev = item;
|
||||
item->next = head;
|
||||
item->prev->next = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Put the unread part back into the channel.
|
||||
* TODO: insert in front */
|
||||
if (reader.js_buf[reader.js_used] != NUL)
|
||||
{
|
||||
channel_save(ch_idx, reader.js_buf + reader.js_used,
|
||||
(int)(reader.js_end - reader.js_buf) - reader.js_used);
|
||||
ret = TRUE;
|
||||
}
|
||||
else
|
||||
ret = FALSE;
|
||||
|
||||
vim_free(reader.js_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -601,7 +628,8 @@ channel_get_json(int ch_idx, int id, typval_T **rettv)
|
||||
typval_T *tv = &l->lv_first->li_tv;
|
||||
|
||||
if ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
|
||||
|| id <= 0)
|
||||
|| (id <= 0
|
||||
&& (tv->v_type != VAR_NUMBER || tv->vval.v_number < 0)))
|
||||
{
|
||||
*rettv = item->value;
|
||||
remove_json_node(item);
|
||||
@@ -666,17 +694,21 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
|
||||
{
|
||||
int is_eval = cmd[1] == 'v';
|
||||
|
||||
if (is_eval && arg3->v_type != VAR_NUMBER)
|
||||
if (is_eval && (arg3 == NULL || arg3->v_type != VAR_NUMBER))
|
||||
{
|
||||
if (p_verbose > 2)
|
||||
EMSG("E904: third argument for eval must be a number");
|
||||
}
|
||||
else
|
||||
{
|
||||
typval_T *tv = eval_expr(arg, NULL);
|
||||
typval_T *tv;
|
||||
typval_T err_tv;
|
||||
char_u *json;
|
||||
|
||||
/* Don't pollute the display with errors. */
|
||||
++emsg_skip;
|
||||
tv = eval_expr(arg, NULL);
|
||||
--emsg_skip;
|
||||
if (is_eval)
|
||||
{
|
||||
if (tv == NULL)
|
||||
@@ -689,7 +721,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
|
||||
channel_send(idx, json, "eval");
|
||||
vim_free(json);
|
||||
}
|
||||
free_tv(tv);
|
||||
if (tv != &err_tv)
|
||||
free_tv(tv);
|
||||
}
|
||||
}
|
||||
else if (p_verbose > 2)
|
||||
@@ -698,8 +731,9 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
|
||||
|
||||
/*
|
||||
* Invoke a callback for channel "idx" if needed.
|
||||
* Return OK when a message was handled, there might be another one.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
may_invoke_callback(int idx)
|
||||
{
|
||||
char_u *msg = NULL;
|
||||
@@ -710,23 +744,19 @@ may_invoke_callback(int idx)
|
||||
int seq_nr = -1;
|
||||
int json_mode = channels[idx].ch_json_mode;
|
||||
|
||||
if (channel_peek(idx) == NULL)
|
||||
return;
|
||||
if (channels[idx].ch_close_cb != NULL)
|
||||
/* this channel is handled elsewhere (netbeans) */
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
if (json_mode)
|
||||
{
|
||||
/* Get any json message. Return if there isn't one. */
|
||||
channel_read_json(idx);
|
||||
/* Get any json message in the queue. */
|
||||
if (channel_get_json(idx, -1, &listtv) == FAIL)
|
||||
return;
|
||||
if (listtv->v_type != VAR_LIST)
|
||||
{
|
||||
/* TODO: give error */
|
||||
clear_tv(listtv);
|
||||
return;
|
||||
/* Parse readahead, return when there is still no message. */
|
||||
channel_parse_json(idx);
|
||||
if (channel_get_json(idx, -1, &listtv) == FAIL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
list = listtv->vval.v_list;
|
||||
@@ -734,7 +764,7 @@ may_invoke_callback(int idx)
|
||||
{
|
||||
/* TODO: give error */
|
||||
clear_tv(listtv);
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
argv[1] = list->lv_first->li_next->li_tv;
|
||||
@@ -744,22 +774,27 @@ may_invoke_callback(int idx)
|
||||
typval_T *arg3 = NULL;
|
||||
char_u *cmd = typetv->vval.v_string;
|
||||
|
||||
/* ["cmd", arg] */
|
||||
/* ["cmd", arg] or ["cmd", arg, arg] */
|
||||
if (list->lv_len == 3)
|
||||
arg3 = &list->lv_last->li_tv;
|
||||
channel_exe_cmd(idx, cmd, &argv[1], arg3);
|
||||
clear_tv(listtv);
|
||||
return;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (typetv->v_type != VAR_NUMBER)
|
||||
{
|
||||
/* TODO: give error */
|
||||
clear_tv(listtv);
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
seq_nr = typetv->vval.v_number;
|
||||
}
|
||||
else if (channel_peek(idx) == NULL)
|
||||
{
|
||||
/* nothing to read on raw channel */
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For a raw channel we don't know where the message ends, just get
|
||||
@@ -786,6 +821,8 @@ may_invoke_callback(int idx)
|
||||
if (listtv != NULL)
|
||||
clear_tv(listtv);
|
||||
vim_free(msg);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1071,19 +1108,30 @@ channel_read_block(int idx)
|
||||
int
|
||||
channel_read_json_block(int ch_idx, int id, typval_T **rettv)
|
||||
{
|
||||
int more;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
channel_read_json(ch_idx);
|
||||
more = channel_parse_json(ch_idx);
|
||||
|
||||
/* search for messsage "id" */
|
||||
if (channel_get_json(ch_idx, id, rettv) == OK)
|
||||
return OK;
|
||||
|
||||
/* Wait for up to 2 seconds.
|
||||
* TODO: use timeout set on the channel. */
|
||||
if (channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL)
|
||||
break;
|
||||
channel_read(ch_idx);
|
||||
if (!more)
|
||||
{
|
||||
/* Handle any other messages in the queue. If done some more
|
||||
* messages may have arrived. */
|
||||
if (channel_parse_messages())
|
||||
continue;
|
||||
|
||||
/* Wait for up to 2 seconds.
|
||||
* TODO: use timeout set on the channel. */
|
||||
if (channels[ch_idx].ch_fd < 0
|
||||
|| channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL)
|
||||
break;
|
||||
channel_read(ch_idx);
|
||||
}
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
@@ -1237,15 +1285,23 @@ channel_select_check(int ret_in, void *rfds_in)
|
||||
# endif /* !FEAT_GUI_W32 && HAVE_SELECT */
|
||||
|
||||
/*
|
||||
* Invoked from the main loop when it's save to execute received commands.
|
||||
* Execute queued up commands.
|
||||
* Invoked from the main loop when it's safe to execute received commands.
|
||||
* Return TRUE when something was done.
|
||||
*/
|
||||
void
|
||||
int
|
||||
channel_parse_messages(void)
|
||||
{
|
||||
int i;
|
||||
int ret = FALSE;
|
||||
|
||||
for (i = 0; i < channel_count; ++i)
|
||||
may_invoke_callback(i);
|
||||
while (may_invoke_callback(i) == OK)
|
||||
{
|
||||
i = 0; /* start over */
|
||||
ret = TRUE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* FEAT_CHANNEL */
|
||||
|
||||
455
src/eval.c
455
src/eval.c
@@ -499,6 +499,12 @@ static void f_call(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_FLOAT
|
||||
static void f_ceil(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
#ifdef FEAT_CHANNEL
|
||||
static void f_ch_open(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_close(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_changenr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_char2nr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_cindent(typval_T *argvars, typval_T *rettv);
|
||||
@@ -515,9 +521,6 @@ static void f_copy(typval_T *argvars, typval_T *rettv);
|
||||
static void f_cos(typval_T *argvars, typval_T *rettv);
|
||||
static void f_cosh(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
#ifdef FEAT_CHANNEL
|
||||
static void f_connect(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_count(typval_T *argvars, typval_T *rettv);
|
||||
static void f_cscope_connection(typval_T *argvars, typval_T *rettv);
|
||||
static void f_cursor(typval_T *argsvars, typval_T *rettv);
|
||||
@@ -526,9 +529,6 @@ static void f_delete(typval_T *argvars, typval_T *rettv);
|
||||
static void f_did_filetype(typval_T *argvars, typval_T *rettv);
|
||||
static void f_diff_filler(typval_T *argvars, typval_T *rettv);
|
||||
static void f_diff_hlID(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_CHANNEL
|
||||
static void f_disconnect(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_empty(typval_T *argvars, typval_T *rettv);
|
||||
static void f_escape(typval_T *argvars, typval_T *rettv);
|
||||
static void f_eval(typval_T *argvars, typval_T *rettv);
|
||||
@@ -703,10 +703,6 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv);
|
||||
static void f_searchpair(typval_T *argvars, typval_T *rettv);
|
||||
static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
|
||||
static void f_searchpos(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_CHANNEL
|
||||
static void f_sendexpr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_sendraw(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_server2client(typval_T *argvars, typval_T *rettv);
|
||||
static void f_serverlist(typval_T *argvars, typval_T *rettv);
|
||||
static void f_setbufvar(typval_T *argvars, typval_T *rettv);
|
||||
@@ -8002,6 +7998,12 @@ static struct fst
|
||||
{"call", 2, 3, f_call},
|
||||
#ifdef FEAT_FLOAT
|
||||
{"ceil", 1, 1, f_ceil},
|
||||
#endif
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"ch_close", 1, 1, f_ch_close},
|
||||
{"ch_open", 2, 3, f_ch_open},
|
||||
{"ch_sendexpr", 2, 3, f_ch_sendexpr},
|
||||
{"ch_sendraw", 2, 3, f_ch_sendraw},
|
||||
#endif
|
||||
{"changenr", 0, 0, f_changenr},
|
||||
{"char2nr", 1, 2, f_char2nr},
|
||||
@@ -8014,9 +8016,6 @@ static struct fst
|
||||
{"complete_check", 0, 0, f_complete_check},
|
||||
#endif
|
||||
{"confirm", 1, 4, f_confirm},
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"connect", 2, 3, f_connect},
|
||||
#endif
|
||||
{"copy", 1, 1, f_copy},
|
||||
#ifdef FEAT_FLOAT
|
||||
{"cos", 1, 1, f_cos},
|
||||
@@ -8030,9 +8029,6 @@ static struct fst
|
||||
{"did_filetype", 0, 0, f_did_filetype},
|
||||
{"diff_filler", 1, 1, f_diff_filler},
|
||||
{"diff_hlID", 2, 2, f_diff_hlID},
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"disconnect", 1, 1, f_disconnect},
|
||||
#endif
|
||||
{"empty", 1, 1, f_empty},
|
||||
{"escape", 2, 2, f_escape},
|
||||
{"eval", 1, 1, f_eval},
|
||||
@@ -8211,10 +8207,6 @@ static struct fst
|
||||
{"searchpair", 3, 7, f_searchpair},
|
||||
{"searchpairpos", 3, 7, f_searchpairpos},
|
||||
{"searchpos", 1, 4, f_searchpos},
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"sendexpr", 2, 3, f_sendexpr},
|
||||
{"sendraw", 2, 3, f_sendraw},
|
||||
#endif
|
||||
{"server2client", 2, 2, f_server2client},
|
||||
{"serverlist", 0, 0, f_serverlist},
|
||||
{"setbufvar", 3, 3, f_setbufvar},
|
||||
@@ -9685,6 +9677,213 @@ f_ceil(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_CHANNEL
|
||||
/*
|
||||
* Get the channel index from the handle argument.
|
||||
* Returns -1 if the handle is invalid or the channel is closed.
|
||||
*/
|
||||
static int
|
||||
get_channel_arg(typval_T *tv)
|
||||
{
|
||||
int ch_idx;
|
||||
|
||||
if (tv->v_type != VAR_NUMBER)
|
||||
{
|
||||
EMSG2(_(e_invarg2), get_tv_string(tv));
|
||||
return -1;
|
||||
}
|
||||
ch_idx = tv->vval.v_number;
|
||||
|
||||
if (!channel_is_open(ch_idx))
|
||||
{
|
||||
EMSGN(_("E906: not an open channel"), ch_idx);
|
||||
return -1;
|
||||
}
|
||||
return ch_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_close()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
int ch_idx = get_channel_arg(&argvars[0]);
|
||||
|
||||
if (ch_idx >= 0)
|
||||
channel_close(ch_idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_open()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_open(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *address;
|
||||
char_u *mode;
|
||||
char_u *callback = NULL;
|
||||
char_u buf1[NUMBUFLEN];
|
||||
char_u *p;
|
||||
int port;
|
||||
int json_mode = FALSE;
|
||||
|
||||
/* default: fail */
|
||||
rettv->vval.v_number = -1;
|
||||
|
||||
address = get_tv_string(&argvars[0]);
|
||||
mode = get_tv_string_buf(&argvars[1], buf1);
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
callback = get_callback(&argvars[2]);
|
||||
if (callback == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse address */
|
||||
p = vim_strchr(address, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
EMSG2(_(e_invarg2), address);
|
||||
return;
|
||||
}
|
||||
*p++ = NUL;
|
||||
port = atoi((char *)p);
|
||||
if (*address == NUL || port <= 0)
|
||||
{
|
||||
p[-1] = ':';
|
||||
EMSG2(_(e_invarg2), address);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse mode */
|
||||
if (STRCMP(mode, "json") == 0)
|
||||
json_mode = TRUE;
|
||||
else if (STRCMP(mode, "raw") != 0)
|
||||
{
|
||||
EMSG2(_(e_invarg2), mode);
|
||||
return;
|
||||
}
|
||||
|
||||
rettv->vval.v_number = channel_open((char *)address, port, NULL);
|
||||
if (rettv->vval.v_number >= 0)
|
||||
{
|
||||
channel_set_json_mode(rettv->vval.v_number, json_mode);
|
||||
if (callback != NULL && *callback != NUL)
|
||||
channel_set_callback(rettv->vval.v_number, callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* common for "sendexpr()" and "sendraw()"
|
||||
* Returns the channel index if the caller should read the response.
|
||||
* Otherwise returns -1.
|
||||
*/
|
||||
static int
|
||||
send_common(typval_T *argvars, char_u *text, char *fun)
|
||||
{
|
||||
int ch_idx;
|
||||
char_u *callback = NULL;
|
||||
|
||||
ch_idx = get_channel_arg(&argvars[0]);
|
||||
if (ch_idx < 0)
|
||||
return -1;
|
||||
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
callback = get_callback(&argvars[2]);
|
||||
if (callback == NULL)
|
||||
return -1;
|
||||
}
|
||||
/* Set the callback or clear it. An empty callback means no callback and
|
||||
* not reading the response. */
|
||||
channel_set_req_callback(ch_idx,
|
||||
callback != NULL && *callback == NUL ? NULL : callback);
|
||||
|
||||
if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
|
||||
return ch_idx;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_sendexpr()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *text;
|
||||
typval_T *listtv;
|
||||
int ch_idx;
|
||||
int id;
|
||||
|
||||
/* return an empty string by default */
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
id = channel_get_id();
|
||||
text = json_encode_nr_expr(id, &argvars[1]);
|
||||
if (text == NULL)
|
||||
return;
|
||||
|
||||
ch_idx = send_common(argvars, text, "sendexpr");
|
||||
if (ch_idx >= 0)
|
||||
{
|
||||
if (channel_read_json_block(ch_idx, id, &listtv) == OK)
|
||||
{
|
||||
if (listtv->v_type == VAR_LIST)
|
||||
{
|
||||
list_T *list = listtv->vval.v_list;
|
||||
|
||||
if (list->lv_len == 2)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
clear_tv(listtv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_sendraw()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_sendraw(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u buf[NUMBUFLEN];
|
||||
char_u *text;
|
||||
int ch_idx;
|
||||
|
||||
/* return an empty string by default */
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
text = get_tv_string_buf(&argvars[1], buf);
|
||||
ch_idx = send_common(argvars, text, "sendraw");
|
||||
if (ch_idx >= 0)
|
||||
rettv->vval.v_string = channel_read_block(ch_idx);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "changenr()" function
|
||||
*/
|
||||
@@ -10033,84 +10232,6 @@ f_count(typval_T *argvars, typval_T *rettv)
|
||||
rettv->vval.v_number = n;
|
||||
}
|
||||
|
||||
#ifdef FEAT_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;
|
||||
}
|
||||
|
||||
/*
|
||||
* "connect()" function
|
||||
*/
|
||||
static void
|
||||
f_connect(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *address;
|
||||
char_u *mode;
|
||||
char_u *callback = NULL;
|
||||
char_u buf1[NUMBUFLEN];
|
||||
char_u *p;
|
||||
int port;
|
||||
int json_mode = FALSE;
|
||||
|
||||
/* default: fail */
|
||||
rettv->vval.v_number = -1;
|
||||
|
||||
address = get_tv_string(&argvars[0]);
|
||||
mode = get_tv_string_buf(&argvars[1], buf1);
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
callback = get_callback(&argvars[2]);
|
||||
if (callback == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse address */
|
||||
p = vim_strchr(address, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
EMSG2(_(e_invarg2), address);
|
||||
return;
|
||||
}
|
||||
*p++ = NUL;
|
||||
port = atoi((char *)p);
|
||||
if (*address == NUL || port <= 0)
|
||||
{
|
||||
p[-1] = ':';
|
||||
EMSG2(_(e_invarg2), address);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse mode */
|
||||
if (STRCMP(mode, "json") == 0)
|
||||
json_mode = TRUE;
|
||||
else if (STRCMP(mode, "raw") != 0)
|
||||
{
|
||||
EMSG2(_(e_invarg2), mode);
|
||||
return;
|
||||
}
|
||||
|
||||
rettv->vval.v_number = channel_open((char *)address, port, NULL);
|
||||
if (rettv->vval.v_number >= 0)
|
||||
{
|
||||
channel_set_json_mode(rettv->vval.v_number, json_mode);
|
||||
if (callback != NULL && *callback != NUL)
|
||||
channel_set_callback(rettv->vval.v_number, callback);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
|
||||
*
|
||||
@@ -10349,44 +10470,6 @@ f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FEAT_CHANNEL
|
||||
/*
|
||||
* Get the channel index from the handle argument.
|
||||
* Returns -1 if the handle is invalid or the channel is closed.
|
||||
*/
|
||||
static int
|
||||
get_channel_arg(typval_T *tv)
|
||||
{
|
||||
int ch_idx;
|
||||
|
||||
if (tv->v_type != VAR_NUMBER)
|
||||
{
|
||||
EMSG2(_(e_invarg2), get_tv_string(tv));
|
||||
return -1;
|
||||
}
|
||||
ch_idx = tv->vval.v_number;
|
||||
|
||||
if (!channel_is_open(ch_idx))
|
||||
{
|
||||
EMSGN(_("E906: not an open channel"), ch_idx);
|
||||
return -1;
|
||||
}
|
||||
return ch_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* "disconnect()" function
|
||||
*/
|
||||
static void
|
||||
f_disconnect(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
int ch_idx = get_channel_arg(&argvars[0]);
|
||||
|
||||
if (ch_idx >= 0)
|
||||
channel_close(ch_idx);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "empty({expr})" function
|
||||
*/
|
||||
@@ -14100,9 +14183,9 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
|
||||
js_read_T reader;
|
||||
|
||||
reader.js_buf = get_tv_string(&argvars[0]);
|
||||
reader.js_eof = TRUE;
|
||||
reader.js_fill = NULL;
|
||||
reader.js_used = 0;
|
||||
if (json_decode(&reader, rettv) == FAIL)
|
||||
if (json_decode_all(&reader, rettv) != OK)
|
||||
EMSG(_(e_invarg));
|
||||
}
|
||||
|
||||
@@ -16860,102 +16943,6 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
|
||||
list_append_number(rettv->vval.v_list, (varnumber_T)n);
|
||||
}
|
||||
|
||||
#ifdef FEAT_CHANNEL
|
||||
/*
|
||||
* common for "sendexpr()" and "sendraw()"
|
||||
* Returns the channel index if the caller should read the response.
|
||||
* Otherwise returns -1.
|
||||
*/
|
||||
static int
|
||||
send_common(typval_T *argvars, char_u *text, char *fun)
|
||||
{
|
||||
int ch_idx;
|
||||
char_u *callback = NULL;
|
||||
|
||||
ch_idx = get_channel_arg(&argvars[0]);
|
||||
if (ch_idx < 0)
|
||||
return -1;
|
||||
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
callback = get_callback(&argvars[2]);
|
||||
if (callback == NULL)
|
||||
return -1;
|
||||
}
|
||||
/* Set the callback or clear it. An empty callback means no callback and
|
||||
* not reading the response. */
|
||||
channel_set_req_callback(ch_idx,
|
||||
callback != NULL && *callback == NUL ? NULL : callback);
|
||||
|
||||
if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
|
||||
return ch_idx;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "sendexpr()" function
|
||||
*/
|
||||
static void
|
||||
f_sendexpr(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *text;
|
||||
typval_T *listtv;
|
||||
int ch_idx;
|
||||
int id;
|
||||
|
||||
/* return an empty string by default */
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
id = channel_get_id();
|
||||
text = json_encode_nr_expr(id, &argvars[1]);
|
||||
if (text == NULL)
|
||||
return;
|
||||
|
||||
ch_idx = send_common(argvars, text, "sendexpr");
|
||||
if (ch_idx >= 0)
|
||||
{
|
||||
if (channel_read_json_block(ch_idx, id, &listtv) == OK)
|
||||
{
|
||||
if (listtv->v_type == VAR_LIST)
|
||||
{
|
||||
list_T *list = listtv->vval.v_list;
|
||||
|
||||
if (list->lv_len == 2)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
clear_tv(listtv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "sendraw()" function
|
||||
*/
|
||||
static void
|
||||
f_sendraw(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u buf[NUMBUFLEN];
|
||||
char_u *text;
|
||||
int ch_idx;
|
||||
|
||||
/* return an empty string by default */
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
text = get_tv_string_buf(&argvars[1], buf);
|
||||
ch_idx = send_common(argvars, text, "sendraw");
|
||||
if (ch_idx >= 0)
|
||||
rettv->vval.v_string = channel_read_block(ch_idx);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
{
|
||||
|
||||
429
src/json.c
429
src/json.c
@@ -17,7 +17,7 @@
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
static int json_encode_item(garray_T *gap, typval_T *val, int copyID);
|
||||
static void json_decode_item(js_read_T *reader, typval_T *res);
|
||||
static int json_decode_item(js_read_T *reader, typval_T *res);
|
||||
|
||||
/*
|
||||
* Encode "val" into a JSON format string.
|
||||
@@ -234,37 +234,60 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* When "reader" has less than NUMBUFLEN bytes available, call the fill
|
||||
* callback to get more.
|
||||
*/
|
||||
static void
|
||||
fill_numbuflen(js_read_T *reader)
|
||||
{
|
||||
if (reader->js_fill != NULL && (int)(reader->js_end - reader->js_buf)
|
||||
- reader->js_used < NUMBUFLEN)
|
||||
{
|
||||
if (reader->js_fill(reader))
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip white space in "reader".
|
||||
* Also tops up readahead when needed.
|
||||
*/
|
||||
static void
|
||||
json_skip_white(js_read_T *reader)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = reader->js_buf[reader->js_used]) == ' '
|
||||
|| c == TAB || c == NL || c == CAR)
|
||||
for (;;)
|
||||
{
|
||||
c = reader->js_buf[reader->js_used];
|
||||
if (reader->js_fill != NULL && c == NUL)
|
||||
{
|
||||
if (reader->js_fill(reader))
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
continue;
|
||||
}
|
||||
if (c != ' ' && c != TAB && c != NL && c != CAR)
|
||||
break;
|
||||
++reader->js_used;
|
||||
}
|
||||
fill_numbuflen(reader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure there are at least enough characters buffered to read a number.
|
||||
*/
|
||||
static void
|
||||
json_fill_buffer(js_read_T *reader UNUSED)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
json_decode_array(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
char_u *p;
|
||||
typval_T item;
|
||||
listitem_T *li;
|
||||
int ret;
|
||||
|
||||
if (rettv_list_alloc(res) == FAIL)
|
||||
goto failsilent;
|
||||
if (res != NULL && rettv_list_alloc(res) == FAIL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
return FAIL;
|
||||
}
|
||||
++reader->js_used; /* consume the '[' */
|
||||
|
||||
while (TRUE)
|
||||
@@ -272,38 +295,43 @@ json_decode_array(js_read_T *reader, typval_T *res)
|
||||
json_skip_white(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
if (*p == NUL)
|
||||
goto fail;
|
||||
return MAYBE;
|
||||
if (*p == ']')
|
||||
{
|
||||
++reader->js_used; /* consume the ']' */
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
|
||||
json_fill_buffer(reader);
|
||||
|
||||
json_decode_item(reader, &item);
|
||||
li = listitem_alloc();
|
||||
if (li == NULL)
|
||||
return;
|
||||
li->li_tv = item;
|
||||
list_append(res->vval.v_list, li);
|
||||
ret = json_decode_item(reader, res == NULL ? NULL : &item);
|
||||
if (ret != OK)
|
||||
return ret;
|
||||
if (res != NULL)
|
||||
{
|
||||
li = listitem_alloc();
|
||||
if (li == NULL)
|
||||
{
|
||||
clear_tv(&item);
|
||||
return FAIL;
|
||||
}
|
||||
li->li_tv = item;
|
||||
list_append(res->vval.v_list, li);
|
||||
}
|
||||
|
||||
json_skip_white(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
if (*p == ',')
|
||||
++reader->js_used;
|
||||
else if (*p != ']')
|
||||
goto fail;
|
||||
{
|
||||
if (*p == NUL)
|
||||
return MAYBE;
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
fail:
|
||||
EMSG(_(e_invarg));
|
||||
failsilent:
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
json_decode_object(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
char_u *p;
|
||||
@@ -311,10 +339,15 @@ json_decode_object(js_read_T *reader, typval_T *res)
|
||||
typval_T item;
|
||||
dictitem_T *di;
|
||||
char_u buf[NUMBUFLEN];
|
||||
char_u *key;
|
||||
char_u *key = NULL;
|
||||
int ret;
|
||||
|
||||
if (rettv_dict_alloc(res) == FAIL)
|
||||
goto failsilent;
|
||||
if (res != NULL && rettv_dict_alloc(res) == FAIL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
return FAIL;
|
||||
}
|
||||
++reader->js_used; /* consume the '{' */
|
||||
|
||||
while (TRUE)
|
||||
@@ -322,243 +355,387 @@ json_decode_object(js_read_T *reader, typval_T *res)
|
||||
json_skip_white(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
if (*p == NUL)
|
||||
goto fail;
|
||||
return MAYBE;
|
||||
if (*p == '}')
|
||||
{
|
||||
++reader->js_used; /* consume the '}' */
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
|
||||
json_fill_buffer(reader);
|
||||
json_decode_item(reader, &tvkey);
|
||||
key = get_tv_string_buf_chk(&tvkey, buf);
|
||||
if (key == NULL || *key == NUL)
|
||||
ret = json_decode_item(reader, res == NULL ? NULL : &tvkey);
|
||||
if (ret != OK)
|
||||
return ret;
|
||||
if (res != NULL)
|
||||
{
|
||||
/* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
|
||||
if (key != NULL)
|
||||
EMSG(_(e_emptykey));
|
||||
clear_tv(&tvkey);
|
||||
goto failsilent;
|
||||
key = get_tv_string_buf_chk(&tvkey, buf);
|
||||
if (key == NULL || *key == NUL)
|
||||
{
|
||||
clear_tv(&tvkey);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
json_skip_white(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
if (*p != ':')
|
||||
{
|
||||
clear_tv(&tvkey);
|
||||
goto fail;
|
||||
if (res != NULL)
|
||||
clear_tv(&tvkey);
|
||||
if (*p == NUL)
|
||||
return MAYBE;
|
||||
return FAIL;
|
||||
}
|
||||
++reader->js_used;
|
||||
json_skip_white(reader);
|
||||
|
||||
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
|
||||
json_fill_buffer(reader);
|
||||
json_decode_item(reader, &item);
|
||||
|
||||
di = dictitem_alloc(key);
|
||||
clear_tv(&tvkey);
|
||||
if (di == NULL)
|
||||
ret = json_decode_item(reader, res == NULL ? NULL : &item);
|
||||
if (ret != OK)
|
||||
{
|
||||
clear_tv(&item);
|
||||
goto fail;
|
||||
if (res != NULL)
|
||||
clear_tv(&tvkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (res != NULL)
|
||||
{
|
||||
di = dictitem_alloc(key);
|
||||
clear_tv(&tvkey);
|
||||
if (di == NULL)
|
||||
{
|
||||
clear_tv(&item);
|
||||
return FAIL;
|
||||
}
|
||||
di->di_tv = item;
|
||||
if (dict_add(res->vval.v_dict, di) == FAIL)
|
||||
{
|
||||
dictitem_free(di);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
di->di_tv = item;
|
||||
if (dict_add(res->vval.v_dict, di) == FAIL)
|
||||
dictitem_free(di);
|
||||
|
||||
json_skip_white(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
if (*p == ',')
|
||||
++reader->js_used;
|
||||
else if (*p != '}')
|
||||
goto fail;
|
||||
{
|
||||
if (*p == NUL)
|
||||
return MAYBE;
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
fail:
|
||||
EMSG(_(e_invarg));
|
||||
failsilent:
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
json_decode_string(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
garray_T ga;
|
||||
int len;
|
||||
char_u *p = reader->js_buf + reader->js_used + 1;
|
||||
char_u *p;
|
||||
int c;
|
||||
long nr;
|
||||
char_u buf[NUMBUFLEN];
|
||||
|
||||
ga_init2(&ga, 1, 200);
|
||||
if (res != NULL)
|
||||
ga_init2(&ga, 1, 200);
|
||||
|
||||
/* TODO: fill buffer when needed. */
|
||||
while (*p != NUL && *p != '"')
|
||||
p = reader->js_buf + reader->js_used + 1; /* skip over " */
|
||||
while (*p != '"')
|
||||
{
|
||||
if (*p == NUL || p[1] == NUL
|
||||
#ifdef FEAT_MBYTE
|
||||
|| utf_ptr2len(p) < utf_byte2len(*p)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (reader->js_fill == NULL)
|
||||
break;
|
||||
len = (int)(reader->js_end - p);
|
||||
reader->js_used = (int)(p - reader->js_buf);
|
||||
if (!reader->js_fill(reader))
|
||||
break; /* didn't get more */
|
||||
p = reader->js_buf + reader->js_used;
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*p == '\\')
|
||||
{
|
||||
c = -1;
|
||||
switch (p[1])
|
||||
{
|
||||
case '\\': c = '\\'; break;
|
||||
case '"': c = '"'; break;
|
||||
case 'b': c = BS; break;
|
||||
case 't': c = TAB; break;
|
||||
case 'n': c = NL; break;
|
||||
case 'f': c = FF; break;
|
||||
case 'r': c = CAR; break;
|
||||
case 'u':
|
||||
if (reader->js_fill != NULL
|
||||
&& (int)(reader->js_end - p) < NUMBUFLEN)
|
||||
{
|
||||
reader->js_used = (int)(p - reader->js_buf);
|
||||
if (reader->js_fill(reader))
|
||||
{
|
||||
p = reader->js_buf + reader->js_used;
|
||||
reader->js_end = reader->js_buf
|
||||
+ STRLEN(reader->js_buf);
|
||||
}
|
||||
}
|
||||
vim_str2nr(p + 2, NULL, &len,
|
||||
STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
|
||||
p += len + 2;
|
||||
if (res != NULL)
|
||||
{
|
||||
#ifdef FEAT_MBYTE
|
||||
buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
|
||||
ga_concat(&ga, buf);
|
||||
buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
|
||||
ga_concat(&ga, buf);
|
||||
#else
|
||||
ga_append(&ga, nr);
|
||||
ga_append(&ga, nr);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
default: c = p[1]; break;
|
||||
default:
|
||||
/* not a special char, skip over \ */
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
if (c > 0)
|
||||
{
|
||||
p += 2;
|
||||
ga_append(&ga, c);
|
||||
if (res != NULL)
|
||||
ga_append(&ga, c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
len = MB_PTR2LEN(p);
|
||||
if (ga_grow(&ga, len) == OK)
|
||||
if (res != NULL)
|
||||
{
|
||||
if (ga_grow(&ga, len) == FAIL)
|
||||
{
|
||||
ga_clear(&ga);
|
||||
return FAIL;
|
||||
}
|
||||
mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
|
||||
ga.ga_len += len;
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
|
||||
{
|
||||
reader->js_used = (int)(p - reader->js_buf);
|
||||
json_fill_buffer(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
}
|
||||
}
|
||||
|
||||
reader->js_used = (int)(p - reader->js_buf);
|
||||
if (*p == '"')
|
||||
{
|
||||
++reader->js_used;
|
||||
res->v_type = VAR_STRING;
|
||||
if (ga.ga_data == NULL)
|
||||
res->vval.v_string = NULL;
|
||||
else
|
||||
res->vval.v_string = vim_strsave(ga.ga_data);
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_STRING;
|
||||
if (ga.ga_data == NULL)
|
||||
res->vval.v_string = NULL;
|
||||
else
|
||||
res->vval.v_string = vim_strsave(ga.ga_data);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
if (res != NULL)
|
||||
{
|
||||
EMSG(_(e_invarg));
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
ga_clear(&ga);
|
||||
}
|
||||
ga_clear(&ga);
|
||||
return MAYBE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode one item and put it in "result".
|
||||
* Decode one item and put it in "res". If "res" is NULL only advance.
|
||||
* Must already have skipped white space.
|
||||
*
|
||||
* Return FAIL for a decoding error.
|
||||
* Return MAYBE for an incomplete message.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
json_decode_item(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
char_u *p = reader->js_buf + reader->js_used;
|
||||
char_u *p;
|
||||
int len;
|
||||
|
||||
fill_numbuflen(reader);
|
||||
p = reader->js_buf + reader->js_used;
|
||||
switch (*p)
|
||||
{
|
||||
case '[': /* array */
|
||||
json_decode_array(reader, res);
|
||||
return;
|
||||
return json_decode_array(reader, res);
|
||||
|
||||
case '{': /* object */
|
||||
json_decode_object(reader, res);
|
||||
return;
|
||||
return json_decode_object(reader, res);
|
||||
|
||||
case '"': /* string */
|
||||
json_decode_string(reader, res);
|
||||
return;
|
||||
return json_decode_string(reader, res);
|
||||
|
||||
case ',': /* comma: empty item */
|
||||
case NUL: /* empty */
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
return;
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
}
|
||||
return OK;
|
||||
|
||||
default:
|
||||
if (VIM_ISDIGIT(*p) || *p == '-')
|
||||
{
|
||||
int len;
|
||||
char_u *sp = p;
|
||||
|
||||
#ifdef FEAT_FLOAT
|
||||
if (*sp == '-')
|
||||
{
|
||||
++sp;
|
||||
if (*sp == NUL)
|
||||
return MAYBE;
|
||||
if (!VIM_ISDIGIT(*sp))
|
||||
return FAIL;
|
||||
}
|
||||
sp = skipdigits(sp);
|
||||
if (*sp == '.' || *sp == 'e' || *sp == 'E')
|
||||
{
|
||||
res->v_type = VAR_FLOAT;
|
||||
len = string2float(p, &res->vval.v_float);
|
||||
if (res == NULL)
|
||||
{
|
||||
float_T f;
|
||||
|
||||
len = string2float(p, &f);
|
||||
}
|
||||
else
|
||||
{
|
||||
res->v_type = VAR_FLOAT;
|
||||
len = string2float(p, &res->vval.v_float);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
long nr;
|
||||
|
||||
res->v_type = VAR_NUMBER;
|
||||
vim_str2nr(reader->js_buf + reader->js_used,
|
||||
NULL, &len, 0, /* what */
|
||||
&nr, NULL, 0);
|
||||
res->vval.v_number = nr;
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_NUMBER;
|
||||
res->vval.v_number = nr;
|
||||
}
|
||||
}
|
||||
reader->js_used += len;
|
||||
return;
|
||||
return OK;
|
||||
}
|
||||
if (STRNICMP((char *)p, "false", 5) == 0)
|
||||
{
|
||||
reader->js_used += 5;
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_FALSE;
|
||||
return;
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_FALSE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
if (STRNICMP((char *)p, "true", 4) == 0)
|
||||
{
|
||||
reader->js_used += 4;
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_TRUE;
|
||||
return;
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_TRUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
if (STRNICMP((char *)p, "null", 4) == 0)
|
||||
{
|
||||
reader->js_used += 4;
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NULL;
|
||||
return;
|
||||
if (res != NULL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NULL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
/* check for truncated name */
|
||||
len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
|
||||
if ((len < 5 && STRNICMP((char *)p, "false", len) == 0)
|
||||
|| (len < 4 && (STRNICMP((char *)p, "true", len) == 0
|
||||
|| STRNICMP((char *)p, "null", len) == 0)))
|
||||
return MAYBE;
|
||||
break;
|
||||
}
|
||||
|
||||
EMSG(_(e_invarg));
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
if (res != NUL)
|
||||
{
|
||||
res->v_type = VAR_SPECIAL;
|
||||
res->vval.v_number = VVAL_NONE;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the JSON from "reader" and store the result in "res".
|
||||
* Return OK or FAIL;
|
||||
* Return FAIL if not the whole message was consumed.
|
||||
*/
|
||||
int
|
||||
json_decode(js_read_T *reader, typval_T *res)
|
||||
json_decode_all(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* We get the end once, to avoid calling strlen() many times. */
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
json_skip_white(reader);
|
||||
json_decode_item(reader, res);
|
||||
ret = json_decode_item(reader, res);
|
||||
if (ret != OK)
|
||||
return FAIL;
|
||||
json_skip_white(reader);
|
||||
if (reader->js_buf[reader->js_used] != NUL)
|
||||
return FAIL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the JSON from "reader" and store the result in "res".
|
||||
* Return FAIL if the message has a decoding error or the message is
|
||||
* truncated. Consumes the message anyway.
|
||||
*/
|
||||
int
|
||||
json_decode(js_read_T *reader, typval_T *res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* We get the end once, to avoid calling strlen() many times. */
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
json_skip_white(reader);
|
||||
ret = json_decode_item(reader, res);
|
||||
json_skip_white(reader);
|
||||
|
||||
return ret == OK ? OK : FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the JSON from "reader" to find the end of the message.
|
||||
* Return FAIL if the message has a decoding error.
|
||||
* Return MAYBE if the message is truncated, need to read more.
|
||||
* This only works reliable if the message contains an object, array or
|
||||
* string. A number might be trucated without knowing.
|
||||
* Does not advance the reader.
|
||||
*/
|
||||
int
|
||||
json_find_end(js_read_T *reader)
|
||||
{
|
||||
int used_save = reader->js_used;
|
||||
int ret;
|
||||
|
||||
/* We get the end once, to avoid calling strlen() many times. */
|
||||
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
|
||||
json_skip_white(reader);
|
||||
ret = json_decode_item(reader, NULL);
|
||||
reader->js_used = used_save;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
197
src/json_test.c
Normal file
197
src/json_test.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/* vi:set ts=8 sts=4 sw=4:
|
||||
*
|
||||
* VIM - Vi IMproved by Bram Moolenaar
|
||||
*
|
||||
* Do ":help uganda" in Vim to read copying and usage conditions.
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* json_test.c: Unittests for json.c
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
/* Must include main.c because it contains much more than just main() */
|
||||
#define NO_VIM_MAIN
|
||||
#include "main.c"
|
||||
|
||||
/* This file has to be included because the tested functions are static */
|
||||
#include "json.c"
|
||||
|
||||
#if defined(FEAT_EVAL)
|
||||
/*
|
||||
* Test json_find_end() with imcomplete items.
|
||||
*/
|
||||
static void
|
||||
test_decode_find_end(void)
|
||||
{
|
||||
js_read_T reader;
|
||||
|
||||
reader.js_fill = NULL;
|
||||
reader.js_used = 0;
|
||||
|
||||
/* string and incomplete string */
|
||||
reader.js_buf = (char_u *)"\"hello\"";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" \"hello\" ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"\"hello";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* number and dash (incomplete number) */
|
||||
reader.js_buf = (char_u *)"123";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"-";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* false, true and null, also incomplete */
|
||||
reader.js_buf = (char_u *)"false";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"f";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"fa";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"fal";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"fals";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
reader.js_buf = (char_u *)"true";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"t";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"tr";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"tru";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
reader.js_buf = (char_u *)"null";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"n";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"nu";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"nul";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* object without white space */
|
||||
reader.js_buf = (char_u *)"{\"a\":123}";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"{\"a\":123";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"{\"a\":";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"{\"a\"";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"{\"a";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"{\"";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"{";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* object with white space */
|
||||
reader.js_buf = (char_u *)" { \"a\" : 123 } ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" { \"a\" : 123 ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" { \"a\" : ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" { \"a\" ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" { \"a ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" { ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* array without white space */
|
||||
reader.js_buf = (char_u *)"[\"a\",123]";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)"[\"a\",123";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"[\"a\",";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"[\"a\"";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"[\"a";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"[\"";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)"[";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
|
||||
/* array with white space */
|
||||
reader.js_buf = (char_u *)" [ \"a\" , 123 ] ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" [ \"a\" , ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" [ \"a\" ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" [ \"a ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
reader.js_buf = (char_u *)" [ ";
|
||||
assert(json_find_end(&reader) == MAYBE);
|
||||
}
|
||||
|
||||
static int
|
||||
fill_from_cookie(js_read_T *reader)
|
||||
{
|
||||
reader->js_buf = reader->js_cookie;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test json_find_end with an incomplete array, calling the fill function.
|
||||
*/
|
||||
static void
|
||||
test_fill_called_on_find_end(void)
|
||||
{
|
||||
js_read_T reader;
|
||||
|
||||
reader.js_fill = fill_from_cookie;
|
||||
reader.js_used = 0;
|
||||
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
|
||||
reader.js_cookie = " [ \"a\" , 123 ] ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" [ \"a\" , ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" [ \"a\" ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" [ \"a";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
reader.js_buf = (char_u *)" [ ";
|
||||
assert(json_find_end(&reader) == OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test json_find_end with an incomplete string, calling the fill function.
|
||||
*/
|
||||
static void
|
||||
test_fill_called_on_string(void)
|
||||
{
|
||||
js_read_T reader;
|
||||
|
||||
reader.js_fill = fill_from_cookie;
|
||||
reader.js_used = 0;
|
||||
reader.js_buf = (char_u *)" \"foo";
|
||||
reader.js_end = reader.js_buf + STRLEN(reader.js_buf);
|
||||
reader.js_cookie = " \"foobar\" ";
|
||||
assert(json_decode_string(&reader, NULL) == OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
#if defined(FEAT_EVAL)
|
||||
test_decode_find_end();
|
||||
test_fill_called_on_find_end();
|
||||
test_fill_called_on_string();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -25,8 +25,6 @@
|
||||
#define index_to_key(i) ((i) ^ 15167)
|
||||
#define TEST_COUNT 50000
|
||||
|
||||
static void test_mf_hash(void);
|
||||
|
||||
/*
|
||||
* Test mf_hash_*() functions.
|
||||
*/
|
||||
|
||||
@@ -4,23 +4,22 @@ int channel_open(char *hostname, int port_in, void (*close_cb)(void));
|
||||
void channel_set_json_mode(int idx, int json_mode);
|
||||
void channel_set_callback(int idx, char_u *callback);
|
||||
void channel_set_req_callback(int idx, char_u *callback);
|
||||
char_u *channel_get(int idx);
|
||||
int channel_collapse(int idx);
|
||||
int channel_is_open(int idx);
|
||||
void channel_close(int idx);
|
||||
int channel_save(int idx, char_u *buf, int len);
|
||||
char_u *channel_peek(int idx);
|
||||
char_u *channel_get(int idx);
|
||||
int channel_collapse(int idx);
|
||||
void channel_clear(int idx);
|
||||
int channel_get_id(void);
|
||||
void channel_read(int idx);
|
||||
char_u *channel_read_block(int idx);
|
||||
int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
|
||||
void channel_read_json(int ch_idx);
|
||||
int channel_socket2idx(sock_T fd);
|
||||
int channel_send(int idx, char_u *buf, char *fun);
|
||||
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);
|
||||
int channel_select_check(int ret_in, void *rfds_in);
|
||||
void channel_parse_messages(void);
|
||||
int channel_parse_messages(void);
|
||||
/* vim: set ft=c : */
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* json.c */
|
||||
char_u *json_encode(typval_T *val);
|
||||
char_u *json_encode_nr_expr(int nr, typval_T *val);
|
||||
int json_decode_all(js_read_T *reader, typval_T *res);
|
||||
int json_decode(js_read_T *reader, typval_T *res);
|
||||
int json_find_end(js_read_T *reader);
|
||||
/* vim: set ft=c : */
|
||||
|
||||
@@ -2687,12 +2687,14 @@ typedef struct {
|
||||
/*
|
||||
* Structure used for reading in json_decode().
|
||||
*/
|
||||
typedef struct
|
||||
struct js_reader
|
||||
{
|
||||
char_u *js_buf; /* text to be decoded */
|
||||
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
|
||||
char_u *js_end; /* NUL in js_buf */
|
||||
int js_used; /* bytes used from js_buf */
|
||||
int js_eof; /* when TRUE js_buf is all there is */
|
||||
int (*js_fill)(void *); /* function to fill the buffer */
|
||||
void *js_cookie; /* passed to js_fill */
|
||||
} js_read_T;
|
||||
int (*js_fill)(struct js_reader *);
|
||||
/* function to fill the buffer or NULL;
|
||||
* return TRUE when the buffer was filled */
|
||||
void *js_cookie; /* can be used by js_fill */
|
||||
};
|
||||
typedef struct js_reader js_read_T;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# A very (if not the most) simplistic Makefile for MSVC
|
||||
|
||||
CC=cl
|
||||
CFLAGS=/O2
|
||||
CFLAGS=/O2 /nologo
|
||||
|
||||
tee.exe: tee.obj
|
||||
$(CC) $(CFLAGS) /Fo$@ $**
|
||||
|
||||
@@ -171,6 +171,7 @@ SCRIPTS_GUI = test16.out
|
||||
NEW_TESTS = test_arglist.res \
|
||||
test_assert.res \
|
||||
test_cdo.res \
|
||||
test_channel.res \
|
||||
test_hardcopy.res \
|
||||
test_increment.res \
|
||||
test_langmap.res \
|
||||
|
||||
141
src/testdir/test_channel.py
Normal file
141
src/testdir/test_channel.py
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Server that will accept connections from a Vim channel.
|
||||
# Run this server and then in Vim you can open the channel:
|
||||
# :let handle = ch_open('localhost:8765', 'json')
|
||||
#
|
||||
# Then Vim can send requests to the server:
|
||||
# :let response = ch_sendexpr(handle, 'hello!')
|
||||
#
|
||||
# See ":help channel-demo" in Vim.
|
||||
#
|
||||
# This requires Python 2.6 or later.
|
||||
|
||||
from __future__ import print_function
|
||||
import json
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
import socketserver
|
||||
except ImportError:
|
||||
# Python 2
|
||||
import SocketServer as socketserver
|
||||
|
||||
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
print("=== socket opened ===")
|
||||
while True:
|
||||
try:
|
||||
received = self.request.recv(4096).decode('utf-8')
|
||||
except socket.error:
|
||||
print("=== socket error ===")
|
||||
break
|
||||
except IOError:
|
||||
print("=== socket closed ===")
|
||||
break
|
||||
if received == '':
|
||||
print("=== socket closed ===")
|
||||
break
|
||||
print("received: {}".format(received))
|
||||
|
||||
# We may receive two messages at once. Take the part up to the
|
||||
# matching "]" (recognized by finding "][").
|
||||
todo = received
|
||||
while todo != '':
|
||||
splitidx = todo.find('][')
|
||||
if splitidx < 0:
|
||||
used = todo
|
||||
todo = ''
|
||||
else:
|
||||
used = todo[:splitidx + 1]
|
||||
todo = todo[splitidx + 1:]
|
||||
if used != received:
|
||||
print("using: {}".format(used))
|
||||
|
||||
try:
|
||||
decoded = json.loads(used)
|
||||
except ValueError:
|
||||
print("json decoding failed")
|
||||
decoded = [-1, '']
|
||||
|
||||
# Send a response if the sequence number is positive.
|
||||
if decoded[0] >= 0:
|
||||
if decoded[1] == 'hello!':
|
||||
# simply send back a string
|
||||
response = "got it"
|
||||
elif decoded[1] == 'make change':
|
||||
# Send two ex commands at the same time, before
|
||||
# replying to the request.
|
||||
cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
|
||||
cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
|
||||
print("sending: {}".format(cmd))
|
||||
self.request.sendall(cmd.encode('utf-8'))
|
||||
response = "ok"
|
||||
elif decoded[1] == 'eval-works':
|
||||
# Send an eval request. We ignore the response.
|
||||
cmd = '["eval","\\"foo\\" . 123", -1]'
|
||||
print("sending: {}".format(cmd))
|
||||
self.request.sendall(cmd.encode('utf-8'))
|
||||
response = "ok"
|
||||
elif decoded[1] == 'eval-fails':
|
||||
# Send an eval request that will fail.
|
||||
cmd = '["eval","xxx", -2]'
|
||||
print("sending: {}".format(cmd))
|
||||
self.request.sendall(cmd.encode('utf-8'))
|
||||
response = "ok"
|
||||
elif decoded[1] == 'eval-bad':
|
||||
# Send an eval request missing the third argument.
|
||||
cmd = '["eval","xxx"]'
|
||||
print("sending: {}".format(cmd))
|
||||
self.request.sendall(cmd.encode('utf-8'))
|
||||
response = "ok"
|
||||
elif decoded[1] == 'eval-result':
|
||||
# Send back the last received eval result.
|
||||
response = last_eval
|
||||
elif decoded[1] == '!quit!':
|
||||
# we're done
|
||||
self.server.shutdown()
|
||||
break
|
||||
elif decoded[1] == '!crash!':
|
||||
# Crash!
|
||||
42 / 0
|
||||
else:
|
||||
response = "what?"
|
||||
|
||||
encoded = json.dumps([decoded[0], response])
|
||||
print("sending: {}".format(encoded))
|
||||
self.request.sendall(encoded.encode('utf-8'))
|
||||
|
||||
# Negative numbers are used for "eval" responses.
|
||||
elif decoded[0] < 0:
|
||||
last_eval = decoded
|
||||
|
||||
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
HOST, PORT = "localhost", 0
|
||||
|
||||
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
|
||||
ip, port = server.server_address
|
||||
|
||||
# Start a thread with the server -- that thread will then start one
|
||||
# more thread for each request
|
||||
server_thread = threading.Thread(target=server.serve_forever)
|
||||
|
||||
# Exit the server thread when the main thread terminates
|
||||
server_thread.start()
|
||||
|
||||
# Write the port number in Xportnr, so that the test knows it.
|
||||
f = open("Xportnr", "w")
|
||||
f.write("{}".format(port))
|
||||
f.close()
|
||||
|
||||
print("Listening on port {}".format(port))
|
||||
|
||||
# Main thread terminates, but the server continues running
|
||||
# until server.shutdown() is called.
|
||||
139
src/testdir/test_channel.vim
Normal file
139
src/testdir/test_channel.vim
Normal file
@@ -0,0 +1,139 @@
|
||||
" Test for channel functions.
|
||||
scriptencoding utf-8
|
||||
|
||||
if !has('channel')
|
||||
finish
|
||||
endif
|
||||
|
||||
" This test requires the Python command to run the test server.
|
||||
" This most likely only works on Unix and Windows console.
|
||||
if has('unix')
|
||||
" We also need the pkill command to make sure the server can be stopped.
|
||||
if !executable('python') || !executable('pkill')
|
||||
finish
|
||||
endif
|
||||
elseif has('win32') && !has('gui_win32')
|
||||
" Use Python Launcher for Windows (py.exe).
|
||||
if !executable('py')
|
||||
finish
|
||||
endif
|
||||
else
|
||||
finish
|
||||
endif
|
||||
|
||||
let s:port = -1
|
||||
|
||||
func s:start_server()
|
||||
" The Python program writes the port number in Xportnr.
|
||||
call delete("Xportnr")
|
||||
|
||||
if has('win32')
|
||||
silent !start cmd /c start "test_channel" py test_channel.py
|
||||
else
|
||||
silent !python test_channel.py&
|
||||
endif
|
||||
|
||||
" Wait for up to 2 seconds for the port number to be there.
|
||||
let cnt = 20
|
||||
let l = []
|
||||
while cnt > 0
|
||||
try
|
||||
let l = readfile("Xportnr")
|
||||
catch
|
||||
endtry
|
||||
if len(l) >= 1
|
||||
break
|
||||
endif
|
||||
sleep 100m
|
||||
let cnt -= 1
|
||||
endwhile
|
||||
call delete("Xportnr")
|
||||
|
||||
if len(l) == 0
|
||||
" Can't make the connection, give up.
|
||||
call s:kill_server()
|
||||
call assert_false(1, "Can't start test_channel.py")
|
||||
return -1
|
||||
endif
|
||||
let s:port = l[0]
|
||||
|
||||
let handle = ch_open('localhost:' . s:port, 'json')
|
||||
return handle
|
||||
endfunc
|
||||
|
||||
func s:kill_server()
|
||||
if has('win32')
|
||||
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
|
||||
else
|
||||
call system("pkill -f test_channel.py")
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func Test_communicate()
|
||||
let handle = s:start_server()
|
||||
if handle < 0
|
||||
return
|
||||
endif
|
||||
|
||||
" Simple string request and reply.
|
||||
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
|
||||
|
||||
" Request that triggers sending two ex commands. These will usually be
|
||||
" handled before getting the response, but it's not guaranteed, thus wait a
|
||||
" tiny bit for the commands to get executed.
|
||||
call assert_equal('ok', ch_sendexpr(handle, 'make change'))
|
||||
sleep 10m
|
||||
call assert_equal('added1', getline(line('$') - 1))
|
||||
call assert_equal('added2', getline('$'))
|
||||
|
||||
" Send an eval request that works.
|
||||
call assert_equal('ok', ch_sendexpr(handle, 'eval-works'))
|
||||
sleep 10m
|
||||
call assert_equal([-1, 'foo123'], ch_sendexpr(handle, 'eval-result'))
|
||||
|
||||
" Send an eval request that fails.
|
||||
call assert_equal('ok', ch_sendexpr(handle, 'eval-fails'))
|
||||
sleep 10m
|
||||
call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result'))
|
||||
|
||||
" Send a bad eval request. There will be no response.
|
||||
call assert_equal('ok', ch_sendexpr(handle, 'eval-bad'))
|
||||
sleep 10m
|
||||
call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result'))
|
||||
|
||||
" make the server quit, can't check if this works, should not hang.
|
||||
call ch_sendexpr(handle, '!quit!', 0)
|
||||
|
||||
call s:kill_server()
|
||||
endfunc
|
||||
|
||||
" Test that we can open two channels.
|
||||
func Test_two_channels()
|
||||
let handle = s:start_server()
|
||||
if handle < 0
|
||||
return
|
||||
endif
|
||||
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
|
||||
|
||||
let newhandle = ch_open('localhost:' . s:port, 'json')
|
||||
call assert_equal('got it', ch_sendexpr(newhandle, 'hello!'))
|
||||
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
|
||||
|
||||
call ch_close(handle)
|
||||
call assert_equal('got it', ch_sendexpr(newhandle, 'hello!'))
|
||||
|
||||
call s:kill_server()
|
||||
endfunc
|
||||
|
||||
" Test that a server crash is handled gracefully.
|
||||
func Test_server_crash()
|
||||
let handle = s:start_server()
|
||||
if handle < 0
|
||||
return
|
||||
endif
|
||||
call ch_sendexpr(handle, '!crash!')
|
||||
|
||||
" kill the server in case if failed to crash
|
||||
sleep 10m
|
||||
call s:kill_server()
|
||||
endfunc
|
||||
@@ -708,4 +708,23 @@ func Test_visual_increment_38()
|
||||
call assert_equal([0, 1, 2, 0], getpos('.'))
|
||||
endfunc
|
||||
|
||||
" Test what patch 7.3.414 fixed. Ctrl-A on "000" drops the leading zeros.
|
||||
func Test_normal_increment_01()
|
||||
call setline(1, "000")
|
||||
exec "norm! gg0\<C-A>"
|
||||
call assert_equal("001", getline(1))
|
||||
|
||||
call setline(1, "000")
|
||||
exec "norm! gg$\<C-A>"
|
||||
call assert_equal("001", getline(1))
|
||||
|
||||
call setline(1, "001")
|
||||
exec "norm! gg0\<C-A>"
|
||||
call assert_equal("002", getline(1))
|
||||
|
||||
call setline(1, "001")
|
||||
exec "norm! gg$\<C-A>"
|
||||
call assert_equal("002", getline(1))
|
||||
endfunc
|
||||
|
||||
" vim: tabstop=2 shiftwidth=2 expandtab
|
||||
|
||||
@@ -742,6 +742,50 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1259,
|
||||
/**/
|
||||
1258,
|
||||
/**/
|
||||
1257,
|
||||
/**/
|
||||
1256,
|
||||
/**/
|
||||
1255,
|
||||
/**/
|
||||
1254,
|
||||
/**/
|
||||
1253,
|
||||
/**/
|
||||
1252,
|
||||
/**/
|
||||
1251,
|
||||
/**/
|
||||
1250,
|
||||
/**/
|
||||
1249,
|
||||
/**/
|
||||
1248,
|
||||
/**/
|
||||
1247,
|
||||
/**/
|
||||
1246,
|
||||
/**/
|
||||
1245,
|
||||
/**/
|
||||
1244,
|
||||
/**/
|
||||
1243,
|
||||
/**/
|
||||
1242,
|
||||
/**/
|
||||
1241,
|
||||
/**/
|
||||
1240,
|
||||
/**/
|
||||
1239,
|
||||
/**/
|
||||
1238,
|
||||
/**/
|
||||
1237,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user