There are 2 problems about job-channel callback.
1) callback isn't invoked when last line doesn't terminate by NL.
[repro steps]
test.vim
---
function! Callback(ch, msg)
echom a:msg
let g:linecount += 1
endfunction
let g:linecount = 0
call job_start(['python', '-c', 'import sys;sys.stdout.write("1\n2\n3")'], {'callback': 'Callback'})
---
vim -Nu NONE -S test.vim
expected: g:linecount == 3 and ':messages shows;
----
1
2
3
----
actual: g:linecount == 2 and ':messages' shows;
----
1
2
----
[patch]
- Flush last line when no ending NL
https://gist.github.com/ichizok/a60b96fc5c913b260c34a7e9e1a02de8
2) callback isn't invoked when job immediately terminates.
[repro steps]
test.vim
----
function! Callback(ch, msg)
echom a:msg
let g:linecount += 1
endfunction
let g:linecount = 0
call job_start(['python', '-c', 'import os,sys;os.close(1);sys.stderr.write("1\n")'], {'callback': 'Callback'})
----
vim -Nu NONE -S test.vim
expected: ':messages' shows "1" and g:linecount == 1
actual: ':messages' shows nothing and g:linecount == 0
[cause]
When one of the fds associated with job-channel reachs EOF,
job-channel are closed even if other fds has readable data.
[patch]
- Don't close job-channel until all readable fds are closed
https://gist.github.com/ichizok/6e0c00daf387b32bebcc2972f0cca137
Thank you.
- Ozaki Kiichi
Bram, in this patch, you can see workaround to avoid a bug with escaping double-quote on Windows.
I'm working this in separately. I'll report a description and send patch in later.
Thanks.
No need to check shell(s) because job_start doesn't use shell to start process. And the problem occur only on Windows.
shellexecute() make dup for double-quote like below.
shellescape('"') => """" (This mean " and "" and " )
This is right behavior when using cmd.exe. But not right for starting process without cmd.exe. Below is a test program.
foo.c
------------
#include <windows.h>
int
main(int argc, char* argv[]) {
MessageBox(0, argv[1], "foo", MB_OK);
return 0;
}
------------
C:\>foo.exe """"
This should work because it runs on cmd.exe. But it doesn't work when starting process without cmd.exe. Please try following.
------------
#include <windows.h>
#include <stdio.h>
int
main(int argc, char* argv[]) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
CreateProcess(NULL, "foo \"import sys;sys.stdout.write(""1\\n2\\n3"")\"", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
}
------------
You expect this to display quoted "1\n2\n3" but this display non-quoted 1\n2\n3.
http://go-gyazo.appspot.com/949aca13dc74e8c3.png
See this article. https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
We should escape double-quote as \" instead of "" if we run without cmd.exe, so we MUST NOT use shellescape for this. Below is an implementation of this escaping.
https://gist.github.com/d47e7d3bfe5ade4be86062b565a4bfca
This change will be tested via Test_read_nonl_line.
Thanks.
- mattn
No, \n"" should be \\n\"\". i.e. second double-quote should be \". I'll add test in later.
Thank you. I updated patch.
https://gist.github.com/ichizok/6e0c00daf387b32bebcc2972f0cca137
- handle "ch_to_be_closed" as bitset.
- modified "ch_close_part_on_error()" logging, shows reason (EOF or error).
I'm wondering:
In channel_close_on_error, when there is netbeans channel, channel_save writes to PART_OUT, but is this right? to PART_SOCK?
https://gist.github.com/mattn/d47e7d3bfe5ade4be86062b565a4bfca
On Wednesday, October 12, 2016 at 11:19:06 PM UTC+9, Bram Moolenaar wrote:
> +strsave_for_argv(char_u *string)
>
> I would call the argument "argv" and the escaped result "argv_escaped".
renamed strsave_for_argv to escape_arg, string to arg, escaped_string to arg_escaped.
>
> + has_spaces = 1;
>
> Use TRUE and FALSE instead of 1 and 0.
okay, thank you.
> + if (*p == '\n')
> + ++length;
>
> But the "\n" is not escaped, thus this is not needed.
Yes, removed.
> + if (escaped_string == NULL)
> + return escaped_string;
>
> Can return NULL.
this too.
> + else
> + if (*p == '\\')
>
> Should be one line.
ditto
> Also, there is no test for a backslash in the text. It would also need
> to test for two or three backslashes. And one or two backslashes
> followed by a double quote.
>
> I would rename "p" to "s" (stands for source).
ditto
> + /* add terminating quote and finish with a NUL */
> + if (has_spaces)
> + {
> + for (i = 0; i < escaping; i++)
> + *d++ = '\\';
> + *d++ = '"';
> + }
> + *d = NUL;
>
> Adding backslashes before the terminating quote looks strange. If this
> is correct it should be covered by a test.
Yes, I was confused. Totally, fixed implementation. But terminating quote is right. See test code.
> +func s:test_list_args(msg, out)
>
> Use "cmd" instead of "msg".
fixed too
> + call job_start([s:python, '-c', a:msg], {'callback': {ch,msg->execute('let s:out.=msg."\n"')}})
>
> Why append the "\n"?
For the separator, but not required at least. Then removed.
> + call s:test_list_args('print(''hello"world'')', "hello\"world\n")
>
> This seems wrong. The argument is hello"world but it results in an
> extra backslash.
Please look carefully. "hello\"world\n"
Double quote in the quote should be escaped with backslash on Vim script as you know.
Thanks.
- mattn