patch 8.0.0022
Problem:    If a channel in NL mode is missing the NL at the end the remaining
            characters are dropped.
Solution:   When the channel is closed use the remaining text. (Ozaki Kiichi)
diff --git a/src/channel.c b/src/channel.c
index 13fb653..d4ec60b 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -2355,8 +2355,9 @@
     typval_T	*listtv = NULL;
     typval_T	argv[CH_JSON_MAX_ARGS];
     int		seq_nr = -1;
-    ch_mode_T	ch_mode = channel->ch_part[part].ch_mode;
-    cbq_T	*cbhead = &channel->ch_part[part].ch_cb_head;
+    chanpart_T	*ch_part = &channel->ch_part[part];
+    ch_mode_T	ch_mode = ch_part->ch_mode;
+    cbq_T	*cbhead = &ch_part->ch_cb_head;
     cbq_T	*cbitem;
     char_u	*callback = NULL;
     partial_T	*partial = NULL;
@@ -2376,10 +2377,10 @@
 	callback = cbitem->cq_callback;
 	partial = cbitem->cq_partial;
     }
-    else if (channel->ch_part[part].ch_callback != NULL)
+    else if (ch_part->ch_callback != NULL)
     {
-	callback = channel->ch_part[part].ch_callback;
-	partial = channel->ch_part[part].ch_partial;
+	callback = ch_part->ch_callback;
+	partial = ch_part->ch_partial;
     }
     else
     {
@@ -2387,11 +2388,11 @@
 	partial = channel->ch_partial;
     }
 
-    buffer = channel->ch_part[part].ch_bufref.br_buf;
-    if (buffer != NULL && !bufref_valid(&channel->ch_part[part].ch_bufref))
+    buffer = ch_part->ch_bufref.br_buf;
+    if (buffer != NULL && !bufref_valid(&ch_part->ch_bufref))
     {
 	/* buffer was wiped out */
-	channel->ch_part[part].ch_bufref.br_buf = NULL;
+	ch_part->ch_bufref.br_buf = NULL;
 	buffer = NULL;
     }
 
@@ -2452,7 +2453,7 @@
 
 	if (ch_mode == MODE_NL)
 	{
-	    char_u  *nl;
+	    char_u  *nl = NULL;
 	    char_u  *buf;
 	    readq_T *node;
 
@@ -2465,10 +2466,25 @@
 		if (nl != NULL)
 		    break;
 		if (channel_collapse(channel, part, TRUE) == FAIL)
+		{
+		    if (ch_part->ch_fd == INVALID_FD && node->rq_buflen > 0)
+			break;
 		    return FALSE; /* incomplete message */
+		}
 	    }
 	    buf = node->rq_buffer;
 
+	    if (nl == NULL)
+	    {
+		/* Flush remaining message that is missing a NL. */
+		buf = vim_realloc(buf, node->rq_buflen + 1);
+		if (buf == NULL)
+		    return FALSE;
+		node->rq_buffer = buf;
+		nl = buf + node->rq_buflen++;
+		*nl = NUL;
+	    }
+
 	    /* Convert NUL to NL, the internal representation. */
 	    for (p = buf; p < nl && p < buf + node->rq_buflen; ++p)
 		if (*p == NUL)
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index eb75a0b..0756dd5 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -1484,6 +1484,27 @@
   bwipe!
 endfunc
 
+func MyLineCountCb(ch, msg)
+  let g:linecount += 1
+endfunc
+
+func Test_read_nonl_line()
+  if !has('job')
+    return
+  endif
+
+  let g:linecount = 0
+  if has('win32')
+    " workaround: 'shellescape' does improper escaping double quotes
+    let arg = 'import sys;sys.stdout.write(\"1\n2\n3\")'
+  else
+    let arg = 'import sys;sys.stdout.write("1\n2\n3")'
+  endif
+  call job_start([s:python, '-c', arg], {'callback': 'MyLineCountCb'})
+  call WaitFor('3 <= g:linecount')
+  call assert_equal(3, g:linecount)
+endfunc
+
 function Ch_test_close_lambda(port)
   let handle = ch_open('localhost:' . a:port, s:chopt)
   if ch_status(handle) == "fail"
diff --git a/src/version.c b/src/version.c
index dc8e9cf..4fd2982 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    22,
+/**/
     21,
 /**/
     20,