Compare commits

...

24 Commits

Author SHA1 Message Date
Bram Moolenaar
3fc3e14282 patch 7.4.1259
Problem:    No test for what patch 7.3.414 fixed.
Solution:   Add a test. (Elias Diem)
2016-02-04 21:53:33 +01:00
Bram Moolenaar
a02b321686 patch 7.4.1258
Problem:    The channel test can fail if messages arrive later.
Solution:   Add a short sleep. (Jun T.)
2016-02-04 21:03:33 +01:00
Bram Moolenaar
681baaf4a4 Update runtime files. 2016-02-04 20:57:07 +01:00
Bram Moolenaar
e24692573a patch 7.4.1257
Problem:    Channel test fails in some configurations.
Solution:   Add check for the +channel feature.
2016-02-04 10:54:34 +01:00
Bram Moolenaar
b3e2f00f39 patch 7.4.1256
Problem:    On Mac sys.exit(0) doesn't kill the test server.
Solution:   Use self.server.shutdown(). (Jun Takimoto)
2016-02-04 00:11:37 +01:00
Bram Moolenaar
66624ff0d9 patch 7.4.1255
Problem:    Crash for channel "eval" command without third argument.
Solution:   Check for missing argument.
2016-02-03 23:59:43 +01:00
Bram Moolenaar
3b05b135e3 patch 7.4.1254
Problem:    Opening a second channel causes a crash. (Ken Takata)
Solution:   Don't re-allocate the array with channels.
2016-02-03 23:25:07 +01:00
Bram Moolenaar
608a8919ca patch 7.4.1253
Problem:    Python test server not displaying second of two commands.
            Solaris doesn't have "pkill --full".
Solution:   Also echo the second command. Use "pkill -f".
2016-02-03 22:39:51 +01:00
Bram Moolenaar
e7bed627c8 patch 7.4.1252
Problem:    The channel test server may receive two messages concatenated.
Solution:   Split the messages.
2016-02-03 22:20:29 +01:00
Bram Moolenaar
bf087cead9 patch 7.4.1251
Problem:    New test file missing from distribution.
Solution:   Add src/testdir/*.py.
2016-02-03 21:56:42 +01:00
Bram Moolenaar
2212c4154c patch 7.4.1250
Problem:    Running tests in shadow directory fails.
Solution:   Also link testdir/*.py
2016-02-03 21:45:27 +01:00
Bram Moolenaar
fcb1e3d168 patch 7.4.1249
Problem:    Crash when the process a channel is connected to exits.
Solution:   Use the file descriptor properly.  Add a test. (Damien)
            Also add a test for eval().
2016-02-03 21:32:46 +01:00
Bram Moolenaar
f92591f7f9 patch 7.4.1248
Problem:    Can't reliably stop the channel test server.  Can't start the
            server if the python file is not executable.
Solution:   Use "pkill" instead of "killall".  Run the python file as an
            argument instead of as an executable.
2016-02-03 20:22:32 +01:00
Bram Moolenaar
a0f9cd148e patch 7.4.1247
Problem:    The channel test doesn't run on MS-Windows.
Solution:   Make it work on the MS-Windows console. (Ken Takata)
2016-02-03 20:13:24 +01:00
Bram Moolenaar
7c764f7bbf Make the python script executable. 2016-02-02 23:33:43 +01:00
Bram Moolenaar
d7ece1008e patch 7.4.1246
Problem:    The channel functionality isn't tested.
Solution:   Add a test using a Python test server.
2016-02-02 23:23:02 +01:00
Bram Moolenaar
d087566a41 patch 7.4.1245
Problem:    File missing from distribution.
Solution:   Add json_test.c.
2016-02-02 20:52:42 +01:00
Bram Moolenaar
f57969a20a patch 7.4.1244
Problem:    The channel functions don't sort together.
Solution:   Use a common "ch_" prefix.
2016-02-02 20:47:49 +01:00
Bram Moolenaar
fbf9c6b6c3 patch 7.4.1243
Problem:    Compiler warning for uninitialized variable.
Solution:   Initialize it. (Elias Diem)
2016-02-02 19:43:57 +01:00
Bram Moolenaar
8d8c509ac8 patch 7.4.1242
Problem:    json_test fails without the eval feature.
Solution:   Add #ifdef.
2016-02-02 19:15:38 +01:00
Bram Moolenaar
04b08c3de6 patch 7.4.1241
Problem:    Missing change in Makefile due to diff mismatch
Solution:   Update the list of object files.
2016-02-02 19:01:55 +01:00
Bram Moolenaar
bc07309225 patch 7.4.1240
Problem:    Visual studio tools are noisy.
Solution:   Suppress startup info. (Mike Williams)
2016-02-02 18:50:45 +01:00
Bram Moolenaar
df5b27b20e patch 7.4.1239
Problem:    JSON message after the first one is dropped.
Solution:   Put remainder of message back in the queue.
2016-02-02 18:43:17 +01:00
Bram Moolenaar
56ead341a7 patch 7.4.1238
Problem:    Can't handle two messages right after each other.
Solution:   Find the end of the JSON.  Read more when incomplete.  Add a C
            test for the JSON decoding.
2016-02-02 18:20:08 +01:00
28 changed files with 1374 additions and 531 deletions

View File

@@ -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 \

View File

@@ -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.

View File

@@ -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>").

View File

@@ -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*

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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\)'

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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 */

View File

@@ -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)
{

View File

@@ -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
View 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;
}

View File

@@ -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.
*/

View File

@@ -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 : */

View File

@@ -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 : */

View File

@@ -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;

View File

@@ -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$@ $**

View File

@@ -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
View 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.

View 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

View File

@@ -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

View File

@@ -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,
/**/