changeset 20045:04ef2ccf2519 v8.2.0578

patch 8.2.0578: heredoc for interfaces does not support "trim" Commit: https://github.com/vim/vim/commit/6c2b7b8055b96463f78abb70f58c4c6d6d4b9d55 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Apr 14 20:15:49 2020 +0200 patch 8.2.0578: heredoc for interfaces does not support "trim" Problem: Heredoc for interfaces does not support "trim". Solution: Update the script heredoc support to be same as the :let command. (Yegappan Lakshmanan, closes #5916)
author Bram Moolenaar <Bram@vim.org>
date Tue, 14 Apr 2020 20:30:05 +0200
parents a5dca21836a2
children 23656ac9113c
files runtime/doc/if_lua.txt runtime/doc/if_mzsch.txt runtime/doc/if_perl.txt runtime/doc/if_pyth.txt runtime/doc/if_ruby.txt runtime/doc/if_tcl.txt src/evalvars.c src/ex_getln.c src/proto/evalvars.pro src/testdir/test86.in src/testdir/test87.in src/testdir/test_lua.vim src/testdir/test_perl.vim src/testdir/test_python2.vim src/testdir/test_python3.vim src/testdir/test_pyx2.vim src/testdir/test_pyx3.vim src/testdir/test_ruby.vim src/testdir/test_tcl.vim src/userfunc.c src/version.c src/vim9compile.c
diffstat 22 files changed, 2093 insertions(+), 1926 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -32,7 +32,7 @@ Examples:
 	:lua local curbuf = vim.buffer() curbuf[7] = "line #7"
 <
 
-:[range]lua << [endmarker]
+:[range]lua << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute Lua script {script}.
@@ -40,10 +40,9 @@ Examples:
 			feature wasn't compiled in.  To avoid errors, see
 			|script-here|.
 
-The {endmarker} must NOT be preceded by any white space.
-
 If [endmarker] is omitted from after the "<<", a dot '.' must be used after
-{script}, like for the |:append| and |:insert| commands.
+{script}, like for the |:append| and |:insert| commands.  Refer to
+|:let-heredoc| for more information.
 
 This form of the |:lua| command is mainly useful for including Lua code
 in Vim scripts.
--- a/runtime/doc/if_mzsch.txt
+++ b/runtime/doc/if_mzsch.txt
@@ -39,7 +39,7 @@ 1. Commands						*mzscheme-commands*
 :[range]mz[scheme] {stmt}
 			Execute MzScheme statement {stmt}.
 
-:[range]mz[scheme] << [endmarker]
+:[range]mz[scheme] << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute inlined MzScheme script {script}.
@@ -47,12 +47,11 @@ 1. Commands						*mzscheme-commands*
 			feature wasn't compiled in.  To avoid errors, see
 			|script-here|.
 
-			The {endmarker} below the {script} must NOT be
-			preceded by any white space.
-
 			If [endmarker] is omitted from after the "<<", a dot
 			'.' must be used after {script}, like for the
-			|:append| and |:insert| commands.
+			|:append| and |:insert| commands.  Refer to
+			|:let-heredoc| for more information.
+
 
 							*:mzfile* *:mzf*
 :[range]mzf[ile] {file}	Execute the MzScheme script in {file}.
--- a/runtime/doc/if_perl.txt
+++ b/runtime/doc/if_perl.txt
@@ -55,7 +55,7 @@ 3. Using the Perl interface				*perl-usi
 			working: >
 				:perl VIM::Msg("Hello")
 
-:pe[rl] << [endmarker]
+:pe[rl] << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute Perl script {script}.
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -34,7 +34,7 @@ 1. Commands						*python-commands*
 			the `:python` command is working: >
 				:python print "Hello"
 
-:[range]py[thon] << [endmarker]
+:[range]py[thon] << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute Python script {script}.
@@ -42,10 +42,9 @@ 1. Commands						*python-commands*
 			feature wasn't compiled in.  To avoid errors, see
 			|script-here|.
 
-The {endmarker} below the {script} must NOT be preceded by any white space.
-
 If [endmarker] is omitted from after the "<<", a dot '.' must be used after
-{script}, like for the |:append| and |:insert| commands.
+{script}, like for the |:append| and |:insert| commands.  Refer to
+|:let-heredoc| for more information.
 
 This form of the |:python| command is mainly useful for including python code
 in Vim scripts.
@@ -768,12 +767,12 @@ 10. Python 3						*python3*
 
 							*:py3* *:python3*
 :[range]py3 {stmt}
-:[range]py3 << [endmarker]
+:[range]py3 << [trim] [{endmarker}]
 {script}
 {endmarker}
 
 :[range]python3 {stmt}
-:[range]python3 << [endmarker]
+:[range]python3 << [trim] [{endmarker}]
 {script}
 {endmarker}
 	The `:py3` and `:python3` commands work similar to `:python`.  A
--- a/runtime/doc/if_ruby.txt
+++ b/runtime/doc/if_ruby.txt
@@ -28,15 +28,15 @@ 1. Commands						*ruby-commands*
 :rub[y] {cmd}		Execute Ruby command {cmd}.  A command to try it out: >
 				:ruby print "Hello"
 
-:rub[y] << [endmarker]
+:rub[y] << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute Ruby script {script}.
-			The {endmarker} after {script} must NOT be preceded by
-			any white space.
 
 			If [endmarker] is omitted, it defaults to a dot '.'
-			like for the |:append| and |:insert| commands.
+			like for the |:append| and |:insert| commands.  Refer
+			to |:let-heredoc| for more information.
+
 
 			This form of the |:ruby| command is mainly useful for
 			including ruby code in vim scripts.
--- a/runtime/doc/if_tcl.txt
+++ b/runtime/doc/if_tcl.txt
@@ -30,7 +30,7 @@ 1. Commands				*tcl-ex-commands* *E571* 
 			is working: >
 				:tcl puts "Hello"
 
-:[range]tc[l] << [endmarker]
+:[range]tc[l] << [trim] [{endmarker}]
 {script}
 {endmarker}
 			Execute Tcl script {script}.
@@ -38,10 +38,9 @@ 1. Commands				*tcl-ex-commands* *E571* 
 			wasn't compiled in.  To avoid errors, see
 			|script-here|.
 
-The {endmarker} after {script} must NOT be preceded by any white space.
-
 If [endmarker] is omitted from after the "<<", a dot '.' must be used after
-{script}, like for the |:append| and |:insert| commands.
+{script}, like for the |:append| and |:insert| commands.  Refer to
+|:let-heredoc| for more information.
 
 This form of the |:tcl| command is mainly useful for including tcl code in Vim
 scripts.
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -541,10 +541,15 @@ list_script_vars(int *first)
  * The {marker} is a string. If the optional 'trim' word is supplied before the
  * marker, then the leading indentation before the lines (matching the
  * indentation in the 'cmd' line) is stripped.
+ *
+ * When getting lines for an embedded script (e.g. python, lua, perl, ruby,
+ * tcl, mzscheme), script_get is set to TRUE. In this case, if the marker is
+ * missing, then '.' is accepted as a marker.
+ *
  * Returns a List with {lines} or NULL.
  */
     list_T *
-heredoc_get(exarg_T *eap, char_u *cmd)
+heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
 {
     char_u	*theline;
     char_u	*marker;
@@ -553,6 +558,7 @@ heredoc_get(exarg_T *eap, char_u *cmd)
     int		marker_indent_len = 0;
     int		text_indent_len = 0;
     char_u	*text_indent = NULL;
+    char_u	dot[] = ".";
 
     if (eap->getline == NULL)
     {
@@ -598,8 +604,15 @@ heredoc_get(exarg_T *eap, char_u *cmd)
     }
     else
     {
-	emsg(_("E172: Missing marker"));
-	return NULL;
+	// When getting lines for an embedded script, if the marker is missing,
+	// accept '.' as the marker.
+	if (script_get)
+	    marker = dot;
+	else
+	{
+	    emsg(_("E172: Missing marker"));
+	    return NULL;
+	}
     }
 
     l = list_alloc();
@@ -746,7 +759,7 @@ ex_let_const(exarg_T *eap, int is_const)
 	list_T	*l;
 
 	// HERE document
-	l = heredoc_get(eap, expr + 3);
+	l = heredoc_get(eap, expr + 3, FALSE);
 	if (l != NULL)
 	{
 	    rettv_list_set(&rettv, l);
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -4408,44 +4408,37 @@ open_cmdwin(void)
  * Returns a pointer to allocated memory with {script} or NULL.
  */
     char_u *
-script_get(exarg_T *eap, char_u *cmd)
+script_get(exarg_T *eap UNUSED, char_u *cmd UNUSED)
 {
-    char_u	*theline;
-    char	*end_pattern = NULL;
-    char	dot[] = ".";
+#ifdef FEAT_EVAL
+    list_T	*l;
+    listitem_T	*li;
+    char_u	*s;
     garray_T	ga;
 
     if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL)
 	return NULL;
+    cmd += 2;
+
+    l = heredoc_get(eap, cmd, TRUE);
+    if (l == NULL)
+	return NULL;
 
     ga_init2(&ga, 1, 0x400);
 
-    if (cmd[2] != NUL)
-	end_pattern = (char *)skipwhite(cmd + 2);
-    else
-	end_pattern = dot;
-
-    for (;;)
+    FOR_ALL_LIST_ITEMS(l, li)
     {
-	theline = eap->getline(
-#ifdef FEAT_EVAL
-	    eap->cstack->cs_looplevel > 0 ? -1 :
-#endif
-	    NUL, eap->cookie, 0, TRUE);
-
-	if (theline == NULL || STRCMP(end_pattern, theline) == 0)
-	{
-	    vim_free(theline);
-	    break;
-	}
-
-	ga_concat(&ga, theline);
+	s = tv_get_string(&li->li_tv);
+	ga_concat(&ga, s);
 	ga_append(&ga, '\n');
-	vim_free(theline);
     }
     ga_append(&ga, NUL);
 
+    list_free(l);
     return (char_u *)ga.ga_data;
+#else
+    return NULL;
+#endif
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -13,7 +13,7 @@ list_T *eval_spell_expr(char_u *badword,
 int get_spellword(list_T *list, char_u **pp);
 void prepare_vimvar(int idx, typval_T *save_tv);
 void restore_vimvar(int idx, typval_T *save_tv);
-list_T *heredoc_get(exarg_T *eap, char_u *cmd);
+list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get);
 void ex_let(exarg_T *eap);
 void ex_const(exarg_T *eap);
 int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int flags, char_u *op);
--- a/src/testdir/test86.in
+++ b/src/testdir/test86.in
@@ -37,20 +37,20 @@ STARTTEST
 :fun d.f()
 :  return 1
 :endfun
-py << EOF
-d=vim.bindeval('d')
-d['1']='asd'
-d.update()  # Must not do anything, including throwing errors
-d.update(b=[1, 2, f])
-d.update((('-1', {'a': 1}),))
-d.update({'0': -1})
-dk = d.keys()
-dv = d.values()
-di = d.items()
-cmpfun = lambda a, b: cmp(repr(a), repr(b))
-dk.sort(cmpfun)
-dv.sort(cmpfun)
-di.sort(cmpfun)
+py << trim EOF
+  d=vim.bindeval('d')
+  d['1']='asd'
+  d.update()  # Must not do anything, including throwing errors
+  d.update(b=[1, 2, f])
+  d.update((('-1', {'a': 1}),))
+  d.update({'0': -1})
+  dk = d.keys()
+  dv = d.values()
+  di = d.items()
+  cmpfun = lambda a, b: cmp(repr(a), repr(b))
+  dk.sort(cmpfun)
+  dv.sort(cmpfun)
+  di.sort(cmpfun)
 EOF
 :$put =pyeval('d[''f''](self={})')
 :$put =pyeval('repr(dk)')
@@ -208,52 +208,52 @@ EOF
 :let l = [0, 1, 2, 3]
 :py l=vim.bindeval('l')
 :lockvar! l
-py << EOF
-def emsg(ei):
-    return ei[0].__name__ + ':' + repr(ei[1].args)
+py << trim EOF
+  def emsg(ei):
+      return ei[0].__name__ + ':' + repr(ei[1].args)
 
-try:
-    l[2]='i'
-except vim.error:
-    cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info()))
+  try:
+      l[2]='i'
+  except vim.error:
+      cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info()))
 EOF
 :$put =string(l)
 :unlockvar! l
 :"
 :" Function calls
-py << EOF
-import sys
-def ee(expr, g=globals(), l=locals()):
-    try:
-        exec(expr, g, l)
-    except:
-        ei = sys.exc_info()
-        msg = emsg(ei)
-        msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
-        if expr.find('None') > -1:
-            msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
-                              'TypeError:("\'NoneType\' object is not iterable",)')
-        if expr.find('FailingNumber') > -1:
-            msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
-            msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
-                              'TypeError:("\'FailingNumber\' object is not iterable",)')
-        if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
-            msg = msg.replace('(\'', '("').replace('\',)', '",)')
-        # Some Python versions say can't, others cannot.
-        if msg.find('can\'t') > -1:
-            msg = msg.replace('can\'t', 'cannot')
-        # Some Python versions use single quote, some double quote
-        if msg.find('"cannot ') > -1:
-            msg = msg.replace('"cannot ', '\'cannot ')
-        if msg.find(' attributes"') > -1:
-            msg = msg.replace(' attributes"', ' attributes\'')
-        if expr == 'fd(self=[])':
-            # HACK: PyMapping_Check changed meaning
-            msg = msg.replace('AttributeError:(\'keys\',)',
-                              'TypeError:(\'unable to convert list to vim dictionary\',)')
-        vim.current.buffer.append(expr + ':' + msg)
-    else:
-        vim.current.buffer.append(expr + ':NOT FAILED')
+py << trim EOF
+  import sys
+  def ee(expr, g=globals(), l=locals()):
+      try:
+          exec(expr, g, l)
+      except:
+          ei = sys.exc_info()
+          msg = emsg(ei)
+          msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
+          if expr.find('None') > -1:
+              msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
+                                'TypeError:("\'NoneType\' object is not iterable",)')
+          if expr.find('FailingNumber') > -1:
+              msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
+              msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
+                                'TypeError:("\'FailingNumber\' object is not iterable",)')
+          if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
+              msg = msg.replace('(\'', '("').replace('\',)', '",)')
+          # Some Python versions say can't, others cannot.
+          if msg.find('can\'t') > -1:
+              msg = msg.replace('can\'t', 'cannot')
+          # Some Python versions use single quote, some double quote
+          if msg.find('"cannot ') > -1:
+              msg = msg.replace('"cannot ', '\'cannot ')
+          if msg.find(' attributes"') > -1:
+              msg = msg.replace(' attributes"', ' attributes\'')
+          if expr == 'fd(self=[])':
+              # HACK: PyMapping_Check changed meaning
+              msg = msg.replace('AttributeError:(\'keys\',)',
+                                'TypeError:(\'unable to convert list to vim dictionary\',)')
+          vim.current.buffer.append(expr + ':' + msg)
+      else:
+          vim.current.buffer.append(expr + ':NOT FAILED')
 EOF
 :fun New(...)
 :   return ['NewStart']+a:000+['NewEnd']
@@ -283,26 +283,26 @@ EOF
 :endif
 :let messages=[]
 :delfunction DictNew
-py <<EOF
-d=vim.bindeval('{}')
-m=vim.bindeval('messages')
-def em(expr, g=globals(), l=locals()):
-    try:
-        exec(expr, g, l)
-    except:
-        m.extend([sys.exc_type.__name__])
+py << trim EOF
+  d=vim.bindeval('{}')
+  m=vim.bindeval('messages')
+  def em(expr, g=globals(), l=locals()):
+      try:
+          exec(expr, g, l)
+      except:
+          m.extend([sys.exc_type.__name__])
 
-em('d["abc1"]')
-em('d["abc1"]="\\0"')
-em('d["abc1"]=vim')
-em('d[""]=1')
-em('d["a\\0b"]=1')
-em('d[u"a\\0b"]=1')
+  em('d["abc1"]')
+  em('d["abc1"]="\\0"')
+  em('d["abc1"]=vim')
+  em('d[""]=1')
+  em('d["a\\0b"]=1')
+  em('d[u"a\\0b"]=1')
 
-em('d.pop("abc1")')
-em('d.popitem()')
-del em
-del m
+  em('d.pop("abc1")')
+  em('d.popitem()')
+  del em
+  del m
 EOF
 :$put =messages
 :unlet messages
@@ -367,24 +367,24 @@ EOF
 :" threading
 :let l = [0]
 :py l=vim.bindeval('l')
-py <<EOF
-import threading
-import time
+py << trim EOF
+  import threading
+  import time
 
-class T(threading.Thread):
-    def __init__(self):
-        threading.Thread.__init__(self)
-        self.t = 0
-        self.running = True
+  class T(threading.Thread):
+      def __init__(self):
+          threading.Thread.__init__(self)
+          self.t = 0
+          self.running = True
 
-    def run(self):
-        while self.running:
-            self.t += 1
-            time.sleep(0.1)
+      def run(self):
+          while self.running:
+              self.t += 1
+              time.sleep(0.1)
 
-t = T()
-del T
-t.start()
+  t = T()
+  del T
+  t.start()
 EOF
 :sleep 1
 :py t.running = False
@@ -400,18 +400,18 @@ EOF
 :" settrace
 :let l = []
 :py l=vim.bindeval('l')
-py <<EOF
-import sys
+py << trim EOF
+  import sys
 
-def traceit(frame, event, arg):
-    global l
-    if event == "line":
-        l.extend([frame.f_lineno])
-    return traceit
+  def traceit(frame, event, arg):
+      global l
+      if event == "line":
+          l.extend([frame.f_lineno])
+      return traceit
 
-def trace_main():
-    for i in range(5):
-        pass
+  def trace_main():
+      for i in range(5):
+          pass
 EOF
 :py sys.settrace(traceit)
 :py trace_main()
@@ -497,19 +497,19 @@ EOF
 :  put ='  W: '.wvals
 :  put ='  B: '.wvals
 :endfun
-py << EOF
-def e(s, g=globals(), l=locals()):
-    try:
-        exec(s, g, l)
-    except:
-        vim.command('return ' + repr(sys.exc_type.__name__))
+py << trim EOF
+  def e(s, g=globals(), l=locals()):
+      try:
+          exec(s, g, l)
+      except:
+          vim.command('return ' + repr(sys.exc_type.__name__))
 
-def ev(s, g=globals(), l=locals()):
-    try:
-        return eval(s, g, l)
-    except:
-        vim.command('let exc=' + repr(sys.exc_type.__name__))
-        return 0
+  def ev(s, g=globals(), l=locals()):
+      try:
+          return eval(s, g, l)
+      except:
+          vim.command('let exc=' + repr(sys.exc_type.__name__))
+          return 0
 EOF
 :fun E(s)
 :   python e(vim.eval('a:s'))
@@ -627,52 +627,52 @@ EOF
 :   autocmd BufFilePost * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePost:' + vim.eval('bufnr("%")'))
 :   autocmd BufFilePre * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePre:' + vim.eval('bufnr("%")'))
 :augroup END
-py << EOF
-# Tests BufferAppend and BufferItem
-cb.append(b[0])
-# Tests BufferSlice and BufferAssSlice
-cb.append('abc5') # Will be overwritten
-cb[-1:] = b[:-2]
-# Test BufferLength and BufferAssSlice
-cb.append('def') # Will not be overwritten
-cb[len(cb):] = b[:]
-# Test BufferAssItem and BufferMark
-cb.append('ghi') # Will be overwritten
-cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1]))
-# Test BufferRepr
-cb.append(repr(cb) + repr(b))
-# Modify foreign buffer
-b.append('foo')
-b[0]='bar'
-b[0:0]=['baz']
-vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number)
-# Test assigning to name property
-import os
-old_name = cb.name
-cb.name = 'foo'
-cb.append(cb.name[-11:].replace(os.path.sep, '/'))
-b.name = 'bar'
-cb.append(b.name[-11:].replace(os.path.sep, '/'))
-cb.name = old_name
-cb.append(cb.name[-17:].replace(os.path.sep, '/'))
-del old_name
-# Test CheckBuffer
-for _b in vim.buffers:
-    if _b is not cb:
-        vim.command('bwipeout! ' + str(_b.number))
-del _b
-cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid)))
-for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'):
-    try:
-        exec(expr)
-    except vim.error:
-        pass
-    else:
-        # Usually a SEGV here
-        # Should not happen in any case
-        cb.append('No exception for ' + expr)
-vim.command('cd .')
-del b
+py << trim EOF
+  # Tests BufferAppend and BufferItem
+  cb.append(b[0])
+  # Tests BufferSlice and BufferAssSlice
+  cb.append('abc5') # Will be overwritten
+  cb[-1:] = b[:-2]
+  # Test BufferLength and BufferAssSlice
+  cb.append('def') # Will not be overwritten
+  cb[len(cb):] = b[:]
+  # Test BufferAssItem and BufferMark
+  cb.append('ghi') # Will be overwritten
+  cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1]))
+  # Test BufferRepr
+  cb.append(repr(cb) + repr(b))
+  # Modify foreign buffer
+  b.append('foo')
+  b[0]='bar'
+  b[0:0]=['baz']
+  vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number)
+  # Test assigning to name property
+  import os
+  old_name = cb.name
+  cb.name = 'foo'
+  cb.append(cb.name[-11:].replace(os.path.sep, '/'))
+  b.name = 'bar'
+  cb.append(b.name[-11:].replace(os.path.sep, '/'))
+  cb.name = old_name
+  cb.append(cb.name[-17:].replace(os.path.sep, '/'))
+  del old_name
+  # Test CheckBuffer
+  for _b in vim.buffers:
+      if _b is not cb:
+          vim.command('bwipeout! ' + str(_b.number))
+  del _b
+  cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid)))
+  for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'):
+      try:
+          exec(expr)
+      except vim.error:
+          pass
+      else:
+          # Usually a SEGV here
+          # Should not happen in any case
+          cb.append('No exception for ' + expr)
+  vim.command('cd .')
+  del b
 EOF
 :augroup BUFS
 :   autocmd!
@@ -687,59 +687,59 @@ EOF
 :buffer #
 :edit c
 :buffer #
-py << EOF
-try:
-    from __builtin__ import next
-except ImportError:
-    next = lambda o: o.next()
-# Check GCing iterator that was not fully exhausted
-i = iter(vim.buffers)
-cb.append('i:' + str(next(i)))
-# and also check creating more than one iterator at a time
-i2 = iter(vim.buffers)
-cb.append('i2:' + str(next(i2)))
-cb.append('i:' + str(next(i)))
-# The following should trigger GC and not cause any problems
-del i
-del i2
-i3 = iter(vim.buffers)
-cb.append('i3:' + str(next(i3)))
-del i3
+py << trim EOF
+  try:
+      from __builtin__ import next
+  except ImportError:
+      next = lambda o: o.next()
+  # Check GCing iterator that was not fully exhausted
+  i = iter(vim.buffers)
+  cb.append('i:' + str(next(i)))
+  # and also check creating more than one iterator at a time
+  i2 = iter(vim.buffers)
+  cb.append('i2:' + str(next(i2)))
+  cb.append('i:' + str(next(i)))
+  # The following should trigger GC and not cause any problems
+  del i
+  del i2
+  i3 = iter(vim.buffers)
+  cb.append('i3:' + str(next(i3)))
+  del i3
 
-prevnum = 0
-for b in vim.buffers:
-    # Check buffer order
-    if prevnum >= b.number:
-        cb.append('!!! Buffer numbers not in strictly ascending order')
-    # Check indexing: vim.buffers[number].number == number
-    cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b))
-    prevnum = b.number
-del prevnum
+  prevnum = 0
+  for b in vim.buffers:
+      # Check buffer order
+      if prevnum >= b.number:
+          cb.append('!!! Buffer numbers not in strictly ascending order')
+      # Check indexing: vim.buffers[number].number == number
+      cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b))
+      prevnum = b.number
+  del prevnum
 
-cb.append(str(len(vim.buffers)))
+  cb.append(str(len(vim.buffers)))
 
-bnums = list(map(lambda b: b.number, vim.buffers))[1:]
+  bnums = list(map(lambda b: b.number, vim.buffers))[1:]
 
-# Test wiping out buffer with existing iterator
-i4 = iter(vim.buffers)
-cb.append('i4:' + str(next(i4)))
-vim.command('bwipeout! ' + str(bnums.pop(0)))
-try:
-    next(i4)
-except vim.error:
-    pass
-else:
-    cb.append('!!!! No vim.error')
-i4 = iter(vim.buffers)
-vim.command('bwipeout! ' + str(bnums.pop(-1)))
-vim.command('bwipeout! ' + str(bnums.pop(-1)))
-cb.append('i4:' + str(next(i4)))
-try:
-    next(i4)
-except StopIteration:
-    cb.append('StopIteration')
-del i4
-del bnums
+  # Test wiping out buffer with existing iterator
+  i4 = iter(vim.buffers)
+  cb.append('i4:' + str(next(i4)))
+  vim.command('bwipeout! ' + str(bnums.pop(0)))
+  try:
+      next(i4)
+  except vim.error:
+      pass
+  else:
+      cb.append('!!!! No vim.error')
+  i4 = iter(vim.buffers)
+  vim.command('bwipeout! ' + str(bnums.pop(-1)))
+  vim.command('bwipeout! ' + str(bnums.pop(-1)))
+  cb.append('i4:' + str(next(i4)))
+  try:
+      next(i4)
+  except StopIteration:
+      cb.append('StopIteration')
+  del i4
+  del bnums
 EOF
 :"
 :" Test vim.{tabpage,window}list and vim.{tabpage,window} objects
@@ -750,128 +750,128 @@ EOF
 :vnew a.2
 :vnew b.2
 :vnew c.2
-py << EOF
-cb.append('Number of tabs: ' + str(len(vim.tabpages)))
-cb.append('Current tab pages:')
-def W(w):
-    if repr(w).find('(unknown)') != -1:
-        return '<window object (unknown)>'
-    else:
-        return repr(w)
+py << trim EOF
+  cb.append('Number of tabs: ' + str(len(vim.tabpages)))
+  cb.append('Current tab pages:')
+  def W(w):
+      if repr(w).find('(unknown)') != -1:
+          return '<window object (unknown)>'
+      else:
+          return repr(w)
 
-start = len(cb)
+  start = len(cb)
 
-def Cursor(w):
-    if w.buffer is cb:
-        return repr((start - w.cursor[0], w.cursor[1]))
-    else:
-        return repr(w.cursor)
+  def Cursor(w):
+      if w.buffer is cb:
+          return repr((start - w.cursor[0], w.cursor[1]))
+      else:
+          return repr(w.cursor)
 
-for t in vim.tabpages:
-    cb.append('  ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window))
-    cb.append('  Windows:')
-    for w in t.windows:
-        cb.append('    ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w))
-        # Other values depend on the size of the terminal, so they are checked partly:
-        for attr in ('height', 'row', 'width', 'col'):
-            try:
-                aval = getattr(w, attr)
-                if type(aval) is not long:
-                    raise TypeError
-                if aval < 0:
-                    raise ValueError
-            except Exception:
-                cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + sys.exc_type.__name__)
-        del aval
-        del attr
-        w.cursor = (len(w.buffer), 0)
-del W
-del Cursor
-cb.append('Number of windows in current tab page: ' + str(len(vim.windows)))
-if list(vim.windows) != list(vim.current.tabpage.windows):
-    cb.append('!!!!!! Windows differ')
+  for t in vim.tabpages:
+      cb.append('  ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window))
+      cb.append('  Windows:')
+      for w in t.windows:
+          cb.append('    ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w))
+          # Other values depend on the size of the terminal, so they are checked partly:
+          for attr in ('height', 'row', 'width', 'col'):
+              try:
+                  aval = getattr(w, attr)
+                  if type(aval) is not long:
+                      raise TypeError
+                  if aval < 0:
+                      raise ValueError
+              except Exception:
+                  cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + sys.exc_type.__name__)
+          del aval
+          del attr
+          w.cursor = (len(w.buffer), 0)
+  del W
+  del Cursor
+  cb.append('Number of windows in current tab page: ' + str(len(vim.windows)))
+  if list(vim.windows) != list(vim.current.tabpage.windows):
+      cb.append('!!!!!! Windows differ')
 EOF
 :"
 :" Test vim.current
-py << EOF
-def H(o):
-    return repr(o)
-cb.append('Current tab page: ' + repr(vim.current.tabpage))
-cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window))
-cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer))
-del H
-# Assigning: fails
-try:
-    vim.current.window = vim.tabpages[0].window
-except ValueError:
-    cb.append('ValueError at assigning foreign tab window')
+py << trim EOF
+  def H(o):
+      return repr(o)
+  cb.append('Current tab page: ' + repr(vim.current.tabpage))
+  cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window))
+  cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer))
+  del H
+  # Assigning: fails
+  try:
+      vim.current.window = vim.tabpages[0].window
+  except ValueError:
+      cb.append('ValueError at assigning foreign tab window')
 
-for attr in ('window', 'tabpage', 'buffer'):
-    try:
-        setattr(vim.current, attr, None)
-    except TypeError:
-        cb.append('Type error at assigning None to vim.current.' + attr)
-del attr
+  for attr in ('window', 'tabpage', 'buffer'):
+      try:
+          setattr(vim.current, attr, None)
+      except TypeError:
+          cb.append('Type error at assigning None to vim.current.' + attr)
+  del attr
 
-# Assigning: success
-vim.current.tabpage = vim.tabpages[-2]
-vim.current.buffer = cb
-vim.current.window = vim.windows[0]
-vim.current.window.cursor = (len(vim.current.buffer), 0)
-cb.append('Current tab page: ' + repr(vim.current.tabpage))
-cb.append('Current window: ' + repr(vim.current.window))
-cb.append('Current buffer: ' + repr(vim.current.buffer))
-cb.append('Current line: ' + repr(vim.current.line))
-ws = list(vim.windows)
-ts = list(vim.tabpages)
-for b in vim.buffers:
-    if b is not cb:
-        vim.command('bwipeout! ' + str(b.number))
-del b
-cb.append('w.valid: ' + repr([w.valid for w in ws]))
-cb.append('t.valid: ' + repr([t.valid for t in ts]))
-del w
-del t
-del ts
-del ws
+  # Assigning: success
+  vim.current.tabpage = vim.tabpages[-2]
+  vim.current.buffer = cb
+  vim.current.window = vim.windows[0]
+  vim.current.window.cursor = (len(vim.current.buffer), 0)
+  cb.append('Current tab page: ' + repr(vim.current.tabpage))
+  cb.append('Current window: ' + repr(vim.current.window))
+  cb.append('Current buffer: ' + repr(vim.current.buffer))
+  cb.append('Current line: ' + repr(vim.current.line))
+  ws = list(vim.windows)
+  ts = list(vim.tabpages)
+  for b in vim.buffers:
+      if b is not cb:
+          vim.command('bwipeout! ' + str(b.number))
+  del b
+  cb.append('w.valid: ' + repr([w.valid for w in ws]))
+  cb.append('t.valid: ' + repr([t.valid for t in ts]))
+  del w
+  del t
+  del ts
+  del ws
 EOF
 :tabonly!
 :only!
 :"
 :" Test types
-py << EOF
-for expr, attr in (
-    ('vim.vars',                         'Dictionary'),
-    ('vim.options',                      'Options'),
-    ('vim.bindeval("{}")',               'Dictionary'),
-    ('vim.bindeval("[]")',               'List'),
-    ('vim.bindeval("function(\'tr\')")', 'Function'),
-    ('vim.current.buffer',               'Buffer'),
-    ('vim.current.range',                'Range'),
-    ('vim.current.window',               'Window'),
-    ('vim.current.tabpage',              'TabPage'),
-):
-    cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr)))
-del expr
-del attr
+py << trim EOF
+  for expr, attr in (
+      ('vim.vars',                         'Dictionary'),
+      ('vim.options',                      'Options'),
+      ('vim.bindeval("{}")',               'Dictionary'),
+      ('vim.bindeval("[]")',               'List'),
+      ('vim.bindeval("function(\'tr\')")', 'Function'),
+      ('vim.current.buffer',               'Buffer'),
+      ('vim.current.range',                'Range'),
+      ('vim.current.window',               'Window'),
+      ('vim.current.tabpage',              'TabPage'),
+  ):
+      cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr)))
+  del expr
+  del attr
 EOF
 :"
 :" Test __dir__() method
-py << EOF
-for name, o in (
-        ('current',    vim.current),
-        ('buffer',     vim.current.buffer),
-        ('window',     vim.current.window),
-        ('tabpage',    vim.current.tabpage),
-        ('range',      vim.current.range),
-        ('dictionary', vim.bindeval('{}')),
-        ('list',       vim.bindeval('[]')),
-        ('function',   vim.bindeval('function("tr")')),
-        ('output',     sys.stdout),
-    ):
-    cb.append(name + ':' + ','.join(dir(o)))
-del name
-del o
+py << trim EOF
+  for name, o in (
+          ('current',    vim.current),
+          ('buffer',     vim.current.buffer),
+          ('window',     vim.current.window),
+          ('tabpage',    vim.current.tabpage),
+          ('range',      vim.current.range),
+          ('dictionary', vim.bindeval('{}')),
+          ('list',       vim.bindeval('[]')),
+          ('function',   vim.bindeval('function("tr")')),
+          ('output',     sys.stdout),
+      ):
+      cb.append(name + ':' + ','.join(dir(o)))
+  del name
+  del o
 EOF
 :"
 :" Test vim.*.__new__
@@ -904,63 +904,63 @@ EOF
 :py Pt = vim.bindeval('Pt')
 :unlet Pt
 :py del Pt
-py << EOF
-def ecall(out_prefix, func, *args, **kwargs):
-    line = out_prefix + ': '
-    try:
-        ret = func(*args, **kwargs)
-    except Exception:
-        line += '!exception: ' + emsg(sys.exc_info())
-    else:
-        line += '!result: ' + vim.Function('string')(ret)
-    cb.append(line)
-a = vim.Function('Args')
-pa1 = vim.Function('Args', args=['abcArgsPA1'])
-pa2 = vim.Function('Args', args=[])
-pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'})
-pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'})
-cb.append('a: ' + repr(a))
-cb.append('pa1: ' + repr(pa1))
-cb.append('pa2: ' + repr(pa2))
-cb.append('pa3: ' + repr(pa3))
-cb.append('pa4: ' + repr(pa4))
-sa = vim.Function('SelfArgs')
-psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1'])
-psa2 = vim.Function('SelfArgs', args=[])
-psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'})
-psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'})
-psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0)
-psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=())
-psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[])
-psa8 = vim.Function('SelfArgs', auto_rebind=False)
-psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True)
-psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1)
-psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'})
-psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC'])
-cb.append('sa: ' + repr(sa))
-cb.append('psa1: ' + repr(psa1))
-cb.append('psa2: ' + repr(psa2))
-cb.append('psa3: ' + repr(psa3))
-cb.append('psa4: ' + repr(psa4))
-cb.append('psa5: ' + repr(psa5))
-cb.append('psa6: ' + repr(psa6))
-cb.append('psa7: ' + repr(psa7))
-cb.append('psa8: ' + repr(psa8))
-cb.append('psa9: ' + repr(psa9))
-cb.append('psaA: ' + repr(psaA))
-cb.append('psaB: ' + repr(psaB))
-cb.append('psaC: ' + repr(psaC))
+py << trim EOF
+  def ecall(out_prefix, func, *args, **kwargs):
+      line = out_prefix + ': '
+      try:
+          ret = func(*args, **kwargs)
+      except Exception:
+          line += '!exception: ' + emsg(sys.exc_info())
+      else:
+          line += '!result: ' + vim.Function('string')(ret)
+      cb.append(line)
+  a = vim.Function('Args')
+  pa1 = vim.Function('Args', args=['abcArgsPA1'])
+  pa2 = vim.Function('Args', args=[])
+  pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'})
+  pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'})
+  cb.append('a: ' + repr(a))
+  cb.append('pa1: ' + repr(pa1))
+  cb.append('pa2: ' + repr(pa2))
+  cb.append('pa3: ' + repr(pa3))
+  cb.append('pa4: ' + repr(pa4))
+  sa = vim.Function('SelfArgs')
+  psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1'])
+  psa2 = vim.Function('SelfArgs', args=[])
+  psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'})
+  psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'})
+  psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0)
+  psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=())
+  psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[])
+  psa8 = vim.Function('SelfArgs', auto_rebind=False)
+  psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True)
+  psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1)
+  psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'})
+  psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC'])
+  cb.append('sa: ' + repr(sa))
+  cb.append('psa1: ' + repr(psa1))
+  cb.append('psa2: ' + repr(psa2))
+  cb.append('psa3: ' + repr(psa3))
+  cb.append('psa4: ' + repr(psa4))
+  cb.append('psa5: ' + repr(psa5))
+  cb.append('psa6: ' + repr(psa6))
+  cb.append('psa7: ' + repr(psa7))
+  cb.append('psa8: ' + repr(psa8))
+  cb.append('psa9: ' + repr(psa9))
+  cb.append('psaA: ' + repr(psaA))
+  cb.append('psaB: ' + repr(psaB))
+  cb.append('psaC: ' + repr(psaC))
 
-psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'})
-psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]]
-psar.self['rec'] = psar
-psar.self['self'] = psar.self
-psar.self['args'] = psar.args
+  psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'})
+  psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]]
+  psar.self['rec'] = psar
+  psar.self['self'] = psar.self
+  psar.self['args'] = psar.args
 
-try:
-    cb.append('psar: ' + repr(psar))
-except Exception:
-    cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+  try:
+      cb.append('psar: ' + repr(psar))
+  except Exception:
+      cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
 EOF
 :$put ='s(a): '.string(pyeval('a'))
 :$put ='s(pa1): '.string(pyeval('pa1'))
@@ -1029,88 +1029,88 @@ EOF
 :py ecall('psa2(self={"20": 1})', psa2, self={'20': 1})
 :py ecall('psa3(self={"20": 1})', psa3, self={'20': 1})
 :py ecall('psa4(self={"20": 1})', psa4, self={'20': 1})
-py << EOF
-def s(v):
-    if v is None:
-        return repr(v)
-    else:
-        return vim.Function('string')(v)
+py << trim EOF
+  def s(v):
+      if v is None:
+          return repr(v)
+      else:
+          return vim.Function('string')(v)
 
-cb.append('a.args: ' + s(a.args))
-cb.append('pa1.args: ' + s(pa1.args))
-cb.append('pa2.args: ' + s(pa2.args))
-cb.append('pa3.args: ' + s(pa3.args))
-cb.append('pa4.args: ' + s(pa4.args))
-cb.append('sa.args: ' + s(sa.args))
-cb.append('psa1.args: ' + s(psa1.args))
-cb.append('psa2.args: ' + s(psa2.args))
-cb.append('psa3.args: ' + s(psa3.args))
-cb.append('psa4.args: ' + s(psa4.args))
+  cb.append('a.args: ' + s(a.args))
+  cb.append('pa1.args: ' + s(pa1.args))
+  cb.append('pa2.args: ' + s(pa2.args))
+  cb.append('pa3.args: ' + s(pa3.args))
+  cb.append('pa4.args: ' + s(pa4.args))
+  cb.append('sa.args: ' + s(sa.args))
+  cb.append('psa1.args: ' + s(psa1.args))
+  cb.append('psa2.args: ' + s(psa2.args))
+  cb.append('psa3.args: ' + s(psa3.args))
+  cb.append('psa4.args: ' + s(psa4.args))
 
-cb.append('a.self: ' + s(a.self))
-cb.append('pa1.self: ' + s(pa1.self))
-cb.append('pa2.self: ' + s(pa2.self))
-cb.append('pa3.self: ' + s(pa3.self))
-cb.append('pa4.self: ' + s(pa4.self))
-cb.append('sa.self: ' + s(sa.self))
-cb.append('psa1.self: ' + s(psa1.self))
-cb.append('psa2.self: ' + s(psa2.self))
-cb.append('psa3.self: ' + s(psa3.self))
-cb.append('psa4.self: ' + s(psa4.self))
+  cb.append('a.self: ' + s(a.self))
+  cb.append('pa1.self: ' + s(pa1.self))
+  cb.append('pa2.self: ' + s(pa2.self))
+  cb.append('pa3.self: ' + s(pa3.self))
+  cb.append('pa4.self: ' + s(pa4.self))
+  cb.append('sa.self: ' + s(sa.self))
+  cb.append('psa1.self: ' + s(psa1.self))
+  cb.append('psa2.self: ' + s(psa2.self))
+  cb.append('psa3.self: ' + s(psa3.self))
+  cb.append('psa4.self: ' + s(psa4.self))
 
-cb.append('a.name: ' + s(a.name))
-cb.append('pa1.name: ' + s(pa1.name))
-cb.append('pa2.name: ' + s(pa2.name))
-cb.append('pa3.name: ' + s(pa3.name))
-cb.append('pa4.name: ' + s(pa4.name))
-cb.append('sa.name: ' + s(sa.name))
-cb.append('psa1.name: ' + s(psa1.name))
-cb.append('psa2.name: ' + s(psa2.name))
-cb.append('psa3.name: ' + s(psa3.name))
-cb.append('psa4.name: ' + s(psa4.name))
+  cb.append('a.name: ' + s(a.name))
+  cb.append('pa1.name: ' + s(pa1.name))
+  cb.append('pa2.name: ' + s(pa2.name))
+  cb.append('pa3.name: ' + s(pa3.name))
+  cb.append('pa4.name: ' + s(pa4.name))
+  cb.append('sa.name: ' + s(sa.name))
+  cb.append('psa1.name: ' + s(psa1.name))
+  cb.append('psa2.name: ' + s(psa2.name))
+  cb.append('psa3.name: ' + s(psa3.name))
+  cb.append('psa4.name: ' + s(psa4.name))
 
-cb.append('a.auto_rebind: ' + s(a.auto_rebind))
-cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind))
-cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind))
-cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind))
-cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind))
-cb.append('sa.auto_rebind: ' + s(sa.auto_rebind))
-cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind))
-cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind))
-cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind))
-cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind))
-cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind))
-cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind))
-cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind))
-cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind))
-cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind))
-cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind))
-cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind))
-cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind))
+  cb.append('a.auto_rebind: ' + s(a.auto_rebind))
+  cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind))
+  cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind))
+  cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind))
+  cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind))
+  cb.append('sa.auto_rebind: ' + s(sa.auto_rebind))
+  cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind))
+  cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind))
+  cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind))
+  cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind))
+  cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind))
+  cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind))
+  cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind))
+  cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind))
+  cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind))
+  cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind))
+  cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind))
+  cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind))
 
-del s
+  del s
 
-del a
-del pa1
-del pa2
-del pa3
-del pa4
-del sa
-del psa1
-del psa2
-del psa3
-del psa4
-del psa5
-del psa6
-del psa7
-del psa8
-del psa9
-del psaA
-del psaB
-del psaC
-del psar
+  del a
+  del pa1
+  del pa2
+  del pa3
+  del pa4
+  del sa
+  del psa1
+  del psa2
+  del psa3
+  del psa4
+  del psa5
+  del psa6
+  del psa7
+  del psa8
+  del psa9
+  del psaA
+  del psaB
+  del psaC
+  del psar
 
-del ecall
+  del ecall
 EOF
 :"
 :" Test stdout/stderr
@@ -1126,27 +1126,27 @@ EOF
 :   $put =string(a:000)
 :   return a:000
 :endfun
-py << EOF
-class DupDict(vim.Dictionary):
-    def __setitem__(self, key, value):
-        super(DupDict, self).__setitem__(key, value)
-        super(DupDict, self).__setitem__('dup_' + key, value)
-dd = DupDict()
-dd['a'] = 'b'
+py << trim EOF
+  class DupDict(vim.Dictionary):
+      def __setitem__(self, key, value):
+          super(DupDict, self).__setitem__(key, value)
+          super(DupDict, self).__setitem__('dup_' + key, value)
+  dd = DupDict()
+  dd['a'] = 'b'
 
-class DupList(vim.List):
-    def __getitem__(self, idx):
-        return [super(DupList, self).__getitem__(idx)] * 2
+  class DupList(vim.List):
+      def __getitem__(self, idx):
+          return [super(DupList, self).__getitem__(idx)] * 2
 
-dl = DupList()
-dl2 = DupList(iter('abcC'))
-dl.extend(dl2[0])
+  dl = DupList()
+  dl2 = DupList(iter('abcC'))
+  dl.extend(dl2[0])
 
-class DupFun(vim.Function):
-    def __call__(self, arg):
-        return super(DupFun, self).__call__(arg, arg)
+  class DupFun(vim.Function):
+      def __call__(self, arg):
+          return super(DupFun, self).__call__(arg, arg)
 
-df = DupFun('Put')
+  df = DupFun('Put')
 EOF
 :$put =string(sort(keys(pyeval('dd'))))
 :$put =string(pyeval('dl'))
@@ -1156,40 +1156,40 @@ EOF
 :$put =string(pyeval('dd') is# pyeval('dd'))
 :$put =string(pyeval('df'))
 :delfunction Put
-py <<
-del DupDict
-del DupList
-del DupFun
-del dd
-del dl
-del dl2
-del df
+py << trim
+  del DupDict
+  del DupList
+  del DupFun
+  del dd
+  del dl
+  del dl2
+  del df
 .
 :"
 :" Test chdir
-py << EOF
-import os
-fnamemodify = vim.Function('fnamemodify')
-cb.append(fnamemodify('.', ':p:h:t'))
-cb.append(vim.eval('@%'))
-os.chdir('..')
-path = fnamemodify('.', ':p:h:t')
-if path != 'src':
-  # Running tests from a shadow directory, so move up another level
-  # This will result in @% looking like shadow/testdir/test86.in, hence the
-  # extra fnamemodify
+py << trim EOF
+  import os
+  fnamemodify = vim.Function('fnamemodify')
+  cb.append(fnamemodify('.', ':p:h:t'))
+  cb.append(vim.eval('@%'))
   os.chdir('..')
-  cb.append(fnamemodify('.', ':p:h:t'))
-  cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/'))
-  os.chdir(path)
-  del path
-else:
+  path = fnamemodify('.', ':p:h:t')
+  if path != 'src':
+    # Running tests from a shadow directory, so move up another level
+    # This will result in @% looking like shadow/testdir/test86.in, hence the
+    # extra fnamemodify
+    os.chdir('..')
+    cb.append(fnamemodify('.', ':p:h:t'))
+    cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/'))
+    os.chdir(path)
+    del path
+  else:
+    cb.append(fnamemodify('.', ':p:h:t'))
+    cb.append(vim.eval('@%').replace(os.path.sep, '/'))
+  os.chdir('testdir')
   cb.append(fnamemodify('.', ':p:h:t'))
-  cb.append(vim.eval('@%').replace(os.path.sep, '/'))
-os.chdir('testdir')
-cb.append(fnamemodify('.', ':p:h:t'))
-cb.append(vim.eval('@%'))
-del fnamemodify
+  cb.append(vim.eval('@%'))
+  del fnamemodify
 EOF
 :"
 :" Test errors
@@ -1197,464 +1197,464 @@ EOF
 :endfun
 :fun D()
 :endfun
-py << EOF
-d = vim.Dictionary()
-ned = vim.Dictionary(foo='bar', baz='abcD')
-dl = vim.Dictionary(a=1)
-dl.locked = True
-l = vim.List()
-ll = vim.List('abcE')
-ll.locked = True
-nel = vim.List('abcO')
-f = vim.Function('string')
-fd = vim.Function('F')
-fdel = vim.Function('D')
-vim.command('delfunction D')
+py << trim EOF
+  d = vim.Dictionary()
+  ned = vim.Dictionary(foo='bar', baz='abcD')
+  dl = vim.Dictionary(a=1)
+  dl.locked = True
+  l = vim.List()
+  ll = vim.List('abcE')
+  ll.locked = True
+  nel = vim.List('abcO')
+  f = vim.Function('string')
+  fd = vim.Function('F')
+  fdel = vim.Function('D')
+  vim.command('delfunction D')
 
-def subexpr_test(expr, name, subexprs):
-    cb.append('>>> Testing %s using %s' % (name, expr))
-    for subexpr in subexprs:
-        ee(expr % subexpr)
-    cb.append('<<< Finished')
+  def subexpr_test(expr, name, subexprs):
+      cb.append('>>> Testing %s using %s' % (name, expr))
+      for subexpr in subexprs:
+          ee(expr % subexpr)
+      cb.append('<<< Finished')
 
-def stringtochars_test(expr):
-    return subexpr_test(expr, 'StringToChars', (
-        '1',       # Fail type checks
-        'u"\\0"',  # Fail PyString_AsStringAndSize(bytes, , NULL) check
-        '"\\0"',   # Fail PyString_AsStringAndSize(object, , NULL) check
-    ))
+  def stringtochars_test(expr):
+      return subexpr_test(expr, 'StringToChars', (
+          '1',       # Fail type checks
+          'u"\\0"',  # Fail PyString_AsStringAndSize(bytes, , NULL) check
+          '"\\0"',   # Fail PyString_AsStringAndSize(object, , NULL) check
+      ))
 
-class Mapping(object):
-    def __init__(self, d):
-        self.d = d
+  class Mapping(object):
+      def __init__(self, d):
+          self.d = d
 
-    def __getitem__(self, key):
-        return self.d[key]
+      def __getitem__(self, key):
+          return self.d[key]
 
-    def keys(self):
-        return self.d.keys()
+      def keys(self):
+          return self.d.keys()
 
-    def items(self):
-        return self.d.items()
+      def items(self):
+          return self.d.items()
 
-def convertfrompyobject_test(expr, recurse=True):
-    # pydict_to_tv
-    stringtochars_test(expr % '{%s : 1}')
-    if recurse:
-        convertfrompyobject_test(expr % '{"abcF" : %s}', False)
-    # pymap_to_tv
-    stringtochars_test(expr % 'Mapping({%s : 1})')
-    if recurse:
-        convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False)
-    # pyseq_to_tv
-    iter_test(expr)
-    return subexpr_test(expr, 'ConvertFromPyObject', (
-        'None',                 # Not conversible
-        '{"": 1}',              # Empty key not allowed
-        '{u"": 1}',             # Same, but with unicode object
-        'FailingMapping()',     #
-        'FailingMappingKey()',  #
-        'FailingNumber()',      #
-    ))
+  def convertfrompyobject_test(expr, recurse=True):
+      # pydict_to_tv
+      stringtochars_test(expr % '{%s : 1}')
+      if recurse:
+          convertfrompyobject_test(expr % '{"abcF" : %s}', False)
+      # pymap_to_tv
+      stringtochars_test(expr % 'Mapping({%s : 1})')
+      if recurse:
+          convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False)
+      # pyseq_to_tv
+      iter_test(expr)
+      return subexpr_test(expr, 'ConvertFromPyObject', (
+          'None',                 # Not conversible
+          '{"": 1}',              # Empty key not allowed
+          '{u"": 1}',             # Same, but with unicode object
+          'FailingMapping()',     #
+          'FailingMappingKey()',  #
+          'FailingNumber()',      #
+      ))
 
-def convertfrompymapping_test(expr):
-    convertfrompyobject_test(expr)
-    return subexpr_test(expr, 'ConvertFromPyMapping', (
-        '[]',
-    ))
+  def convertfrompymapping_test(expr):
+      convertfrompyobject_test(expr)
+      return subexpr_test(expr, 'ConvertFromPyMapping', (
+          '[]',
+      ))
 
-def iter_test(expr):
-    return subexpr_test(expr, '*Iter*', (
-        'FailingIter()',
-        'FailingIterNext()',
-    ))
+  def iter_test(expr):
+      return subexpr_test(expr, '*Iter*', (
+          'FailingIter()',
+          'FailingIterNext()',
+      ))
 
-def number_test(expr, natural=False, unsigned=False):
-    if natural:
-        unsigned = True
-    return subexpr_test(expr, 'NumberToLong', (
-        '[]',
-        'None',
-    ) + (unsigned and ('-1',) or ())
-    + (natural and ('0',) or ()))
+  def number_test(expr, natural=False, unsigned=False):
+      if natural:
+          unsigned = True
+      return subexpr_test(expr, 'NumberToLong', (
+          '[]',
+          'None',
+      ) + (unsigned and ('-1',) or ())
+      + (natural and ('0',) or ()))
 
-class FailingTrue(object):
-    def __nonzero__(self):
-        raise NotImplementedError('bool')
+  class FailingTrue(object):
+      def __nonzero__(self):
+          raise NotImplementedError('bool')
 
-class FailingIter(object):
-    def __iter__(self):
-        raise NotImplementedError('iter')
+  class FailingIter(object):
+      def __iter__(self):
+          raise NotImplementedError('iter')
 
-class FailingIterNext(object):
-    def __iter__(self):
-        return self
+  class FailingIterNext(object):
+      def __iter__(self):
+          return self
 
-    def next(self):
-        raise NotImplementedError('next')
+      def next(self):
+          raise NotImplementedError('next')
 
-class FailingIterNextN(object):
-    def __init__(self, n):
-        self.n = n
+  class FailingIterNextN(object):
+      def __init__(self, n):
+          self.n = n
 
-    def __iter__(self):
-        return self
+      def __iter__(self):
+          return self
 
-    def next(self):
-        if self.n:
-            self.n -= 1
-            return 1
-        else:
-            raise NotImplementedError('next N')
+      def next(self):
+          if self.n:
+              self.n -= 1
+              return 1
+          else:
+              raise NotImplementedError('next N')
 
-class FailingMappingKey(object):
-    def __getitem__(self, item):
-        raise NotImplementedError('getitem:mappingkey')
+  class FailingMappingKey(object):
+      def __getitem__(self, item):
+          raise NotImplementedError('getitem:mappingkey')
 
-    def keys(self):
-        return list("abcH")
+      def keys(self):
+          return list("abcH")
 
-class FailingMapping(object):
-    def __getitem__(self):
-        raise NotImplementedError('getitem:mapping')
+  class FailingMapping(object):
+      def __getitem__(self):
+          raise NotImplementedError('getitem:mapping')
 
-    def keys(self):
-        raise NotImplementedError('keys')
+      def keys(self):
+          raise NotImplementedError('keys')
 
-class FailingList(list):
-    def __getitem__(self, idx):
-        if i == 2:
-            raise NotImplementedError('getitem:list')
-        else:
-            return super(FailingList, self).__getitem__(idx)
+  class FailingList(list):
+      def __getitem__(self, idx):
+          if i == 2:
+              raise NotImplementedError('getitem:list')
+          else:
+              return super(FailingList, self).__getitem__(idx)
 
-class NoArgsCall(object):
-    def __call__(self):
-        pass
+  class NoArgsCall(object):
+      def __call__(self):
+          pass
 
-class FailingCall(object):
-    def __call__(self, path):
-        raise NotImplementedError('call')
+  class FailingCall(object):
+      def __call__(self, path):
+          raise NotImplementedError('call')
 
-class FailingNumber(object):
-    def __int__(self):
-        raise NotImplementedError('int')
+  class FailingNumber(object):
+      def __int__(self):
+          raise NotImplementedError('int')
 
-cb.append("> Output")
-cb.append(">> OutputSetattr")
-ee('del sys.stdout.softspace')
-number_test('sys.stdout.softspace = %s', unsigned=True)
-number_test('sys.stderr.softspace = %s', unsigned=True)
-ee('assert sys.stdout.isatty()==False')
-ee('assert sys.stdout.seekable()==False')
-ee('sys.stdout.close()')
-ee('sys.stdout.flush()')
-ee('assert sys.stderr.isatty()==False')
-ee('assert sys.stderr.seekable()==False')
-ee('sys.stderr.close()')
-ee('sys.stderr.flush()')
-ee('sys.stdout.attr = None')
-cb.append(">> OutputWrite")
-ee('assert sys.stdout.writable()==True')
-ee('assert sys.stdout.readable()==False')
-ee('assert sys.stderr.writable()==True')
-ee('assert sys.stderr.readable()==False')
-ee('assert sys.stdout.closed()==False')
-ee('assert sys.stderr.closed()==False')
-ee('assert sys.stdout.errors=="strict"')
-ee('assert sys.stderr.errors=="strict"')
-ee('assert sys.stdout.encoding==sys.stderr.encoding')
-ee('sys.stdout.write(None)')
-cb.append(">> OutputWriteLines")
-ee('sys.stdout.writelines(None)')
-ee('sys.stdout.writelines([1])')
-iter_test('sys.stdout.writelines(%s)')
-cb.append("> VimCommand")
-stringtochars_test('vim.command(%s)')
-ee('vim.command("", 2)')
-#! Not checked: vim->python exceptions translating: checked later
-cb.append("> VimToPython")
-#! Not checked: everything: needs errors in internal python functions
-cb.append("> VimEval")
-stringtochars_test('vim.eval(%s)')
-ee('vim.eval("", FailingTrue())')
-#! Not checked: everything: needs errors in internal python functions
-cb.append("> VimEvalPy")
-stringtochars_test('vim.bindeval(%s)')
-ee('vim.eval("", 2)')
-#! Not checked: vim->python exceptions translating: checked later
-cb.append("> VimStrwidth")
-stringtochars_test('vim.strwidth(%s)')
-cb.append("> VimForeachRTP")
-ee('vim.foreach_rtp(None)')
-ee('vim.foreach_rtp(NoArgsCall())')
-ee('vim.foreach_rtp(FailingCall())')
-ee('vim.foreach_rtp(int, 2)')
-cb.append('> import')
-old_rtp = vim.options['rtp']
-vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,')
-ee('import xxx_no_such_module_xxx')
-ee('import failing_import')
-ee('import failing')
-vim.options['rtp'] = old_rtp
-del old_rtp
-cb.append("> Options")
-cb.append(">> OptionsItem")
-ee('vim.options["abcQ"]')
-ee('vim.options[""]')
-stringtochars_test('vim.options[%s]')
-cb.append(">> OptionsContains")
-stringtochars_test('%s in vim.options')
-cb.append("> Dictionary")
-cb.append(">> DictionaryConstructor")
-ee('vim.Dictionary("abcI")')
-##! Not checked: py_dict_alloc failure
-cb.append(">> DictionarySetattr")
-ee('del d.locked')
-ee('d.locked = FailingTrue()')
-ee('vim.vvars.locked = False')
-ee('d.scope = True')
-ee('d.xxx = True')
-cb.append(">> _DictionaryItem")
-ee('d.get("a", 2, 3)')
-stringtochars_test('d.get(%s)')
-ee('d.pop("a")')
-ee('dl.pop("a")')
-cb.append(">> DictionaryContains")
-ee('"" in d')
-ee('0 in d')
-cb.append(">> DictionaryIterNext")
-ee('for i in ned: ned["a"] = 1')
-del i
-cb.append(">> DictionaryAssItem")
-ee('dl["b"] = 1')
-stringtochars_test('d[%s] = 1')
-convertfrompyobject_test('d["a"] = %s')
-cb.append(">> DictionaryUpdate")
-cb.append(">>> kwargs")
-cb.append(">>> iter")
-ee('d.update(FailingMapping())')
-ee('d.update([FailingIterNext()])')
-ee('d.update([FailingIterNextN(1)])')
-iter_test('d.update(%s)')
-convertfrompyobject_test('d.update(%s)')
-stringtochars_test('d.update(((%s, 0),))')
-convertfrompyobject_test('d.update((("a", %s),))')
-cb.append(">> DictionaryPopItem")
-ee('d.popitem(1, 2)')
-cb.append(">> DictionaryHasKey")
-ee('d.has_key()')
-cb.append("> List")
-cb.append(">> ListConstructor")
-ee('vim.List(1, 2)')
-ee('vim.List(a=1)')
-iter_test('vim.List(%s)')
-convertfrompyobject_test('vim.List([%s])')
-cb.append(">> ListItem")
-ee('l[1000]')
-cb.append(">> ListAssItem")
-ee('ll[1] = 2')
-ee('l[1000] = 3')
-cb.append(">> ListAssSlice")
-ee('ll[1:100] = "abcJ"')
-iter_test('l[:] = %s')
-ee('nel[1:10:2]  = "abcK"')
-cb.append(repr(tuple(nel)))
-ee('nel[1:10:2]  = "a"')
-cb.append(repr(tuple(nel)))
-ee('nel[1:1:-1]  = "a"')
-cb.append(repr(tuple(nel)))
-ee('nel[:] = FailingIterNextN(2)')
-cb.append(repr(tuple(nel)))
-convertfrompyobject_test('l[:] = [%s]')
-cb.append(">> ListConcatInPlace")
-iter_test('l.extend(%s)')
-convertfrompyobject_test('l.extend([%s])')
-cb.append(">> ListSetattr")
-ee('del l.locked')
-ee('l.locked = FailingTrue()')
-ee('l.xxx = True')
-cb.append("> Function")
-cb.append(">> FunctionConstructor")
-cb.append(">>> FunctionConstructor")
-ee('vim.Function("123")')
-ee('vim.Function("xxx_non_existent_function_xxx")')
-ee('vim.Function("xxx#non#existent#function#xxx")')
-ee('vim.Function("xxx_non_existent_function_xxx2", args=[])')
-ee('vim.Function("xxx_non_existent_function_xxx3", self={})')
-ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})')
-cb.append(">>> FunctionNew")
-ee('vim.Function("tr", self="abcFuncSelf")')
-ee('vim.Function("tr", args=427423)')
-ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function("tr", "")')
-cb.append(">> FunctionCall")
-convertfrompyobject_test('f(%s)')
-convertfrompymapping_test('fd(self=%s)')
-cb.append("> TabPage")
-cb.append(">> TabPageAttr")
-ee('vim.current.tabpage.xxx')
-cb.append("> TabList")
-cb.append(">> TabListItem")
-ee('vim.tabpages[1000]')
-cb.append("> Window")
-cb.append(">> WindowAttr")
-ee('vim.current.window.xxx')
-cb.append(">> WindowSetattr")
-ee('vim.current.window.buffer = 0')
-ee('vim.current.window.cursor = (100000000, 100000000)')
-ee('vim.current.window.cursor = True')
-number_test('vim.current.window.height = %s', unsigned=True)
-number_test('vim.current.window.width = %s', unsigned=True)
-ee('vim.current.window.xxxxxx = True')
-cb.append("> WinList")
-cb.append(">> WinListItem")
-ee('vim.windows[1000]')
-cb.append("> Buffer")
-cb.append(">> StringToLine (indirect)")
-ee('vim.current.buffer[0] = u"\\na"')
-ee('vim.current.buffer[0] = "\\na"')
-cb.append(">> SetBufferLine (indirect)")
-ee('vim.current.buffer[0] = True')
-cb.append(">> SetBufferLineList (indirect)")
-ee('vim.current.buffer[:] = True')
-ee('vim.current.buffer[:] = ["\\na", "bc"]')
-cb.append(">> InsertBufferLines (indirect)")
-ee('vim.current.buffer.append(None)')
-ee('vim.current.buffer.append(["\\na", "bc"])')
-ee('vim.current.buffer.append("\\nbc")')
-cb.append(">> RBItem")
-ee('vim.current.buffer[100000000]')
-cb.append(">> RBAsItem")
-ee('vim.current.buffer[100000000] = ""')
-cb.append(">> BufferAttr")
-ee('vim.current.buffer.xxx')
-cb.append(">> BufferSetattr")
-ee('vim.current.buffer.name = True')
-ee('vim.current.buffer.xxx = True')
-cb.append(">> BufferMark")
-ee('vim.current.buffer.mark(0)')
-ee('vim.current.buffer.mark("abcM")')
-ee('vim.current.buffer.mark("!")')
-cb.append(">> BufferRange")
-ee('vim.current.buffer.range(1, 2, 3)')
-cb.append("> BufMap")
-cb.append(">> BufMapItem")
-ee('vim.buffers[100000000]')
-number_test('vim.buffers[%s]', natural=True)
-cb.append("> Current")
-cb.append(">> CurrentGetattr")
-ee('vim.current.xxx')
-cb.append(">> CurrentSetattr")
-ee('vim.current.line = True')
-ee('vim.current.buffer = True')
-ee('vim.current.window = True')
-ee('vim.current.tabpage = True')
-ee('vim.current.xxx = True')
-del d
-del ned
-del dl
-del l
-del ll
-del nel
-del f
-del fd
-del fdel
-del subexpr_test
-del stringtochars_test
-del Mapping
-del convertfrompyobject_test
-del convertfrompymapping_test
-del iter_test
-del number_test
-del FailingTrue
-del FailingIter
-del FailingIterNext
-del FailingIterNextN
-del FailingMapping
-del FailingMappingKey
-del FailingList
-del NoArgsCall
-del FailingCall
-del FailingNumber
+  cb.append("> Output")
+  cb.append(">> OutputSetattr")
+  ee('del sys.stdout.softspace')
+  number_test('sys.stdout.softspace = %s', unsigned=True)
+  number_test('sys.stderr.softspace = %s', unsigned=True)
+  ee('assert sys.stdout.isatty()==False')
+  ee('assert sys.stdout.seekable()==False')
+  ee('sys.stdout.close()')
+  ee('sys.stdout.flush()')
+  ee('assert sys.stderr.isatty()==False')
+  ee('assert sys.stderr.seekable()==False')
+  ee('sys.stderr.close()')
+  ee('sys.stderr.flush()')
+  ee('sys.stdout.attr = None')
+  cb.append(">> OutputWrite")
+  ee('assert sys.stdout.writable()==True')
+  ee('assert sys.stdout.readable()==False')
+  ee('assert sys.stderr.writable()==True')
+  ee('assert sys.stderr.readable()==False')
+  ee('assert sys.stdout.closed()==False')
+  ee('assert sys.stderr.closed()==False')
+  ee('assert sys.stdout.errors=="strict"')
+  ee('assert sys.stderr.errors=="strict"')
+  ee('assert sys.stdout.encoding==sys.stderr.encoding')
+  ee('sys.stdout.write(None)')
+  cb.append(">> OutputWriteLines")
+  ee('sys.stdout.writelines(None)')
+  ee('sys.stdout.writelines([1])')
+  iter_test('sys.stdout.writelines(%s)')
+  cb.append("> VimCommand")
+  stringtochars_test('vim.command(%s)')
+  ee('vim.command("", 2)')
+  #! Not checked: vim->python exceptions translating: checked later
+  cb.append("> VimToPython")
+  #! Not checked: everything: needs errors in internal python functions
+  cb.append("> VimEval")
+  stringtochars_test('vim.eval(%s)')
+  ee('vim.eval("", FailingTrue())')
+  #! Not checked: everything: needs errors in internal python functions
+  cb.append("> VimEvalPy")
+  stringtochars_test('vim.bindeval(%s)')
+  ee('vim.eval("", 2)')
+  #! Not checked: vim->python exceptions translating: checked later
+  cb.append("> VimStrwidth")
+  stringtochars_test('vim.strwidth(%s)')
+  cb.append("> VimForeachRTP")
+  ee('vim.foreach_rtp(None)')
+  ee('vim.foreach_rtp(NoArgsCall())')
+  ee('vim.foreach_rtp(FailingCall())')
+  ee('vim.foreach_rtp(int, 2)')
+  cb.append('> import')
+  old_rtp = vim.options['rtp']
+  vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,')
+  ee('import xxx_no_such_module_xxx')
+  ee('import failing_import')
+  ee('import failing')
+  vim.options['rtp'] = old_rtp
+  del old_rtp
+  cb.append("> Options")
+  cb.append(">> OptionsItem")
+  ee('vim.options["abcQ"]')
+  ee('vim.options[""]')
+  stringtochars_test('vim.options[%s]')
+  cb.append(">> OptionsContains")
+  stringtochars_test('%s in vim.options')
+  cb.append("> Dictionary")
+  cb.append(">> DictionaryConstructor")
+  ee('vim.Dictionary("abcI")')
+  ##! Not checked: py_dict_alloc failure
+  cb.append(">> DictionarySetattr")
+  ee('del d.locked')
+  ee('d.locked = FailingTrue()')
+  ee('vim.vvars.locked = False')
+  ee('d.scope = True')
+  ee('d.xxx = True')
+  cb.append(">> _DictionaryItem")
+  ee('d.get("a", 2, 3)')
+  stringtochars_test('d.get(%s)')
+  ee('d.pop("a")')
+  ee('dl.pop("a")')
+  cb.append(">> DictionaryContains")
+  ee('"" in d')
+  ee('0 in d')
+  cb.append(">> DictionaryIterNext")
+  ee('for i in ned: ned["a"] = 1')
+  del i
+  cb.append(">> DictionaryAssItem")
+  ee('dl["b"] = 1')
+  stringtochars_test('d[%s] = 1')
+  convertfrompyobject_test('d["a"] = %s')
+  cb.append(">> DictionaryUpdate")
+  cb.append(">>> kwargs")
+  cb.append(">>> iter")
+  ee('d.update(FailingMapping())')
+  ee('d.update([FailingIterNext()])')
+  ee('d.update([FailingIterNextN(1)])')
+  iter_test('d.update(%s)')
+  convertfrompyobject_test('d.update(%s)')
+  stringtochars_test('d.update(((%s, 0),))')
+  convertfrompyobject_test('d.update((("a", %s),))')
+  cb.append(">> DictionaryPopItem")
+  ee('d.popitem(1, 2)')
+  cb.append(">> DictionaryHasKey")
+  ee('d.has_key()')
+  cb.append("> List")
+  cb.append(">> ListConstructor")
+  ee('vim.List(1, 2)')
+  ee('vim.List(a=1)')
+  iter_test('vim.List(%s)')
+  convertfrompyobject_test('vim.List([%s])')
+  cb.append(">> ListItem")
+  ee('l[1000]')
+  cb.append(">> ListAssItem")
+  ee('ll[1] = 2')
+  ee('l[1000] = 3')
+  cb.append(">> ListAssSlice")
+  ee('ll[1:100] = "abcJ"')
+  iter_test('l[:] = %s')
+  ee('nel[1:10:2]  = "abcK"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[1:10:2]  = "a"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[1:1:-1]  = "a"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[:] = FailingIterNextN(2)')
+  cb.append(repr(tuple(nel)))
+  convertfrompyobject_test('l[:] = [%s]')
+  cb.append(">> ListConcatInPlace")
+  iter_test('l.extend(%s)')
+  convertfrompyobject_test('l.extend([%s])')
+  cb.append(">> ListSetattr")
+  ee('del l.locked')
+  ee('l.locked = FailingTrue()')
+  ee('l.xxx = True')
+  cb.append("> Function")
+  cb.append(">> FunctionConstructor")
+  cb.append(">>> FunctionConstructor")
+  ee('vim.Function("123")')
+  ee('vim.Function("xxx_non_existent_function_xxx")')
+  ee('vim.Function("xxx#non#existent#function#xxx")')
+  ee('vim.Function("xxx_non_existent_function_xxx2", args=[])')
+  ee('vim.Function("xxx_non_existent_function_xxx3", self={})')
+  ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})')
+  cb.append(">>> FunctionNew")
+  ee('vim.Function("tr", self="abcFuncSelf")')
+  ee('vim.Function("tr", args=427423)')
+  ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function("tr", "")')
+  cb.append(">> FunctionCall")
+  convertfrompyobject_test('f(%s)')
+  convertfrompymapping_test('fd(self=%s)')
+  cb.append("> TabPage")
+  cb.append(">> TabPageAttr")
+  ee('vim.current.tabpage.xxx')
+  cb.append("> TabList")
+  cb.append(">> TabListItem")
+  ee('vim.tabpages[1000]')
+  cb.append("> Window")
+  cb.append(">> WindowAttr")
+  ee('vim.current.window.xxx')
+  cb.append(">> WindowSetattr")
+  ee('vim.current.window.buffer = 0')
+  ee('vim.current.window.cursor = (100000000, 100000000)')
+  ee('vim.current.window.cursor = True')
+  number_test('vim.current.window.height = %s', unsigned=True)
+  number_test('vim.current.window.width = %s', unsigned=True)
+  ee('vim.current.window.xxxxxx = True')
+  cb.append("> WinList")
+  cb.append(">> WinListItem")
+  ee('vim.windows[1000]')
+  cb.append("> Buffer")
+  cb.append(">> StringToLine (indirect)")
+  ee('vim.current.buffer[0] = u"\\na"')
+  ee('vim.current.buffer[0] = "\\na"')
+  cb.append(">> SetBufferLine (indirect)")
+  ee('vim.current.buffer[0] = True')
+  cb.append(">> SetBufferLineList (indirect)")
+  ee('vim.current.buffer[:] = True')
+  ee('vim.current.buffer[:] = ["\\na", "bc"]')
+  cb.append(">> InsertBufferLines (indirect)")
+  ee('vim.current.buffer.append(None)')
+  ee('vim.current.buffer.append(["\\na", "bc"])')
+  ee('vim.current.buffer.append("\\nbc")')
+  cb.append(">> RBItem")
+  ee('vim.current.buffer[100000000]')
+  cb.append(">> RBAsItem")
+  ee('vim.current.buffer[100000000] = ""')
+  cb.append(">> BufferAttr")
+  ee('vim.current.buffer.xxx')
+  cb.append(">> BufferSetattr")
+  ee('vim.current.buffer.name = True')
+  ee('vim.current.buffer.xxx = True')
+  cb.append(">> BufferMark")
+  ee('vim.current.buffer.mark(0)')
+  ee('vim.current.buffer.mark("abcM")')
+  ee('vim.current.buffer.mark("!")')
+  cb.append(">> BufferRange")
+  ee('vim.current.buffer.range(1, 2, 3)')
+  cb.append("> BufMap")
+  cb.append(">> BufMapItem")
+  ee('vim.buffers[100000000]')
+  number_test('vim.buffers[%s]', natural=True)
+  cb.append("> Current")
+  cb.append(">> CurrentGetattr")
+  ee('vim.current.xxx')
+  cb.append(">> CurrentSetattr")
+  ee('vim.current.line = True')
+  ee('vim.current.buffer = True')
+  ee('vim.current.window = True')
+  ee('vim.current.tabpage = True')
+  ee('vim.current.xxx = True')
+  del d
+  del ned
+  del dl
+  del l
+  del ll
+  del nel
+  del f
+  del fd
+  del fdel
+  del subexpr_test
+  del stringtochars_test
+  del Mapping
+  del convertfrompyobject_test
+  del convertfrompymapping_test
+  del iter_test
+  del number_test
+  del FailingTrue
+  del FailingIter
+  del FailingIterNext
+  del FailingIterNextN
+  del FailingMapping
+  del FailingMappingKey
+  del FailingList
+  del NoArgsCall
+  del FailingCall
+  del FailingNumber
 EOF
 :delfunction F
 :"
 :" Test import
-py << EOF
-sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
-sys.path.append(os.path.join(os.getcwd(), 'python_after'))
-vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
-l = []
-def callback(path):
-    l.append(path[-len('/testdir'):].replace(os.path.sep, '/'))
-vim.foreach_rtp(callback)
-cb.append(repr(l))
-del l
-def callback(path):
-    return path[-len('/testdir'):].replace(os.path.sep, '/')
-cb.append(repr(vim.foreach_rtp(callback)))
-del callback
-from module import dir as d
-from modulex import ddir
-cb.append(d + ',' + ddir)
-import before
-cb.append(before.dir)
-import after
-cb.append(after.dir)
-import topmodule as tm
-import topmodule.submodule as tms
-import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss
-cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):])
-cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):])
-cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
-del before
-del after
-del d
-del ddir
-del tm
-del tms
-del tmsss
+py << trim EOF
+  sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
+  sys.path.append(os.path.join(os.getcwd(), 'python_after'))
+  vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
+  l = []
+  def callback(path):
+      l.append(path[-len('/testdir'):].replace(os.path.sep, '/'))
+  vim.foreach_rtp(callback)
+  cb.append(repr(l))
+  del l
+  def callback(path):
+      return path[-len('/testdir'):].replace(os.path.sep, '/')
+  cb.append(repr(vim.foreach_rtp(callback)))
+  del callback
+  from module import dir as d
+  from modulex import ddir
+  cb.append(d + ',' + ddir)
+  import before
+  cb.append(before.dir)
+  import after
+  cb.append(after.dir)
+  import topmodule as tm
+  import topmodule.submodule as tms
+  import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss
+  cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):])
+  cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):])
+  cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
+  del before
+  del after
+  del d
+  del ddir
+  del tm
+  del tms
+  del tmsss
 EOF
 :"
 :" Test exceptions
 :fun Exe(e)
 :   execute a:e
 :endfun
-py << EOF
-Exe = vim.bindeval('function("Exe")')
-ee('vim.command("throw \'abcN\'")')
-ee('Exe("throw \'def\'")')
-ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
-ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
-ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
-ee('vim.eval("xxx_unknown_function_xxx()")')
-ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
-del Exe
+py << trim EOF
+  Exe = vim.bindeval('function("Exe")')
+  ee('vim.command("throw \'abcN\'")')
+  ee('Exe("throw \'def\'")')
+  ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
+  ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
+  ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
+  ee('vim.eval("xxx_unknown_function_xxx()")')
+  ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
+  del Exe
 EOF
 :delfunction Exe
 :"
 :" Regression: interrupting vim.command propagates to next vim.command
-py << EOF
-def test_keyboard_interrupt():
-    try:
-        vim.command('while 1 | endwhile')
-    except KeyboardInterrupt:
-        cb.append('Caught KeyboardInterrupt')
-    except Exception:
-        cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
-    else:
-        cb.append('!!!!!!!! No exception')
-    try:
-        vim.command('$ put =\'Running :put\'')
-    except KeyboardInterrupt:
-        cb.append('!!!!!!!! Caught KeyboardInterrupt')
-    except Exception:
-        cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
-    else:
-        cb.append('No exception')
+py << trim EOF
+  def test_keyboard_interrupt():
+      try:
+          vim.command('while 1 | endwhile')
+      except KeyboardInterrupt:
+          cb.append('Caught KeyboardInterrupt')
+      except Exception:
+          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+      else:
+          cb.append('!!!!!!!! No exception')
+      try:
+          vim.command('$ put =\'Running :put\'')
+      except KeyboardInterrupt:
+          cb.append('!!!!!!!! Caught KeyboardInterrupt')
+      except Exception:
+          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+      else:
+          cb.append('No exception')
 EOF
 :debuggreedy
 :call inputsave()
@@ -1669,13 +1669,13 @@ EOF
 :py del test_keyboard_interrupt
 :"
 :" Cleanup
-py << EOF
-del cb
-del ee
-del emsg
-del sys
-del os
-del vim
+py << trim EOF
+  del cb
+  del ee
+  del emsg
+  del sys
+  del os
+  del vim
 EOF
 :endfun
 :"
--- a/src/testdir/test87.in
+++ b/src/testdir/test87.in
@@ -31,19 +31,19 @@ STARTTEST
 :fun d.f()
 :  return 1
 :endfun
-py3 << EOF
-d=vim.bindeval('d')
-d['1']='asd'
-d.update()  # Must not do anything, including throwing errors
-d.update(b=[1, 2, f])
-d.update((('-1', {'a': 1}),))
-d.update({'0': -1})
-dk = d.keys()
-dv = d.values()
-di = d.items()
-dk.sort(key=repr)
-dv.sort(key=repr)
-di.sort(key=repr)
+py3 << trim EOF
+  d=vim.bindeval('d')
+  d['1']='asd'
+  d.update()  # Must not do anything, including throwing errors
+  d.update(b=[1, 2, f])
+  d.update((('-1', {'a': 1}),))
+  d.update({'0': -1})
+  dk = d.keys()
+  dv = d.values()
+  di = d.items()
+  dk.sort(key=repr)
+  dv.sort(key=repr)
+  di.sort(key=repr)
 EOF
 :$put =py3eval('d[''f''](self={})')
 :$put =py3eval('repr(dk)')
@@ -201,77 +201,77 @@ EOF
 :let l = [0, 1, 2, 3]
 :py3 l=vim.bindeval('l')
 :lockvar! l
-py3 << EOF
-def emsg(ei):
-    return ei[0].__name__ + ':' + repr(ei[1].args)
+py3 << trim EOF
+  def emsg(ei):
+      return ei[0].__name__ + ':' + repr(ei[1].args)
 
-try:
-    l[2]='i'
-except vim.error:
-    cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info()))
+  try:
+      l[2]='i'
+  except vim.error:
+      cb.append('l[2] threw vim.error: ' + emsg(sys.exc_info()))
 EOF
 :$put =string(l)
 :unlockvar! l
 :"
 :" Function calls
-py3 << EOF
-import sys
-import re
+py3 << trim EOF
+  import sys
+  import re
 
-py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
-py37_exception_repr = re.compile(r'([^\(\),])(\)+)$')
+  py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
+  py37_exception_repr = re.compile(r'([^\(\),])(\)+)$')
 
-def ee(expr, g=globals(), l=locals()):
-    cb = vim.current.buffer
-    try:
-        try:
-            exec(expr, g, l)
-        except Exception as e:
-            if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."):
-                msg = repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1])))
-            elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
-                msg = repr((e.__class__, ImportError(str(e).replace("'", ''))))
-            elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError:
-                # Python 3.6 gives ModuleNotFoundError, change it to an ImportError
-                msg = repr((ImportError, ImportError(str(e).replace("'", ''))))
-            elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
-                m = py33_type_error_pattern.search(str(e))
-                if m:
-                    msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
-                    msg = repr((e.__class__, TypeError(msg)))
-                else:
-                    msg = repr((e.__class__, e))
-                    # Messages changed with Python 3.6, change new to old.
-                    newmsg1 = """'argument must be str, bytes or bytearray, not None'"""
-                    oldmsg1 = '''"Can't convert 'NoneType' object to str implicitly"'''
-                    if msg.find(newmsg1) > -1:
-                        msg = msg.replace(newmsg1, oldmsg1)
-                    newmsg2 = """'argument must be str, bytes or bytearray, not int'"""
-                    oldmsg2 = '''"Can't convert 'int' object to str implicitly"'''
-                    if msg.find(newmsg2) > -1:
-                        msg = msg.replace(newmsg2, oldmsg2)
-            elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
-                msg = repr((TypeError, TypeError('expected bytes with no null')))
-            else:
-                msg = repr((e.__class__, e))
-                # Some Python versions say can't, others cannot.
-                if msg.find('can\'t') > -1:
-                    msg = msg.replace('can\'t', 'cannot')
-                # Some Python versions use single quote, some double quote
-                if msg.find('"cannot ') > -1:
-                    msg = msg.replace('"cannot ', '\'cannot ')
-                if msg.find(' attributes"') > -1:
-                    msg = msg.replace(' attributes"', ' attributes\'')
-            if sys.version_info >= (3, 7):
-                msg = py37_exception_repr.sub(r'\1,\2', msg)
-            cb.append(expr + ':' + msg)
-        else:
-            cb.append(expr + ':NOT FAILED')
-    except Exception as e:
-        msg = repr((e.__class__, e))
-        if sys.version_info >= (3, 7):
-            msg = py37_exception_repr.sub(r'\1,\2', msg)
-        cb.append(expr + '::' + msg)
+  def ee(expr, g=globals(), l=locals()):
+      cb = vim.current.buffer
+      try:
+          try:
+              exec(expr, g, l)
+          except Exception as e:
+              if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."):
+                  msg = repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1])))
+              elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
+                  msg = repr((e.__class__, ImportError(str(e).replace("'", ''))))
+              elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError:
+                  # Python 3.6 gives ModuleNotFoundError, change it to an ImportError
+                  msg = repr((ImportError, ImportError(str(e).replace("'", ''))))
+              elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
+                  m = py33_type_error_pattern.search(str(e))
+                  if m:
+                      msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
+                      msg = repr((e.__class__, TypeError(msg)))
+                  else:
+                      msg = repr((e.__class__, e))
+                      # Messages changed with Python 3.6, change new to old.
+                      newmsg1 = """'argument must be str, bytes or bytearray, not None'"""
+                      oldmsg1 = '''"Can't convert 'NoneType' object to str implicitly"'''
+                      if msg.find(newmsg1) > -1:
+                          msg = msg.replace(newmsg1, oldmsg1)
+                      newmsg2 = """'argument must be str, bytes or bytearray, not int'"""
+                      oldmsg2 = '''"Can't convert 'int' object to str implicitly"'''
+                      if msg.find(newmsg2) > -1:
+                          msg = msg.replace(newmsg2, oldmsg2)
+              elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
+                  msg = repr((TypeError, TypeError('expected bytes with no null')))
+              else:
+                  msg = repr((e.__class__, e))
+                  # Some Python versions say can't, others cannot.
+                  if msg.find('can\'t') > -1:
+                      msg = msg.replace('can\'t', 'cannot')
+                  # Some Python versions use single quote, some double quote
+                  if msg.find('"cannot ') > -1:
+                      msg = msg.replace('"cannot ', '\'cannot ')
+                  if msg.find(' attributes"') > -1:
+                      msg = msg.replace(' attributes"', ' attributes\'')
+              if sys.version_info >= (3, 7):
+                  msg = py37_exception_repr.sub(r'\1,\2', msg)
+              cb.append(expr + ':' + msg)
+          else:
+              cb.append(expr + ':NOT FAILED')
+      except Exception as e:
+          msg = repr((e.__class__, e))
+          if sys.version_info >= (3, 7):
+              msg = py37_exception_repr.sub(r'\1,\2', msg)
+          cb.append(expr + '::' + msg)
 EOF
 :fun New(...)
 :   return ['NewStart']+a:000+['NewEnd']
@@ -301,30 +301,30 @@ EOF
 :endif
 :let messages=[]
 :delfunction DictNew
-py3 <<EOF
-import sys
-d=vim.bindeval('{}')
-m=vim.bindeval('messages')
-def em(expr, g=globals(), l=locals()):
-    try:
-        exec(expr, g, l)
-    except Exception as e:
-        if sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
-            m.extend([TypeError.__name__])
-        else:
-            m.extend([e.__class__.__name__])
+py3 << trim EOF
+  import sys
+  d=vim.bindeval('{}')
+  m=vim.bindeval('messages')
+  def em(expr, g=globals(), l=locals()):
+      try:
+          exec(expr, g, l)
+      except Exception as e:
+          if sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
+              m.extend([TypeError.__name__])
+          else:
+              m.extend([e.__class__.__name__])
 
-em('d["abc1"]')
-em('d["abc1"]="\\0"')
-em('d["abc1"]=vim')
-em('d[""]=1')
-em('d["a\\0b"]=1')
-em('d[b"a\\0b"]=1')
+  em('d["abc1"]')
+  em('d["abc1"]="\\0"')
+  em('d["abc1"]=vim')
+  em('d[""]=1')
+  em('d["a\\0b"]=1')
+  em('d[b"a\\0b"]=1')
 
-em('d.pop("abc1")')
-em('d.popitem()')
-del em
-del m
+  em('d.pop("abc1")')
+  em('d.popitem()')
+  del em
+  del m
 EOF
 :$put =messages
 :unlet messages
@@ -389,24 +389,24 @@ EOF
 :" threading
 :let l = [0]
 :py3 l=vim.bindeval('l')
-py3 <<EOF
-import threading
-import time
+py3 << trim EOF
+  import threading
+  import time
 
-class T(threading.Thread):
-    def __init__(self):
-        threading.Thread.__init__(self)
-        self.t = 0
-        self.running = True
+  class T(threading.Thread):
+      def __init__(self):
+          threading.Thread.__init__(self)
+          self.t = 0
+          self.running = True
 
-    def run(self):
-        while self.running:
-            self.t += 1
-            time.sleep(0.1)
+      def run(self):
+          while self.running:
+              self.t += 1
+              time.sleep(0.1)
 
-t = T()
-del T
-t.start()
+  t = T()
+  del T
+  t.start()
 EOF
 :sleep 1
 :py3 t.running = False
@@ -422,18 +422,18 @@ EOF
 :" settrace
 :let l = []
 :py3 l=vim.bindeval('l')
-py3 <<EOF
-import sys
+py3 << trim EOF
+  import sys
 
-def traceit(frame, event, arg):
-    global l
-    if event == "line":
-        l += [frame.f_lineno]
-    return traceit
+  def traceit(frame, event, arg):
+      global l
+      if event == "line":
+          l += [frame.f_lineno]
+      return traceit
 
-def trace_main():
-    for i in range(5):
-        pass
+  def trace_main():
+      for i in range(5):
+          pass
 EOF
 :py3 sys.settrace(traceit)
 :py3 trace_main()
@@ -519,19 +519,19 @@ EOF
 :  put ='  W: '.wvals
 :  put ='  B: '.wvals
 :endfun
-py3 << EOF
-def e(s, g=globals(), l=locals()):
-    try:
-        exec(s, g, l)
-    except Exception as e:
-        vim.command('return ' + repr(e.__class__.__name__))
+py3 << trim EOF
+  def e(s, g=globals(), l=locals()):
+      try:
+          exec(s, g, l)
+      except Exception as e:
+          vim.command('return ' + repr(e.__class__.__name__))
 
-def ev(s, g=globals(), l=locals()):
-    try:
-        return eval(s, g, l)
-    except Exception as e:
-        vim.command('let exc=' + repr(e.__class__.__name__))
-        return 0
+  def ev(s, g=globals(), l=locals()):
+      try:
+          return eval(s, g, l)
+      except Exception as e:
+          vim.command('let exc=' + repr(e.__class__.__name__))
+          return 0
 EOF
 :fun E(s)
 :   python3 e(vim.eval('a:s'))
@@ -649,52 +649,52 @@ EOF
 :   autocmd BufFilePost * python3 cb.append(vim.eval('expand("<abuf>")') + ':BufFilePost:' + vim.eval('bufnr("%")'))
 :   autocmd BufFilePre * python3 cb.append(vim.eval('expand("<abuf>")') + ':BufFilePre:' + vim.eval('bufnr("%")'))
 :augroup END
-py3 << EOF
-# Tests BufferAppend and BufferItem
-cb.append(b[0])
-# Tests BufferSlice and BufferAssSlice
-cb.append('abc5') # Will be overwritten
-cb[-1:] = b[:-2]
-# Test BufferLength and BufferAssSlice
-cb.append('def') # Will not be overwritten
-cb[len(cb):] = b[:]
-# Test BufferAssItem and BufferMark
-cb.append('ghi') # Will be overwritten
-cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1]))
-# Test BufferRepr
-cb.append(repr(cb) + repr(b))
-# Modify foreign buffer
-b.append('foo')
-b[0]='bar'
-b[0:0]=['baz']
-vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number)
-# Test assigning to name property
-import os
-old_name = cb.name
-cb.name = 'foo'
-cb.append(cb.name[-11:].replace(os.path.sep, '/'))
-b.name = 'bar'
-cb.append(b.name[-11:].replace(os.path.sep, '/'))
-cb.name = old_name
-cb.append(cb.name[-17:].replace(os.path.sep, '/'))
-del old_name
-# Test CheckBuffer
-for _b in vim.buffers:
-    if _b is not cb:
-        vim.command('bwipeout! ' + str(_b.number))
-del _b
-cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid)))
-for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")'):
-    try:
-        exec(expr)
-    except vim.error:
-        pass
-    else:
-        # Usually a SEGV here
-        # Should not happen in any case
-        cb.append('No exception for ' + expr)
-vim.command('cd .')
-del b
+py3 << trim EOF
+  # Tests BufferAppend and BufferItem
+  cb.append(b[0])
+  # Tests BufferSlice and BufferAssSlice
+  cb.append('abc5') # Will be overwritten
+  cb[-1:] = b[:-2]
+  # Test BufferLength and BufferAssSlice
+  cb.append('def') # Will not be overwritten
+  cb[len(cb):] = b[:]
+  # Test BufferAssItem and BufferMark
+  cb.append('ghi') # Will be overwritten
+  cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1]))
+  # Test BufferRepr
+  cb.append(repr(cb) + repr(b))
+  # Modify foreign buffer
+  b.append('foo')
+  b[0]='bar'
+  b[0:0]=['baz']
+  vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number)
+  # Test assigning to name property
+  import os
+  old_name = cb.name
+  cb.name = 'foo'
+  cb.append(cb.name[-11:].replace(os.path.sep, '/'))
+  b.name = 'bar'
+  cb.append(b.name[-11:].replace(os.path.sep, '/'))
+  cb.name = old_name
+  cb.append(cb.name[-17:].replace(os.path.sep, '/'))
+  del old_name
+  # Test CheckBuffer
+  for _b in vim.buffers:
+      if _b is not cb:
+          vim.command('bwipeout! ' + str(_b.number))
+  del _b
+  cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid)))
+  for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")'):
+      try:
+          exec(expr)
+      except vim.error:
+          pass
+      else:
+          # Usually a SEGV here
+          # Should not happen in any case
+          cb.append('No exception for ' + expr)
+  vim.command('cd .')
+  del b
 EOF
 :"
 :" Test vim.buffers object
@@ -705,55 +705,55 @@ EOF
 :buffer #
 :edit c
 :buffer #
-py3 << EOF
-# Check GCing iterator that was not fully exhausted
-i = iter(vim.buffers)
-cb.append('i:' + str(next(i)))
-# and also check creating more than one iterator at a time
-i2 = iter(vim.buffers)
-cb.append('i2:' + str(next(i2)))
-cb.append('i:' + str(next(i)))
-# The following should trigger GC and not cause any problems
-del i
-del i2
-i3 = iter(vim.buffers)
-cb.append('i3:' + str(next(i3)))
-del i3
+py3 << trim EOF
+  # Check GCing iterator that was not fully exhausted
+  i = iter(vim.buffers)
+  cb.append('i:' + str(next(i)))
+  # and also check creating more than one iterator at a time
+  i2 = iter(vim.buffers)
+  cb.append('i2:' + str(next(i2)))
+  cb.append('i:' + str(next(i)))
+  # The following should trigger GC and not cause any problems
+  del i
+  del i2
+  i3 = iter(vim.buffers)
+  cb.append('i3:' + str(next(i3)))
+  del i3
 
-prevnum = 0
-for b in vim.buffers:
-    # Check buffer order
-    if prevnum >= b.number:
-        cb.append('!!! Buffer numbers not in strictly ascending order')
-    # Check indexing: vim.buffers[number].number == number
-    cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b))
-    prevnum = b.number
-del prevnum
+  prevnum = 0
+  for b in vim.buffers:
+      # Check buffer order
+      if prevnum >= b.number:
+          cb.append('!!! Buffer numbers not in strictly ascending order')
+      # Check indexing: vim.buffers[number].number == number
+      cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b))
+      prevnum = b.number
+  del prevnum
 
-cb.append(str(len(vim.buffers)))
+  cb.append(str(len(vim.buffers)))
 
-bnums = list(map(lambda b: b.number, vim.buffers))[1:]
+  bnums = list(map(lambda b: b.number, vim.buffers))[1:]
 
-# Test wiping out buffer with existing iterator
-i4 = iter(vim.buffers)
-cb.append('i4:' + str(next(i4)))
-vim.command('bwipeout! ' + str(bnums.pop(0)))
-try:
-    next(i4)
-except vim.error:
-    pass
-else:
-    cb.append('!!!! No vim.error')
-i4 = iter(vim.buffers)
-vim.command('bwipeout! ' + str(bnums.pop(-1)))
-vim.command('bwipeout! ' + str(bnums.pop(-1)))
-cb.append('i4:' + str(next(i4)))
-try:
-    next(i4)
-except StopIteration:
-    cb.append('StopIteration')
-del i4
-del bnums
+  # Test wiping out buffer with existing iterator
+  i4 = iter(vim.buffers)
+  cb.append('i4:' + str(next(i4)))
+  vim.command('bwipeout! ' + str(bnums.pop(0)))
+  try:
+      next(i4)
+  except vim.error:
+      pass
+  else:
+      cb.append('!!!! No vim.error')
+  i4 = iter(vim.buffers)
+  vim.command('bwipeout! ' + str(bnums.pop(-1)))
+  vim.command('bwipeout! ' + str(bnums.pop(-1)))
+  cb.append('i4:' + str(next(i4)))
+  try:
+      next(i4)
+  except StopIteration:
+      cb.append('StopIteration')
+  del i4
+  del bnums
 EOF
 :"
 :" Test vim.{tabpage,window}list and vim.{tabpage,window} objects
@@ -764,127 +764,127 @@ EOF
 :vnew a.2
 :vnew b.2
 :vnew c.2
-py3 << EOF
-cb.append('Number of tabs: ' + str(len(vim.tabpages)))
-cb.append('Current tab pages:')
+py3 << trim EOF
+  cb.append('Number of tabs: ' + str(len(vim.tabpages)))
+  cb.append('Current tab pages:')
 
-def W(w):
-    if '(unknown)' in repr(w):
-        return '<window object (unknown)>'
-    else:
-        return repr(w)
+  def W(w):
+      if '(unknown)' in repr(w):
+          return '<window object (unknown)>'
+      else:
+          return repr(w)
 
-def Cursor(w, start=len(cb)):
-    if w.buffer is cb:
-        return repr((start - w.cursor[0], w.cursor[1]))
-    else:
-        return repr(w.cursor)
+  def Cursor(w, start=len(cb)):
+      if w.buffer is cb:
+          return repr((start - w.cursor[0], w.cursor[1]))
+      else:
+          return repr(w.cursor)
 
-for t in vim.tabpages:
-    cb.append('  ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window))
-    cb.append('  Windows:')
-    for w in t.windows:
-        cb.append('    ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w))
-        # Other values depend on the size of the terminal, so they are checked partly:
-        for attr in ('height', 'row', 'width', 'col'):
-            try:
-                aval = getattr(w, attr)
-                if type(aval) is not int:
-                    raise TypeError
-                if aval < 0:
-                    raise ValueError
-            except Exception as e:
-                cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + e.__class__.__name__)
-        del aval
-        del attr
-        w.cursor = (len(w.buffer), 0)
-del W
-del Cursor
-cb.append('Number of windows in current tab page: ' + str(len(vim.windows)))
-if list(vim.windows) != list(vim.current.tabpage.windows):
-    cb.append('!!!!!! Windows differ')
+  for t in vim.tabpages:
+      cb.append('  ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window))
+      cb.append('  Windows:')
+      for w in t.windows:
+          cb.append('    ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w))
+          # Other values depend on the size of the terminal, so they are checked partly:
+          for attr in ('height', 'row', 'width', 'col'):
+              try:
+                  aval = getattr(w, attr)
+                  if type(aval) is not int:
+                      raise TypeError
+                  if aval < 0:
+                      raise ValueError
+              except Exception as e:
+                  cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + e.__class__.__name__)
+          del aval
+          del attr
+          w.cursor = (len(w.buffer), 0)
+  del W
+  del Cursor
+  cb.append('Number of windows in current tab page: ' + str(len(vim.windows)))
+  if list(vim.windows) != list(vim.current.tabpage.windows):
+      cb.append('!!!!!! Windows differ')
 EOF
 :"
 :" Test vim.current
-py3 << EOF
-def H(o):
-    return repr(o)
-cb.append('Current tab page: ' + repr(vim.current.tabpage))
-cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window))
-cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer))
-del H
-# Assigning: fails
-try:
-    vim.current.window = vim.tabpages[0].window
-except ValueError:
-    cb.append('ValueError at assigning foreign tab window')
+py3 << trim EOF
+  def H(o):
+      return repr(o)
+  cb.append('Current tab page: ' + repr(vim.current.tabpage))
+  cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window))
+  cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer))
+  del H
+  # Assigning: fails
+  try:
+      vim.current.window = vim.tabpages[0].window
+  except ValueError:
+      cb.append('ValueError at assigning foreign tab window')
 
-for attr in ('window', 'tabpage', 'buffer'):
-    try:
-        setattr(vim.current, attr, None)
-    except TypeError:
-        cb.append('Type error at assigning None to vim.current.' + attr)
-del attr
+  for attr in ('window', 'tabpage', 'buffer'):
+      try:
+          setattr(vim.current, attr, None)
+      except TypeError:
+          cb.append('Type error at assigning None to vim.current.' + attr)
+  del attr
 
-# Assigning: success
-vim.current.tabpage = vim.tabpages[-2]
-vim.current.buffer = cb
-vim.current.window = vim.windows[0]
-vim.current.window.cursor = (len(vim.current.buffer), 0)
-cb.append('Current tab page: ' + repr(vim.current.tabpage))
-cb.append('Current window: ' + repr(vim.current.window))
-cb.append('Current buffer: ' + repr(vim.current.buffer))
-cb.append('Current line: ' + repr(vim.current.line))
-ws = list(vim.windows)
-ts = list(vim.tabpages)
-for b in vim.buffers:
-    if b is not cb:
-        vim.command('bwipeout! ' + str(b.number))
-del b
-cb.append('w.valid: ' + repr([w.valid for w in ws]))
-cb.append('t.valid: ' + repr([t.valid for t in ts]))
-del w
-del t
-del ts
-del ws
+  # Assigning: success
+  vim.current.tabpage = vim.tabpages[-2]
+  vim.current.buffer = cb
+  vim.current.window = vim.windows[0]
+  vim.current.window.cursor = (len(vim.current.buffer), 0)
+  cb.append('Current tab page: ' + repr(vim.current.tabpage))
+  cb.append('Current window: ' + repr(vim.current.window))
+  cb.append('Current buffer: ' + repr(vim.current.buffer))
+  cb.append('Current line: ' + repr(vim.current.line))
+  ws = list(vim.windows)
+  ts = list(vim.tabpages)
+  for b in vim.buffers:
+      if b is not cb:
+          vim.command('bwipeout! ' + str(b.number))
+  del b
+  cb.append('w.valid: ' + repr([w.valid for w in ws]))
+  cb.append('t.valid: ' + repr([t.valid for t in ts]))
+  del w
+  del t
+  del ts
+  del ws
 EOF
 :tabonly!
 :only!
 :"
 :" Test types
-py3 << EOF
-for expr, attr in (
-    ('vim.vars',                         'Dictionary'),
-    ('vim.options',                      'Options'),
-    ('vim.bindeval("{}")',               'Dictionary'),
-    ('vim.bindeval("[]")',               'List'),
-    ('vim.bindeval("function(\'tr\')")', 'Function'),
-    ('vim.current.buffer',               'Buffer'),
-    ('vim.current.range',                'Range'),
-    ('vim.current.window',               'Window'),
-    ('vim.current.tabpage',              'TabPage'),
-):
-    cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr)))
-del expr
-del attr
+py3 << trim EOF
+  for expr, attr in (
+      ('vim.vars',                         'Dictionary'),
+      ('vim.options',                      'Options'),
+      ('vim.bindeval("{}")',               'Dictionary'),
+      ('vim.bindeval("[]")',               'List'),
+      ('vim.bindeval("function(\'tr\')")', 'Function'),
+      ('vim.current.buffer',               'Buffer'),
+      ('vim.current.range',                'Range'),
+      ('vim.current.window',               'Window'),
+      ('vim.current.tabpage',              'TabPage'),
+  ):
+      cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr)))
+  del expr
+  del attr
 EOF
 :"
 :" Test __dir__() method
-py3 << EOF
-for name, o in (
-        ('current',    vim.current),
-        ('buffer',     vim.current.buffer),
-        ('window',     vim.current.window),
-        ('tabpage',    vim.current.tabpage),
-        ('range',      vim.current.range),
-        ('dictionary', vim.bindeval('{}')),
-        ('list',       vim.bindeval('[]')),
-        ('function',   vim.bindeval('function("tr")')),
-        ('output',     sys.stdout),
-    ):
-    cb.append(name + ':' + ','.join(dir(o)))
-del name
-del o
+py3 << trim EOF
+  for name, o in (
+          ('current',    vim.current),
+          ('buffer',     vim.current.buffer),
+          ('window',     vim.current.window),
+          ('tabpage',    vim.current.tabpage),
+          ('range',      vim.current.range),
+          ('dictionary', vim.bindeval('{}')),
+          ('list',       vim.bindeval('[]')),
+          ('function',   vim.bindeval('function("tr")')),
+          ('output',     sys.stdout),
+      ):
+      cb.append(name + ':' + ','.join(dir(o)))
+  del name
+  del o
 EOF
 :"
 :" Test vim.*.__new__
@@ -917,63 +917,63 @@ EOF
 :py3 Pt = vim.bindeval('Pt')
 :unlet Pt
 :py3 del Pt
-py3 << EOF
-def ecall(out_prefix, func, *args, **kwargs):
-    line = out_prefix + ': '
-    try:
-        ret = func(*args, **kwargs)
-    except Exception:
-        line += '!exception: ' + emsg(sys.exc_info())
-    else:
-        line += '!result: ' + str(vim.Function('string')(ret), 'utf-8')
-    cb.append(line)
-a = vim.Function('Args')
-pa1 = vim.Function('Args', args=['abcArgsPA1'])
-pa2 = vim.Function('Args', args=[])
-pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'})
-pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'})
-cb.append('a: ' + repr(a))
-cb.append('pa1: ' + repr(pa1))
-cb.append('pa2: ' + repr(pa2))
-cb.append('pa3: ' + repr(pa3))
-cb.append('pa4: ' + repr(pa4))
-sa = vim.Function('SelfArgs')
-psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1'])
-psa2 = vim.Function('SelfArgs', args=[])
-psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'})
-psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'})
-psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0)
-psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=())
-psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[])
-psa8 = vim.Function('SelfArgs', auto_rebind=False)
-psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True)
-psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1)
-psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'})
-psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC'])
-cb.append('sa: ' + repr(sa))
-cb.append('psa1: ' + repr(psa1))
-cb.append('psa2: ' + repr(psa2))
-cb.append('psa3: ' + repr(psa3))
-cb.append('psa4: ' + repr(psa4))
-cb.append('psa5: ' + repr(psa5))
-cb.append('psa6: ' + repr(psa6))
-cb.append('psa7: ' + repr(psa7))
-cb.append('psa8: ' + repr(psa8))
-cb.append('psa9: ' + repr(psa9))
-cb.append('psaA: ' + repr(psaA))
-cb.append('psaB: ' + repr(psaB))
-cb.append('psaC: ' + repr(psaC))
+py3 << trim EOF
+  def ecall(out_prefix, func, *args, **kwargs):
+      line = out_prefix + ': '
+      try:
+          ret = func(*args, **kwargs)
+      except Exception:
+          line += '!exception: ' + emsg(sys.exc_info())
+      else:
+          line += '!result: ' + str(vim.Function('string')(ret), 'utf-8')
+      cb.append(line)
+  a = vim.Function('Args')
+  pa1 = vim.Function('Args', args=['abcArgsPA1'])
+  pa2 = vim.Function('Args', args=[])
+  pa3 = vim.Function('Args', args=['abcArgsPA3'], self={'abcSelfPA3': 'abcSelfPA3Val'})
+  pa4 = vim.Function('Args', self={'abcSelfPA4': 'abcSelfPA4Val'})
+  cb.append('a: ' + repr(a))
+  cb.append('pa1: ' + repr(pa1))
+  cb.append('pa2: ' + repr(pa2))
+  cb.append('pa3: ' + repr(pa3))
+  cb.append('pa4: ' + repr(pa4))
+  sa = vim.Function('SelfArgs')
+  psa1 = vim.Function('SelfArgs', args=['abcArgsPSA1'])
+  psa2 = vim.Function('SelfArgs', args=[])
+  psa3 = vim.Function('SelfArgs', args=['abcArgsPSA3'], self={'abcSelfPSA3': 'abcSelfPSA3Val'})
+  psa4 = vim.Function('SelfArgs', self={'abcSelfPSA4': 'abcSelfPSA4Val'})
+  psa5 = vim.Function('SelfArgs', self={'abcSelfPSA5': 'abcSelfPSA5Val'}, auto_rebind=0)
+  psa6 = vim.Function('SelfArgs', args=['abcArgsPSA6'], self={'abcSelfPSA6': 'abcSelfPSA6Val'}, auto_rebind=())
+  psa7 = vim.Function('SelfArgs', args=['abcArgsPSA7'], auto_rebind=[])
+  psa8 = vim.Function('SelfArgs', auto_rebind=False)
+  psa9 = vim.Function('SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_rebind=True)
+  psaA = vim.Function('SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=1)
+  psaB = vim.Function('SelfArgs', args=['abcArgsPSAB'], auto_rebind={'abcARPSAB': 'abcARPSABVal'})
+  psaC = vim.Function('SelfArgs', auto_rebind=['abcARPSAC'])
+  cb.append('sa: ' + repr(sa))
+  cb.append('psa1: ' + repr(psa1))
+  cb.append('psa2: ' + repr(psa2))
+  cb.append('psa3: ' + repr(psa3))
+  cb.append('psa4: ' + repr(psa4))
+  cb.append('psa5: ' + repr(psa5))
+  cb.append('psa6: ' + repr(psa6))
+  cb.append('psa7: ' + repr(psa7))
+  cb.append('psa8: ' + repr(psa8))
+  cb.append('psa9: ' + repr(psa9))
+  cb.append('psaA: ' + repr(psaA))
+  cb.append('psaB: ' + repr(psaB))
+  cb.append('psaC: ' + repr(psaC))
 
-psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'})
-psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]]
-psar.self['rec'] = psar
-psar.self['self'] = psar.self
-psar.self['args'] = psar.args
+  psar = vim.Function('SelfArgs', args=[{'abcArgsPSAr': 'abcArgsPSArVal'}], self={'abcSelfPSAr': 'abcSelfPSArVal'})
+  psar.args[0]['abcArgsPSAr2'] = [psar.self, psar.args[0]]
+  psar.self['rec'] = psar
+  psar.self['self'] = psar.self
+  psar.self['args'] = psar.args
 
-try:
-    cb.append('psar: ' + repr(psar))
-except Exception:
-    cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+  try:
+      cb.append('psar: ' + repr(psar))
+  except Exception:
+      cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
 EOF
 :$put ='s(a): '.string(py3eval('a'))
 :$put ='s(pa1): '.string(py3eval('pa1'))
@@ -1042,88 +1042,88 @@ EOF
 :py3 ecall('psa2(self={"20": 1})', psa2, self={'20': 1})
 :py3 ecall('psa3(self={"20": 1})', psa3, self={'20': 1})
 :py3 ecall('psa4(self={"20": 1})', psa4, self={'20': 1})
-py3 << EOF
-def s(v):
-    if v is None:
-        return repr(v)
-    else:
-        return str(vim.Function('string')(v), 'utf-8')
+py3 << trim EOF
+  def s(v):
+      if v is None:
+          return repr(v)
+      else:
+          return str(vim.Function('string')(v), 'utf-8')
 
-cb.append('a.args: ' + s(a.args))
-cb.append('pa1.args: ' + s(pa1.args))
-cb.append('pa2.args: ' + s(pa2.args))
-cb.append('pa3.args: ' + s(pa3.args))
-cb.append('pa4.args: ' + s(pa4.args))
-cb.append('sa.args: ' + s(sa.args))
-cb.append('psa1.args: ' + s(psa1.args))
-cb.append('psa2.args: ' + s(psa2.args))
-cb.append('psa3.args: ' + s(psa3.args))
-cb.append('psa4.args: ' + s(psa4.args))
+  cb.append('a.args: ' + s(a.args))
+  cb.append('pa1.args: ' + s(pa1.args))
+  cb.append('pa2.args: ' + s(pa2.args))
+  cb.append('pa3.args: ' + s(pa3.args))
+  cb.append('pa4.args: ' + s(pa4.args))
+  cb.append('sa.args: ' + s(sa.args))
+  cb.append('psa1.args: ' + s(psa1.args))
+  cb.append('psa2.args: ' + s(psa2.args))
+  cb.append('psa3.args: ' + s(psa3.args))
+  cb.append('psa4.args: ' + s(psa4.args))
 
-cb.append('a.self: ' + s(a.self))
-cb.append('pa1.self: ' + s(pa1.self))
-cb.append('pa2.self: ' + s(pa2.self))
-cb.append('pa3.self: ' + s(pa3.self))
-cb.append('pa4.self: ' + s(pa4.self))
-cb.append('sa.self: ' + s(sa.self))
-cb.append('psa1.self: ' + s(psa1.self))
-cb.append('psa2.self: ' + s(psa2.self))
-cb.append('psa3.self: ' + s(psa3.self))
-cb.append('psa4.self: ' + s(psa4.self))
+  cb.append('a.self: ' + s(a.self))
+  cb.append('pa1.self: ' + s(pa1.self))
+  cb.append('pa2.self: ' + s(pa2.self))
+  cb.append('pa3.self: ' + s(pa3.self))
+  cb.append('pa4.self: ' + s(pa4.self))
+  cb.append('sa.self: ' + s(sa.self))
+  cb.append('psa1.self: ' + s(psa1.self))
+  cb.append('psa2.self: ' + s(psa2.self))
+  cb.append('psa3.self: ' + s(psa3.self))
+  cb.append('psa4.self: ' + s(psa4.self))
 
-cb.append('a.name: ' + s(a.name))
-cb.append('pa1.name: ' + s(pa1.name))
-cb.append('pa2.name: ' + s(pa2.name))
-cb.append('pa3.name: ' + s(pa3.name))
-cb.append('pa4.name: ' + s(pa4.name))
-cb.append('sa.name: ' + s(sa.name))
-cb.append('psa1.name: ' + s(psa1.name))
-cb.append('psa2.name: ' + s(psa2.name))
-cb.append('psa3.name: ' + s(psa3.name))
-cb.append('psa4.name: ' + s(psa4.name))
+  cb.append('a.name: ' + s(a.name))
+  cb.append('pa1.name: ' + s(pa1.name))
+  cb.append('pa2.name: ' + s(pa2.name))
+  cb.append('pa3.name: ' + s(pa3.name))
+  cb.append('pa4.name: ' + s(pa4.name))
+  cb.append('sa.name: ' + s(sa.name))
+  cb.append('psa1.name: ' + s(psa1.name))
+  cb.append('psa2.name: ' + s(psa2.name))
+  cb.append('psa3.name: ' + s(psa3.name))
+  cb.append('psa4.name: ' + s(psa4.name))
 
-cb.append('a.auto_rebind: ' + s(a.auto_rebind))
-cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind))
-cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind))
-cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind))
-cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind))
-cb.append('sa.auto_rebind: ' + s(sa.auto_rebind))
-cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind))
-cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind))
-cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind))
-cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind))
-cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind))
-cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind))
-cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind))
-cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind))
-cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind))
-cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind))
-cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind))
-cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind))
+  cb.append('a.auto_rebind: ' + s(a.auto_rebind))
+  cb.append('pa1.auto_rebind: ' + s(pa1.auto_rebind))
+  cb.append('pa2.auto_rebind: ' + s(pa2.auto_rebind))
+  cb.append('pa3.auto_rebind: ' + s(pa3.auto_rebind))
+  cb.append('pa4.auto_rebind: ' + s(pa4.auto_rebind))
+  cb.append('sa.auto_rebind: ' + s(sa.auto_rebind))
+  cb.append('psa1.auto_rebind: ' + s(psa1.auto_rebind))
+  cb.append('psa2.auto_rebind: ' + s(psa2.auto_rebind))
+  cb.append('psa3.auto_rebind: ' + s(psa3.auto_rebind))
+  cb.append('psa4.auto_rebind: ' + s(psa4.auto_rebind))
+  cb.append('psa5.auto_rebind: ' + s(psa5.auto_rebind))
+  cb.append('psa6.auto_rebind: ' + s(psa6.auto_rebind))
+  cb.append('psa7.auto_rebind: ' + s(psa7.auto_rebind))
+  cb.append('psa8.auto_rebind: ' + s(psa8.auto_rebind))
+  cb.append('psa9.auto_rebind: ' + s(psa9.auto_rebind))
+  cb.append('psaA.auto_rebind: ' + s(psaA.auto_rebind))
+  cb.append('psaB.auto_rebind: ' + s(psaB.auto_rebind))
+  cb.append('psaC.auto_rebind: ' + s(psaC.auto_rebind))
 
-del s
+  del s
 
-del a
-del pa1
-del pa2
-del pa3
-del pa4
-del sa
-del psa1
-del psa2
-del psa3
-del psa4
-del psa5
-del psa6
-del psa7
-del psa8
-del psa9
-del psaA
-del psaB
-del psaC
-del psar
+  del a
+  del pa1
+  del pa2
+  del pa3
+  del pa4
+  del sa
+  del psa1
+  del psa2
+  del psa3
+  del psa4
+  del psa5
+  del psa6
+  del psa7
+  del psa8
+  del psa9
+  del psaA
+  del psaB
+  del psaC
+  del psar
 
-del ecall
+  del ecall
 EOF
 :"
 :" Test stdout/stderr
@@ -1139,27 +1139,27 @@ EOF
 :   $put =string(a:000)
 :   return a:000
 :endfun
-py3 << EOF
-class DupDict(vim.Dictionary):
-    def __setitem__(self, key, value):
-        super(DupDict, self).__setitem__(key, value)
-        super(DupDict, self).__setitem__('dup_' + key, value)
-dd = DupDict()
-dd['a'] = 'b'
+py3 << trim EOF
+  class DupDict(vim.Dictionary):
+      def __setitem__(self, key, value):
+          super(DupDict, self).__setitem__(key, value)
+          super(DupDict, self).__setitem__('dup_' + key, value)
+  dd = DupDict()
+  dd['a'] = 'b'
 
-class DupList(vim.List):
-    def __getitem__(self, idx):
-        return [super(DupList, self).__getitem__(idx)] * 2
+  class DupList(vim.List):
+      def __getitem__(self, idx):
+          return [super(DupList, self).__getitem__(idx)] * 2
 
-dl = DupList()
-dl2 = DupList(iter('abcC'))
-dl.extend(dl2[0])
+  dl = DupList()
+  dl2 = DupList(iter('abcC'))
+  dl.extend(dl2[0])
 
-class DupFun(vim.Function):
-    def __call__(self, arg):
-        return super(DupFun, self).__call__(arg, arg)
+  class DupFun(vim.Function):
+      def __call__(self, arg):
+          return super(DupFun, self).__call__(arg, arg)
 
-df = DupFun('Put')
+  df = DupFun('Put')
 EOF
 :$put =string(sort(keys(py3eval('dd'))))
 :$put =string(py3eval('dl'))
@@ -1169,40 +1169,40 @@ EOF
 :$put =string(py3eval('dd') is# py3eval('dd'))
 :$put =string(py3eval('df'))
 :delfunction Put
-py3 << EOF
-del DupDict
-del DupList
-del DupFun
-del dd
-del dl
-del dl2
-del df
+py3 << trim EOF
+  del DupDict
+  del DupList
+  del DupFun
+  del dd
+  del dl
+  del dl2
+  del df
 EOF
 :"
 :" Test chdir
-py3 << EOF
-import os
-fnamemodify = vim.Function('fnamemodify')
-cb.append(str(fnamemodify('.', ':p:h:t')))
-cb.append(vim.eval('@%'))
-os.chdir('..')
-path = fnamemodify('.', ':p:h:t')
-if path != b'src':
-  # Running tests from a shadow directory, so move up another level
-  # This will result in @% looking like shadow/testdir/test87.in, hence the
-  # slicing to remove the leading path and path separator
+py3 << trim EOF
+  import os
+  fnamemodify = vim.Function('fnamemodify')
+  cb.append(str(fnamemodify('.', ':p:h:t')))
+  cb.append(vim.eval('@%'))
   os.chdir('..')
-  cb.append(str(fnamemodify('.', ':p:h:t')))
-  cb.append(vim.eval('@%')[len(path)+1:].replace(os.path.sep, '/'))
-  os.chdir(path)
-else:
+  path = fnamemodify('.', ':p:h:t')
+  if path != b'src':
+    # Running tests from a shadow directory, so move up another level
+    # This will result in @% looking like shadow/testdir/test87.in, hence the
+    # slicing to remove the leading path and path separator
+    os.chdir('..')
+    cb.append(str(fnamemodify('.', ':p:h:t')))
+    cb.append(vim.eval('@%')[len(path)+1:].replace(os.path.sep, '/'))
+    os.chdir(path)
+  else:
+    cb.append(str(fnamemodify('.', ':p:h:t')))
+    cb.append(vim.eval('@%').replace(os.path.sep, '/'))
+  del path
+  os.chdir('testdir')
   cb.append(str(fnamemodify('.', ':p:h:t')))
-  cb.append(vim.eval('@%').replace(os.path.sep, '/'))
-del path
-os.chdir('testdir')
-cb.append(str(fnamemodify('.', ':p:h:t')))
-cb.append(vim.eval('@%'))
-del fnamemodify
+  cb.append(vim.eval('@%'))
+  del fnamemodify
 EOF
 :"
 :" Test errors
@@ -1210,464 +1210,464 @@ EOF
 :endfun
 :fun D()
 :endfun
-py3 << EOF
-d = vim.Dictionary()
-ned = vim.Dictionary(foo='bar', baz='abcD')
-dl = vim.Dictionary(a=1)
-dl.locked = True
-l = vim.List()
-ll = vim.List('abcE')
-ll.locked = True
-nel = vim.List('abcO')
-f = vim.Function('string')
-fd = vim.Function('F')
-fdel = vim.Function('D')
-vim.command('delfunction D')
+py3 << trim EOF
+  d = vim.Dictionary()
+  ned = vim.Dictionary(foo='bar', baz='abcD')
+  dl = vim.Dictionary(a=1)
+  dl.locked = True
+  l = vim.List()
+  ll = vim.List('abcE')
+  ll.locked = True
+  nel = vim.List('abcO')
+  f = vim.Function('string')
+  fd = vim.Function('F')
+  fdel = vim.Function('D')
+  vim.command('delfunction D')
 
-def subexpr_test(expr, name, subexprs):
-    cb.append('>>> Testing %s using %s' % (name, expr))
-    for subexpr in subexprs:
-        ee(expr % subexpr)
-    cb.append('<<< Finished')
+  def subexpr_test(expr, name, subexprs):
+      cb.append('>>> Testing %s using %s' % (name, expr))
+      for subexpr in subexprs:
+          ee(expr % subexpr)
+      cb.append('<<< Finished')
 
-def stringtochars_test(expr):
-    return subexpr_test(expr, 'StringToChars', (
-        '1',       # Fail type checks
-        'b"\\0"',  # Fail PyString_AsStringAndSize(object, , NULL) check
-        '"\\0"',   # Fail PyString_AsStringAndSize(bytes, , NULL) check
-    ))
+  def stringtochars_test(expr):
+      return subexpr_test(expr, 'StringToChars', (
+          '1',       # Fail type checks
+          'b"\\0"',  # Fail PyString_AsStringAndSize(object, , NULL) check
+          '"\\0"',   # Fail PyString_AsStringAndSize(bytes, , NULL) check
+      ))
 
-class Mapping(object):
-    def __init__(self, d):
-        self.d = d
+  class Mapping(object):
+      def __init__(self, d):
+          self.d = d
 
-    def __getitem__(self, key):
-        return self.d[key]
+      def __getitem__(self, key):
+          return self.d[key]
 
-    def keys(self):
-        return self.d.keys()
+      def keys(self):
+          return self.d.keys()
 
-    def items(self):
-        return self.d.items()
+      def items(self):
+          return self.d.items()
 
-def convertfrompyobject_test(expr, recurse=True):
-    # pydict_to_tv
-    stringtochars_test(expr % '{%s : 1}')
-    if recurse:
-        convertfrompyobject_test(expr % '{"abcF" : %s}', False)
-    # pymap_to_tv
-    stringtochars_test(expr % 'Mapping({%s : 1})')
-    if recurse:
-        convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False)
-    # pyseq_to_tv
-    iter_test(expr)
-    return subexpr_test(expr, 'ConvertFromPyObject', (
-        'None',                 # Not conversible
-        '{b"": 1}',             # Empty key not allowed
-        '{"": 1}',              # Same, but with unicode object
-        'FailingMapping()',     #
-        'FailingMappingKey()',  #
-        'FailingNumber()',      #
-    ))
+  def convertfrompyobject_test(expr, recurse=True):
+      # pydict_to_tv
+      stringtochars_test(expr % '{%s : 1}')
+      if recurse:
+          convertfrompyobject_test(expr % '{"abcF" : %s}', False)
+      # pymap_to_tv
+      stringtochars_test(expr % 'Mapping({%s : 1})')
+      if recurse:
+          convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False)
+      # pyseq_to_tv
+      iter_test(expr)
+      return subexpr_test(expr, 'ConvertFromPyObject', (
+          'None',                 # Not conversible
+          '{b"": 1}',             # Empty key not allowed
+          '{"": 1}',              # Same, but with unicode object
+          'FailingMapping()',     #
+          'FailingMappingKey()',  #
+          'FailingNumber()',      #
+      ))
 
-def convertfrompymapping_test(expr):
-    convertfrompyobject_test(expr)
-    return subexpr_test(expr, 'ConvertFromPyMapping', (
-        '[]',
-    ))
+  def convertfrompymapping_test(expr):
+      convertfrompyobject_test(expr)
+      return subexpr_test(expr, 'ConvertFromPyMapping', (
+          '[]',
+      ))
 
-def iter_test(expr):
-    return subexpr_test(expr, '*Iter*', (
-        'FailingIter()',
-        'FailingIterNext()',
-    ))
+  def iter_test(expr):
+      return subexpr_test(expr, '*Iter*', (
+          'FailingIter()',
+          'FailingIterNext()',
+      ))
 
-def number_test(expr, natural=False, unsigned=False):
-    if natural:
-        unsigned = True
-    return subexpr_test(expr, 'NumberToLong', (
-        '[]',
-        'None',
-    ) + (('-1',) if unsigned else ())
-    + (('0',) if natural else ()))
+  def number_test(expr, natural=False, unsigned=False):
+      if natural:
+          unsigned = True
+      return subexpr_test(expr, 'NumberToLong', (
+          '[]',
+          'None',
+      ) + (('-1',) if unsigned else ())
+      + (('0',) if natural else ()))
 
-class FailingTrue(object):
-    def __bool__(self):
-        raise NotImplementedError('bool')
+  class FailingTrue(object):
+      def __bool__(self):
+          raise NotImplementedError('bool')
 
-class FailingIter(object):
-    def __iter__(self):
-        raise NotImplementedError('iter')
+  class FailingIter(object):
+      def __iter__(self):
+          raise NotImplementedError('iter')
 
-class FailingIterNext(object):
-    def __iter__(self):
-        return self
+  class FailingIterNext(object):
+      def __iter__(self):
+          return self
 
-    def __next__(self):
-        raise NotImplementedError('next')
+      def __next__(self):
+          raise NotImplementedError('next')
 
-class FailingIterNextN(object):
-    def __init__(self, n):
-        self.n = n
+  class FailingIterNextN(object):
+      def __init__(self, n):
+          self.n = n
 
-    def __iter__(self):
-        return self
+      def __iter__(self):
+          return self
 
-    def __next__(self):
-        if self.n:
-            self.n -= 1
-            return 1
-        else:
-            raise NotImplementedError('next N')
+      def __next__(self):
+          if self.n:
+              self.n -= 1
+              return 1
+          else:
+              raise NotImplementedError('next N')
 
-class FailingMappingKey(object):
-    def __getitem__(self, item):
-        raise NotImplementedError('getitem:mappingkey')
+  class FailingMappingKey(object):
+      def __getitem__(self, item):
+          raise NotImplementedError('getitem:mappingkey')
 
-    def keys(self):
-        return list("abcH")
+      def keys(self):
+          return list("abcH")
 
-class FailingMapping(object):
-    def __getitem__(self):
-        raise NotImplementedError('getitem:mapping')
+  class FailingMapping(object):
+      def __getitem__(self):
+          raise NotImplementedError('getitem:mapping')
 
-    def keys(self):
-        raise NotImplementedError('keys')
+      def keys(self):
+          raise NotImplementedError('keys')
 
-class FailingList(list):
-    def __getitem__(self, idx):
-        if i == 2:
-            raise NotImplementedError('getitem:list')
-        else:
-            return super(FailingList, self).__getitem__(idx)
+  class FailingList(list):
+      def __getitem__(self, idx):
+          if i == 2:
+              raise NotImplementedError('getitem:list')
+          else:
+              return super(FailingList, self).__getitem__(idx)
 
-class NoArgsCall(object):
-    def __call__(self):
-        pass
+  class NoArgsCall(object):
+      def __call__(self):
+          pass
 
-class FailingCall(object):
-    def __call__(self, path):
-        raise NotImplementedError('call')
+  class FailingCall(object):
+      def __call__(self, path):
+          raise NotImplementedError('call')
 
-class FailingNumber(object):
-    def __int__(self):
-        raise NotImplementedError('int')
+  class FailingNumber(object):
+      def __int__(self):
+          raise NotImplementedError('int')
 
-cb.append("> Output")
-cb.append(">> OutputSetattr")
-ee('del sys.stdout.softspace')
-number_test('sys.stdout.softspace = %s', unsigned=True)
-number_test('sys.stderr.softspace = %s', unsigned=True)
-ee('assert sys.stdout.isatty()==False')
-ee('assert sys.stdout.seekable()==False')
-ee('sys.stdout.close()')
-ee('sys.stdout.flush()')
-ee('assert sys.stderr.isatty()==False')
-ee('assert sys.stderr.seekable()==False')
-ee('sys.stderr.close()')
-ee('sys.stderr.flush()')
-ee('sys.stdout.attr = None')
-cb.append(">> OutputWrite")
-ee('assert sys.stdout.writable()==True')
-ee('assert sys.stdout.readable()==False')
-ee('assert sys.stderr.writable()==True')
-ee('assert sys.stderr.readable()==False')
-ee('assert sys.stdout.closed()==False')
-ee('assert sys.stderr.closed()==False')
-ee('assert sys.stdout.errors=="strict"')
-ee('assert sys.stderr.errors=="strict"')
-ee('assert sys.stdout.encoding==sys.stderr.encoding')
-ee('sys.stdout.write(None)')
-cb.append(">> OutputWriteLines")
-ee('sys.stdout.writelines(None)')
-ee('sys.stdout.writelines([1])')
-iter_test('sys.stdout.writelines(%s)')
-cb.append("> VimCommand")
-stringtochars_test('vim.command(%s)')
-ee('vim.command("", 2)')
-#! Not checked: vim->python exceptions translating: checked later
-cb.append("> VimToPython")
-#! Not checked: everything: needs errors in internal python functions
-cb.append("> VimEval")
-stringtochars_test('vim.eval(%s)')
-ee('vim.eval("", FailingTrue())')
-#! Not checked: everything: needs errors in internal python functions
-cb.append("> VimEvalPy")
-stringtochars_test('vim.bindeval(%s)')
-ee('vim.eval("", 2)')
-#! Not checked: vim->python exceptions translating: checked later
-cb.append("> VimStrwidth")
-stringtochars_test('vim.strwidth(%s)')
-cb.append("> VimForeachRTP")
-ee('vim.foreach_rtp(None)')
-ee('vim.foreach_rtp(NoArgsCall())')
-ee('vim.foreach_rtp(FailingCall())')
-ee('vim.foreach_rtp(int, 2)')
-cb.append('> import')
-old_rtp = vim.options['rtp']
-vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,')
-ee('import xxx_no_such_module_xxx')
-ee('import failing_import')
-ee('import failing')
-vim.options['rtp'] = old_rtp
-del old_rtp
-cb.append("> Options")
-cb.append(">> OptionsItem")
-ee('vim.options["abcQ"]')
-ee('vim.options[""]')
-stringtochars_test('vim.options[%s]')
-cb.append(">> OptionsContains")
-stringtochars_test('%s in vim.options')
-cb.append("> Dictionary")
-cb.append(">> DictionaryConstructor")
-ee('vim.Dictionary("abcI")')
-##! Not checked: py_dict_alloc failure
-cb.append(">> DictionarySetattr")
-ee('del d.locked')
-ee('d.locked = FailingTrue()')
-ee('vim.vvars.locked = False')
-ee('d.scope = True')
-ee('d.xxx = True')
-cb.append(">> _DictionaryItem")
-ee('d.get("a", 2, 3)')
-stringtochars_test('d.get(%s)')
-ee('d.pop("a")')
-ee('dl.pop("a")')
-cb.append(">> DictionaryContains")
-ee('"" in d')
-ee('0 in d')
-cb.append(">> DictionaryIterNext")
-ee('for i in ned: ned["a"] = 1')
-del i
-cb.append(">> DictionaryAssItem")
-ee('dl["b"] = 1')
-stringtochars_test('d[%s] = 1')
-convertfrompyobject_test('d["a"] = %s')
-cb.append(">> DictionaryUpdate")
-cb.append(">>> kwargs")
-cb.append(">>> iter")
-ee('d.update(FailingMapping())')
-ee('d.update([FailingIterNext()])')
-ee('d.update([FailingIterNextN(1)])')
-iter_test('d.update(%s)')
-convertfrompyobject_test('d.update(%s)')
-stringtochars_test('d.update(((%s, 0),))')
-convertfrompyobject_test('d.update((("a", %s),))')
-cb.append(">> DictionaryPopItem")
-ee('d.popitem(1, 2)')
-cb.append(">> DictionaryHasKey")
-ee('d.has_key()')
-cb.append("> List")
-cb.append(">> ListConstructor")
-ee('vim.List(1, 2)')
-ee('vim.List(a=1)')
-iter_test('vim.List(%s)')
-convertfrompyobject_test('vim.List([%s])')
-cb.append(">> ListItem")
-ee('l[1000]')
-cb.append(">> ListAssItem")
-ee('ll[1] = 2')
-ee('l[1000] = 3')
-cb.append(">> ListAssSlice")
-ee('ll[1:100] = "abcJ"')
-iter_test('l[:] = %s')
-ee('nel[1:10:2]  = "abcK"')
-cb.append(repr(tuple(nel)))
-ee('nel[1:10:2]  = "a"')
-cb.append(repr(tuple(nel)))
-ee('nel[1:1:-1]  = "a"')
-cb.append(repr(tuple(nel)))
-ee('nel[:] = FailingIterNextN(2)')
-cb.append(repr(tuple(nel)))
-convertfrompyobject_test('l[:] = [%s]')
-cb.append(">> ListConcatInPlace")
-iter_test('l.extend(%s)')
-convertfrompyobject_test('l.extend([%s])')
-cb.append(">> ListSetattr")
-ee('del l.locked')
-ee('l.locked = FailingTrue()')
-ee('l.xxx = True')
-cb.append("> Function")
-cb.append(">> FunctionConstructor")
-cb.append(">>> FunctionConstructor")
-ee('vim.Function("123")')
-ee('vim.Function("xxx_non_existent_function_xxx")')
-ee('vim.Function("xxx#non#existent#function#xxx")')
-ee('vim.Function("xxx_non_existent_function_xxx2", args=[])')
-ee('vim.Function("xxx_non_existent_function_xxx3", self={})')
-ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})')
-cb.append(">>> FunctionNew")
-ee('vim.Function("tr", self="abcFuncSelf")')
-ee('vim.Function("tr", args=427423)')
-ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")')
-ee('vim.Function("tr", "")')
-cb.append(">> FunctionCall")
-convertfrompyobject_test('f(%s)')
-convertfrompymapping_test('fd(self=%s)')
-cb.append("> TabPage")
-cb.append(">> TabPageAttr")
-ee('vim.current.tabpage.xxx')
-cb.append("> TabList")
-cb.append(">> TabListItem")
-ee('vim.tabpages[1000]')
-cb.append("> Window")
-cb.append(">> WindowAttr")
-ee('vim.current.window.xxx')
-cb.append(">> WindowSetattr")
-ee('vim.current.window.buffer = 0')
-ee('vim.current.window.cursor = (100000000, 100000000)')
-ee('vim.current.window.cursor = True')
-number_test('vim.current.window.height = %s', unsigned=True)
-number_test('vim.current.window.width = %s', unsigned=True)
-ee('vim.current.window.xxxxxx = True')
-cb.append("> WinList")
-cb.append(">> WinListItem")
-ee('vim.windows[1000]')
-cb.append("> Buffer")
-cb.append(">> StringToLine (indirect)")
-ee('vim.current.buffer[0] = "\\na"')
-ee('vim.current.buffer[0] = b"\\na"')
-cb.append(">> SetBufferLine (indirect)")
-ee('vim.current.buffer[0] = True')
-cb.append(">> SetBufferLineList (indirect)")
-ee('vim.current.buffer[:] = True')
-ee('vim.current.buffer[:] = ["\\na", "bc"]')
-cb.append(">> InsertBufferLines (indirect)")
-ee('vim.current.buffer.append(None)')
-ee('vim.current.buffer.append(["\\na", "bc"])')
-ee('vim.current.buffer.append("\\nbc")')
-cb.append(">> RBItem")
-ee('vim.current.buffer[100000000]')
-cb.append(">> RBAsItem")
-ee('vim.current.buffer[100000000] = ""')
-cb.append(">> BufferAttr")
-ee('vim.current.buffer.xxx')
-cb.append(">> BufferSetattr")
-ee('vim.current.buffer.name = True')
-ee('vim.current.buffer.xxx = True')
-cb.append(">> BufferMark")
-ee('vim.current.buffer.mark(0)')
-ee('vim.current.buffer.mark("abcM")')
-ee('vim.current.buffer.mark("!")')
-cb.append(">> BufferRange")
-ee('vim.current.buffer.range(1, 2, 3)')
-cb.append("> BufMap")
-cb.append(">> BufMapItem")
-ee('vim.buffers[100000000]')
-number_test('vim.buffers[%s]', natural=True)
-cb.append("> Current")
-cb.append(">> CurrentGetattr")
-ee('vim.current.xxx')
-cb.append(">> CurrentSetattr")
-ee('vim.current.line = True')
-ee('vim.current.buffer = True')
-ee('vim.current.window = True')
-ee('vim.current.tabpage = True')
-ee('vim.current.xxx = True')
-del d
-del ned
-del dl
-del l
-del ll
-del nel
-del f
-del fd
-del fdel
-del subexpr_test
-del stringtochars_test
-del Mapping
-del convertfrompyobject_test
-del convertfrompymapping_test
-del iter_test
-del number_test
-del FailingTrue
-del FailingIter
-del FailingIterNext
-del FailingIterNextN
-del FailingMapping
-del FailingMappingKey
-del FailingList
-del NoArgsCall
-del FailingCall
-del FailingNumber
+  cb.append("> Output")
+  cb.append(">> OutputSetattr")
+  ee('del sys.stdout.softspace')
+  number_test('sys.stdout.softspace = %s', unsigned=True)
+  number_test('sys.stderr.softspace = %s', unsigned=True)
+  ee('assert sys.stdout.isatty()==False')
+  ee('assert sys.stdout.seekable()==False')
+  ee('sys.stdout.close()')
+  ee('sys.stdout.flush()')
+  ee('assert sys.stderr.isatty()==False')
+  ee('assert sys.stderr.seekable()==False')
+  ee('sys.stderr.close()')
+  ee('sys.stderr.flush()')
+  ee('sys.stdout.attr = None')
+  cb.append(">> OutputWrite")
+  ee('assert sys.stdout.writable()==True')
+  ee('assert sys.stdout.readable()==False')
+  ee('assert sys.stderr.writable()==True')
+  ee('assert sys.stderr.readable()==False')
+  ee('assert sys.stdout.closed()==False')
+  ee('assert sys.stderr.closed()==False')
+  ee('assert sys.stdout.errors=="strict"')
+  ee('assert sys.stderr.errors=="strict"')
+  ee('assert sys.stdout.encoding==sys.stderr.encoding')
+  ee('sys.stdout.write(None)')
+  cb.append(">> OutputWriteLines")
+  ee('sys.stdout.writelines(None)')
+  ee('sys.stdout.writelines([1])')
+  iter_test('sys.stdout.writelines(%s)')
+  cb.append("> VimCommand")
+  stringtochars_test('vim.command(%s)')
+  ee('vim.command("", 2)')
+  #! Not checked: vim->python exceptions translating: checked later
+  cb.append("> VimToPython")
+  #! Not checked: everything: needs errors in internal python functions
+  cb.append("> VimEval")
+  stringtochars_test('vim.eval(%s)')
+  ee('vim.eval("", FailingTrue())')
+  #! Not checked: everything: needs errors in internal python functions
+  cb.append("> VimEvalPy")
+  stringtochars_test('vim.bindeval(%s)')
+  ee('vim.eval("", 2)')
+  #! Not checked: vim->python exceptions translating: checked later
+  cb.append("> VimStrwidth")
+  stringtochars_test('vim.strwidth(%s)')
+  cb.append("> VimForeachRTP")
+  ee('vim.foreach_rtp(None)')
+  ee('vim.foreach_rtp(NoArgsCall())')
+  ee('vim.foreach_rtp(FailingCall())')
+  ee('vim.foreach_rtp(int, 2)')
+  cb.append('> import')
+  old_rtp = vim.options['rtp']
+  vim.options['rtp'] = os.getcwd().replace('\\', '\\\\').replace(',', '\\,')
+  ee('import xxx_no_such_module_xxx')
+  ee('import failing_import')
+  ee('import failing')
+  vim.options['rtp'] = old_rtp
+  del old_rtp
+  cb.append("> Options")
+  cb.append(">> OptionsItem")
+  ee('vim.options["abcQ"]')
+  ee('vim.options[""]')
+  stringtochars_test('vim.options[%s]')
+  cb.append(">> OptionsContains")
+  stringtochars_test('%s in vim.options')
+  cb.append("> Dictionary")
+  cb.append(">> DictionaryConstructor")
+  ee('vim.Dictionary("abcI")')
+  ##! Not checked: py_dict_alloc failure
+  cb.append(">> DictionarySetattr")
+  ee('del d.locked')
+  ee('d.locked = FailingTrue()')
+  ee('vim.vvars.locked = False')
+  ee('d.scope = True')
+  ee('d.xxx = True')
+  cb.append(">> _DictionaryItem")
+  ee('d.get("a", 2, 3)')
+  stringtochars_test('d.get(%s)')
+  ee('d.pop("a")')
+  ee('dl.pop("a")')
+  cb.append(">> DictionaryContains")
+  ee('"" in d')
+  ee('0 in d')
+  cb.append(">> DictionaryIterNext")
+  ee('for i in ned: ned["a"] = 1')
+  del i
+  cb.append(">> DictionaryAssItem")
+  ee('dl["b"] = 1')
+  stringtochars_test('d[%s] = 1')
+  convertfrompyobject_test('d["a"] = %s')
+  cb.append(">> DictionaryUpdate")
+  cb.append(">>> kwargs")
+  cb.append(">>> iter")
+  ee('d.update(FailingMapping())')
+  ee('d.update([FailingIterNext()])')
+  ee('d.update([FailingIterNextN(1)])')
+  iter_test('d.update(%s)')
+  convertfrompyobject_test('d.update(%s)')
+  stringtochars_test('d.update(((%s, 0),))')
+  convertfrompyobject_test('d.update((("a", %s),))')
+  cb.append(">> DictionaryPopItem")
+  ee('d.popitem(1, 2)')
+  cb.append(">> DictionaryHasKey")
+  ee('d.has_key()')
+  cb.append("> List")
+  cb.append(">> ListConstructor")
+  ee('vim.List(1, 2)')
+  ee('vim.List(a=1)')
+  iter_test('vim.List(%s)')
+  convertfrompyobject_test('vim.List([%s])')
+  cb.append(">> ListItem")
+  ee('l[1000]')
+  cb.append(">> ListAssItem")
+  ee('ll[1] = 2')
+  ee('l[1000] = 3')
+  cb.append(">> ListAssSlice")
+  ee('ll[1:100] = "abcJ"')
+  iter_test('l[:] = %s')
+  ee('nel[1:10:2]  = "abcK"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[1:10:2]  = "a"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[1:1:-1]  = "a"')
+  cb.append(repr(tuple(nel)))
+  ee('nel[:] = FailingIterNextN(2)')
+  cb.append(repr(tuple(nel)))
+  convertfrompyobject_test('l[:] = [%s]')
+  cb.append(">> ListConcatInPlace")
+  iter_test('l.extend(%s)')
+  convertfrompyobject_test('l.extend([%s])')
+  cb.append(">> ListSetattr")
+  ee('del l.locked')
+  ee('l.locked = FailingTrue()')
+  ee('l.xxx = True')
+  cb.append("> Function")
+  cb.append(">> FunctionConstructor")
+  cb.append(">>> FunctionConstructor")
+  ee('vim.Function("123")')
+  ee('vim.Function("xxx_non_existent_function_xxx")')
+  ee('vim.Function("xxx#non#existent#function#xxx")')
+  ee('vim.Function("xxx_non_existent_function_xxx2", args=[])')
+  ee('vim.Function("xxx_non_existent_function_xxx3", self={})')
+  ee('vim.Function("xxx_non_existent_function_xxx4", args=[], self={})')
+  cb.append(">>> FunctionNew")
+  ee('vim.Function("tr", self="abcFuncSelf")')
+  ee('vim.Function("tr", args=427423)')
+  ee('vim.Function("tr", self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function(self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function("tr", "", self="abcFuncSelf2", args="abcFuncArgs2")')
+  ee('vim.Function("tr", "")')
+  cb.append(">> FunctionCall")
+  convertfrompyobject_test('f(%s)')
+  convertfrompymapping_test('fd(self=%s)')
+  cb.append("> TabPage")
+  cb.append(">> TabPageAttr")
+  ee('vim.current.tabpage.xxx')
+  cb.append("> TabList")
+  cb.append(">> TabListItem")
+  ee('vim.tabpages[1000]')
+  cb.append("> Window")
+  cb.append(">> WindowAttr")
+  ee('vim.current.window.xxx')
+  cb.append(">> WindowSetattr")
+  ee('vim.current.window.buffer = 0')
+  ee('vim.current.window.cursor = (100000000, 100000000)')
+  ee('vim.current.window.cursor = True')
+  number_test('vim.current.window.height = %s', unsigned=True)
+  number_test('vim.current.window.width = %s', unsigned=True)
+  ee('vim.current.window.xxxxxx = True')
+  cb.append("> WinList")
+  cb.append(">> WinListItem")
+  ee('vim.windows[1000]')
+  cb.append("> Buffer")
+  cb.append(">> StringToLine (indirect)")
+  ee('vim.current.buffer[0] = "\\na"')
+  ee('vim.current.buffer[0] = b"\\na"')
+  cb.append(">> SetBufferLine (indirect)")
+  ee('vim.current.buffer[0] = True')
+  cb.append(">> SetBufferLineList (indirect)")
+  ee('vim.current.buffer[:] = True')
+  ee('vim.current.buffer[:] = ["\\na", "bc"]')
+  cb.append(">> InsertBufferLines (indirect)")
+  ee('vim.current.buffer.append(None)')
+  ee('vim.current.buffer.append(["\\na", "bc"])')
+  ee('vim.current.buffer.append("\\nbc")')
+  cb.append(">> RBItem")
+  ee('vim.current.buffer[100000000]')
+  cb.append(">> RBAsItem")
+  ee('vim.current.buffer[100000000] = ""')
+  cb.append(">> BufferAttr")
+  ee('vim.current.buffer.xxx')
+  cb.append(">> BufferSetattr")
+  ee('vim.current.buffer.name = True')
+  ee('vim.current.buffer.xxx = True')
+  cb.append(">> BufferMark")
+  ee('vim.current.buffer.mark(0)')
+  ee('vim.current.buffer.mark("abcM")')
+  ee('vim.current.buffer.mark("!")')
+  cb.append(">> BufferRange")
+  ee('vim.current.buffer.range(1, 2, 3)')
+  cb.append("> BufMap")
+  cb.append(">> BufMapItem")
+  ee('vim.buffers[100000000]')
+  number_test('vim.buffers[%s]', natural=True)
+  cb.append("> Current")
+  cb.append(">> CurrentGetattr")
+  ee('vim.current.xxx')
+  cb.append(">> CurrentSetattr")
+  ee('vim.current.line = True')
+  ee('vim.current.buffer = True')
+  ee('vim.current.window = True')
+  ee('vim.current.tabpage = True')
+  ee('vim.current.xxx = True')
+  del d
+  del ned
+  del dl
+  del l
+  del ll
+  del nel
+  del f
+  del fd
+  del fdel
+  del subexpr_test
+  del stringtochars_test
+  del Mapping
+  del convertfrompyobject_test
+  del convertfrompymapping_test
+  del iter_test
+  del number_test
+  del FailingTrue
+  del FailingIter
+  del FailingIterNext
+  del FailingIterNextN
+  del FailingMapping
+  del FailingMappingKey
+  del FailingList
+  del NoArgsCall
+  del FailingCall
+  del FailingNumber
 EOF
 :delfunction F
 :"
 :" Test import
-py3 << EOF
-sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
-sys.path.append(os.path.join(os.getcwd(), 'python_after'))
-vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
-l = []
-def callback(path):
-    l.append(os.path.relpath(path))
-vim.foreach_rtp(callback)
-cb.append(repr(l))
-del l
-def callback(path):
-    return os.path.relpath(path)
-cb.append(repr(vim.foreach_rtp(callback)))
-del callback
-from module import dir as d
-from modulex import ddir
-cb.append(d + ',' + ddir)
-import before
-cb.append(before.dir)
-import after
-cb.append(after.dir)
-import topmodule as tm
-import topmodule.submodule as tms
-import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss
-cb.append(tm.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):])
-cb.append(tms.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):])
-cb.append(tmsss.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
-del before
-del after
-del d
-del ddir
-del tm
-del tms
-del tmsss
+py3 << trim EOF
+  sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
+  sys.path.append(os.path.join(os.getcwd(), 'python_after'))
+  vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
+  l = []
+  def callback(path):
+      l.append(os.path.relpath(path))
+  vim.foreach_rtp(callback)
+  cb.append(repr(l))
+  del l
+  def callback(path):
+      return os.path.relpath(path)
+  cb.append(repr(vim.foreach_rtp(callback)))
+  del callback
+  from module import dir as d
+  from modulex import ddir
+  cb.append(d + ',' + ddir)
+  import before
+  cb.append(before.dir)
+  import after
+  cb.append(after.dir)
+  import topmodule as tm
+  import topmodule.submodule as tms
+  import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss
+  cb.append(tm.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):])
+  cb.append(tms.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):])
+  cb.append(tmsss.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
+  del before
+  del after
+  del d
+  del ddir
+  del tm
+  del tms
+  del tmsss
 EOF
 :"
 :" Test exceptions
 :fun Exe(e)
 :   execute a:e
 :endfun
-py3 << EOF
-Exe = vim.bindeval('function("Exe")')
-ee('vim.command("throw \'abcN\'")')
-ee('Exe("throw \'def\'")')
-ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
-ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
-ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
-ee('vim.eval("xxx_unknown_function_xxx()")')
-ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
-del Exe
+py3 << trim EOF
+  Exe = vim.bindeval('function("Exe")')
+  ee('vim.command("throw \'abcN\'")')
+  ee('Exe("throw \'def\'")')
+  ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
+  ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
+  ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
+  ee('vim.eval("xxx_unknown_function_xxx()")')
+  ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
+  del Exe
 EOF
 :delfunction Exe
 :"
 :" Regression: interrupting vim.command propagates to next vim.command
-py3 << EOF
-def test_keyboard_interrupt():
-    try:
-        vim.command('while 1 | endwhile')
-    except KeyboardInterrupt:
-        cb.append('Caught KeyboardInterrupt')
-    except Exception:
-        cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
-    else:
-        cb.append('!!!!!!!! No exception')
-    try:
-        vim.command('$ put =\'Running :put\'')
-    except KeyboardInterrupt:
-        cb.append('!!!!!!!! Caught KeyboardInterrupt')
-    except Exception:
-        cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
-    else:
-        cb.append('No exception')
+py3 << trim EOF
+  def test_keyboard_interrupt():
+      try:
+          vim.command('while 1 | endwhile')
+      except KeyboardInterrupt:
+          cb.append('Caught KeyboardInterrupt')
+      except Exception:
+          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+      else:
+          cb.append('!!!!!!!! No exception')
+      try:
+          vim.command('$ put =\'Running :put\'')
+      except KeyboardInterrupt:
+          cb.append('!!!!!!!! Caught KeyboardInterrupt')
+      except Exception:
+          cb.append('!!!!!!!! Caught exception: ' + emsg(sys.exc_info()))
+      else:
+          cb.append('No exception')
 EOF
 :debuggreedy
 :call inputsave()
@@ -1682,13 +1682,13 @@ EOF
 :py3 del test_keyboard_interrupt
 :"
 :" Cleanup
-py3 << EOF
-del cb
-del ee
-del emsg
-del sys
-del os
-del vim
+py3 << trim EOF
+  del cb
+  del ee
+  del emsg
+  del sys
+  del os
+  del vim
 EOF
 :endfun
 :"
--- a/src/testdir/test_lua.vim
+++ b/src/testdir/test_lua.vim
@@ -597,14 +597,33 @@ func Test_lua_set_cursor()
   new
   call setline(1, ['first line', 'second line'])
   normal gg
-  lua << EOF
-w = vim.window()
-w.line = 1
-w.col = 5
-EOF
+  lua << trim EOF
+    w = vim.window()
+    w.line = 1
+    w.col = 5
+  EOF
   call assert_equal([1, 5], [line('.'), col('.')])
 
   " Check that movement after setting cursor position keeps current column.
   normal j
   call assert_equal([2, 5], [line('.'), col('.')])
 endfunc
+
+" Test for various heredoc syntax
+func Test_lua_heredoc()
+  lua << END
+vim.command('let s = "A"')
+END
+  lua <<
+vim.command('let s ..= "B"')
+.
+  lua << trim END
+    vim.command('let s ..= "C"')
+  END
+  lua << trim
+    vim.command('let s ..= "D"')
+  .
+  call assert_equal('ABCD', s)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_perl.vim
+++ b/src/testdir/test_perl.vim
@@ -219,11 +219,11 @@ endfunc
 
 func Test_stdio()
   redir =>l:out
-  perl <<EOF
+  perl << trim EOF
     VIM::Msg("&VIM::Msg");
     print "STDOUT";
     print STDERR "STDERR";
-EOF
+  EOF
   redir END
   call assert_equal(['&VIM::Msg', 'STDOUT', 'STDERR'], split(l:out, "\n"))
 endfunc
@@ -290,3 +290,22 @@ func Test_set_cursor()
   normal j
   call assert_equal([2, 6], [line('.'), col('.')])
 endfunc
+
+" Test for various heredoc syntax
+func Test_perl_heredoc()
+  perl << END
+VIM::DoCommand('let s = "A"')
+END
+  perl <<
+VIM::DoCommand('let s ..= "B"')
+.
+  perl << trim END
+    VIM::DoCommand('let s ..= "C"')
+  END
+  perl << trim
+    VIM::DoCommand('let s ..= "D"')
+  .
+  call assert_equal('ABCD', s)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_python2.vim
+++ b/src/testdir/test_python2.vim
@@ -167,3 +167,22 @@ func Test_Catch_Exception_Message()
     call assert_match( '^Vim(.*):RuntimeError: TEST$', v:exception )
   endtry
 endfunc
+
+" Test for various heredoc syntax
+func Test_python_heredoc()
+  python << END
+s='A'
+END
+  python <<
+s+='B'
+.
+  python << trim END
+    s+='C'
+  END
+  python << trim
+    s+='D'
+  .
+  call assert_equal('ABCD', pyxeval('s'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_python3.vim
+++ b/src/testdir/test_python3.vim
@@ -295,9 +295,11 @@ func Test_python3_opt_reset_local_to_glo
   " Set the global and buffer-local option values and then clear the
   " buffer-local option value.
   for opt in bopts
-    py3 pyopt = vim.bindeval("opt")
-    py3 vim.options[pyopt[0]] = pyopt[1]
-    py3 curbuf.options[pyopt[0]] = pyopt[2]
+    py3 << trim END
+      pyopt = vim.bindeval("opt")
+      vim.options[pyopt[0]] = pyopt[1]
+      curbuf.options[pyopt[0]] = pyopt[2]
+    END
     exe "call assert_equal(opt[2], &" .. opt[0] .. ")"
     exe "call assert_equal(opt[1], &g:" .. opt[0] .. ")"
     exe "call assert_equal(opt[2], &l:" .. opt[0] .. ")"
@@ -315,9 +317,11 @@ func Test_python3_opt_reset_local_to_glo
         \ ['sidescrolloff', 6, 12, -1],
         \ ['statusline', '%<%f', '%<%F', '']]
   for opt in wopts
-    py3 pyopt = vim.bindeval("opt")
-    py3 vim.options[pyopt[0]] = pyopt[1]
-    py3 curwin.options[pyopt[0]] = pyopt[2]
+    py3 << trim
+      pyopt = vim.bindeval("opt")
+      vim.options[pyopt[0]] = pyopt[1]
+      curwin.options[pyopt[0]] = pyopt[2]
+    .
     exe "call assert_equal(opt[2], &" .. opt[0] .. ")"
     exe "call assert_equal(opt[1], &g:" .. opt[0] .. ")"
     exe "call assert_equal(opt[2], &l:" .. opt[0] .. ")"
@@ -331,4 +335,21 @@ func Test_python3_opt_reset_local_to_glo
   close!
 endfunc
 
+" Test for various heredoc syntax
+func Test_python3_heredoc()
+  python3 << END
+s='A'
+END
+  python3 <<
+s+='B'
+.
+  python3 << trim END
+    s+='C'
+  END
+  python3 << trim
+    s+='D'
+  .
+  call assert_equal('ABCD', pyxeval('s'))
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_pyx2.vim
+++ b/src/testdir/test_pyx2.vim
@@ -15,10 +15,10 @@ endfunc
 
 func Test_pyx()
   redir => var
-  pyx << EOF
-import sys
-print(sys.version)
-EOF
+  pyx << trim EOF
+    import sys
+    print(sys.version)
+  EOF
   redir END
   call assert_match(s:py2pattern, split(var)[0])
 endfunc
@@ -79,3 +79,22 @@ func Test_Catch_Exception_Message()
     call assert_match( '^Vim(.*):RuntimeError: TEST$', v:exception )
   endtry
 endfunc
+
+" Test for various heredoc syntaxes
+func Test_pyx2_heredoc()
+  pyx << END
+result='A'
+END
+  pyx <<
+result+='B'
+.
+  pyx << trim END
+    result+='C'
+  END
+  pyx << trim
+    result+='D'
+  .
+  call assert_equal('ABCD', pyxeval('result'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_pyx3.vim
+++ b/src/testdir/test_pyx3.vim
@@ -15,10 +15,10 @@ endfunc
 
 func Test_pyx()
   redir => var
-  pyx << EOF
-import sys
-print(sys.version)
-EOF
+  pyx << trim EOF
+    import sys
+    print(sys.version)
+  EOF
   redir END
   call assert_match(s:py3pattern, split(var)[0])
 endfunc
@@ -79,3 +79,22 @@ func Test_Catch_Exception_Message()
     call assert_match( '^Vim(.*):RuntimeError: TEST$', v:exception )
   endtry
 endfunc
+
+" Test for various heredoc syntaxes
+func Test_pyx3_heredoc()
+  pyx << END
+result='A'
+END
+  pyx <<
+result+='B'
+.
+  pyx << trim END
+    result+='C'
+  END
+  pyx << trim
+    result+='D'
+  .
+  call assert_equal('ABCD', pyxeval('result'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_ruby.vim
+++ b/src/testdir/test_ruby.vim
@@ -332,11 +332,11 @@ func Test_ruby_Vim_evaluate_list()
   call setline(line('$'), ['2 line 2'])
   ruby Vim.command("normal /^2\n")
   let l = ["abc", "def"]
-  ruby << EOF
-  curline = $curbuf.line_number
-  l = Vim.evaluate("l");
-  $curbuf.append(curline, l.join("\n"))
-EOF
+  ruby << trim EOF
+    curline = $curbuf.line_number
+    l = Vim.evaluate("l");
+    $curbuf.append(curline, l.join("\n"))
+  EOF
   normal j
   .rubydo $_ = $_.gsub(/\n/, '/')
   call assert_equal('abc/def', getline('$'))
@@ -394,3 +394,22 @@ func Test_ruby_p()
   let messages = split(execute('message'), "\n")
   call assert_equal(0, len(messages))
 endfunc
+
+" Test for various heredoc syntax
+func Test_ruby_heredoc()
+  ruby << END
+Vim.command('let s = "A"')
+END
+  ruby <<
+Vim.command('let s ..= "B"')
+.
+  ruby << trim END
+    Vim.command('let s ..= "C"')
+  END
+  ruby << trim
+    Vim.command('let s ..= "D"')
+  .
+  call assert_equal('ABCD', s)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_tcl.vim
+++ b/src/testdir/test_tcl.vim
@@ -67,11 +67,11 @@ func Test_vim_buffer()
   " Test ::vim::buffer list
   call assert_equal('2',    TclEval('llength [::vim::buffer list]'))
   call assert_equal(b1.' '.b2, TclEval('::vim::buffer list'))
-  tcl <<EOF
+  tcl << trim EOF
     proc eachbuf { cmd } {
       foreach b [::vim::buffer list] { $b command $cmd }
     }
-EOF
+  EOF
   tcl eachbuf %s/foo/FOO/g
   b! Xfoo1
   call assert_equal(['FOObar'], getline(1, '$'))
@@ -680,3 +680,22 @@ func Test_set_cursor()
   normal j
   call assert_equal([2, 5], [line('.'), col('.')])
 endfunc
+
+" Test for different syntax for ruby heredoc
+func Test_tcl_heredoc()
+  tcl << END
+::vim::command {let s = "A"}
+END
+  tcl <<
+::vim::command {let s ..= "B"}
+.
+  tcl << trim END
+    ::vim::command {let s ..= "C"}
+  END
+  tcl << trim
+    ::vim::command {let s ..= "D"}
+  .
+  call assert_equal('ABCD', s)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -2830,10 +2830,19 @@ ex_function(exarg_T *eap)
 	    {
 		// ":python <<" continues until a dot, like ":append"
 		p = skipwhite(arg + 2);
+		if (STRNCMP(p, "trim", 4) == 0)
+		{
+		    // Ignore leading white space.
+		    p = skipwhite(p + 4);
+		    heredoc_trimmed = vim_strnsave(theline,
+			    (int)(skipwhite(theline) - theline));
+		}
 		if (*p == NUL)
 		    skip_until = vim_strsave((char_u *)".");
 		else
-		    skip_until = vim_strsave(p);
+		    skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p));
+		do_concat = FALSE;
+		is_heredoc = TRUE;
 	    }
 
 	    // Check for ":let v =<< [trim] EOF"
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    578,
+/**/
     577,
 /**/
     576,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4207,7 +4207,7 @@ compile_assignment(char_u *arg, exarg_T 
 	// [let] varname =<< [trim] {end}
 	eap->getline = heredoc_getline;
 	eap->cookie = cctx;
-	l = heredoc_get(eap, op + 3);
+	l = heredoc_get(eap, op + 3, FALSE);
 
 	// Push each line and the create the list.
 	FOR_ALL_LIST_ITEMS(l, li)