Repository: xonsh/xonsh

Issue:
cd complains if directory name has trailing backspace.
The trailing backspace on directory names on windows cause `cd` to complain. This one is fairly new, but I haven't dug into what PR caused it. (The trailing backspace are automatically added by the tab-completer by design).

```
snail@sea ~\Documents\xonsh master
$ cd build\
xonsh: To log full traceback to a file set: $XONSH_TRACEBACK_LOGFILE = <filename>
Traceback (most recent call last):
  File "C:\Users\mel\Documents\xonsh\xonsh\execer.py", line 164, in _parse_ctx_free
    debug_level=(self.debug_level > 2))
  File "C:\Users\mel\Documents\xonsh\xonsh\parsers\base.py", line 335, in parse
    tree = self.parser.parse(input=s, lexer=self.lexer, debug=debug_level)
  File "C:\Users\mel\Anaconda3\lib\site-packages\ply\yacc.py", line 331, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "C:\Users\mel\Anaconda3\lib\site-packages\ply\yacc.py", line 1199, in parseopt_notrack
    tok = call_errorfunc(self.errorfunc, errtoken, self)
  File "C:\Users\mel\Anaconda3\lib\site-packages\ply\yacc.py", line 193, in call_errorfunc
    r = errorfunc(token)
  File "C:\Users\mel\Documents\xonsh\xonsh\parsers\base.py", line 2694, in p_error
    column=p.lexpos))
  File "C:\Users\mel\Documents\xonsh\xonsh\parsers\base.py", line 466, in _parse_error
    raise err
  File "<string>", line None
SyntaxError: <xonsh-code>:1:3: ('code: build',)
cd build\
   ^

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\mel\Documents\xonsh\xonsh\ptk\shell.py", line 134, in _push
    locs=None)
  File "C:\Users\mel\Documents\xonsh\xonsh\execer.py", line 109, in compile
    transform=transform)
  File "C:\Users\mel\Documents\xonsh\xonsh\execer.py", line 79, in parse
    tree, input = self._parse_ctx_free(input, mode=mode, filename=filename)
  File "C:\Users\mel\Documents\xonsh\xonsh\execer.py", line 182, in _parse_ctx_free
    line, nlogical = get_logical_line(lines, idx)
  File "C:\Users\mel\Documents\xonsh\xonsh\tools.py", line 361, in get_logical_line
    line = line[:-1] + lines[idx]
IndexError: list index out of range
```


Discussion:
Thanks for reporting this @melund! I feel that this is related to #983
I am not sure that it is directly related. This has worked nicely for a long time. #983 was mostly about quoted directory path having to end with double backslash.

This issue is when names are not quoted. 

Git bisect tells me the following: 
```
0cb619468a20a2433191111ff7f544c85fb1b04f is the first bad commit
commit 0cb619468a20a2433191111ff7f544c85fb1b04f
Author: Anthony Scopatz <scopatz@gmail.com>
Date:   Wed Feb 15 00:58:38 2017 -0500

    line continuation fixes
```

Probably not surprising. Maybe we can make a special case for `cd`  and Windows. 


Yeah, you are right that is a red herring. Sorry.  Maybe we should just makes it so that on windows the `\` needs to be preceded by a space. Other commands, like ls, dir, etc, will also have this problem. So this is less of a `cd` issue and more of a Windows issue.
Do mean that the line continuation character `\` should be preceded by a space? That could probably be fine as a general solution. 
Of course, it would be annoying that xonsh code can be platform dependent.  
yeah that is what I mean.
well the path stuff in Python is already platform independent. We could also restrict the extra space to interactive mode.  That would force scripts to be platform independent
*Python paths = platform dependent
I think that makes good sense :) I was afraid this was going to be a hard dilemma. 

Resolving patch:
diff --git a/xonsh/base_shell.py b/xonsh/base_shell.py
--- a/xonsh/base_shell.py
+++ b/xonsh/base_shell.py
@@ -7,7 +7,8 @@
 import builtins
 
 from xonsh.tools import (XonshError, print_exception, DefaultNotGiven,
-                         check_for_partial_string, format_std_prepost)
+                         check_for_partial_string, format_std_prepost,
+                         LINE_CONTINUATION)
 from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS
 from xonsh.codecache import (should_use_cache, code_cache_name,
                              code_cache_check, get_cache_filename,
@@ -408,7 +409,7 @@ def compile(self, src):
             if usecache:
                 self.reset_buffer()
                 return src, code
-        if src.endswith('\\\n'):
+        if src.endswith(str(LINE_CONTINUATION)+'\n'):
             self.need_more_lines = True
             return src, None
         try:
diff --git a/xonsh/ptk/key_bindings.py b/xonsh/ptk/key_bindings.py
--- a/xonsh/ptk/key_bindings.py
+++ b/xonsh/ptk/key_bindings.py
@@ -8,7 +8,7 @@
 from prompt_toolkit.keys import Keys
 
 from xonsh.aliases import xonsh_exit
-from xonsh.tools import ON_WINDOWS, check_for_partial_string
+from xonsh.tools import check_for_partial_string, LINE_CONTINUATION
 from xonsh.shell import transform_command
 
 env = builtins.__xonsh_env__
@@ -50,9 +50,7 @@ def carriage_return(b, cli, *, autoindent=True):
         b.delete_before_cursor(count=len(indent))
     elif (not doc.on_first_line and not current_line_blank):
         b.newline(copy_margin=autoindent)
-    elif (doc.char_before_cursor == '\\' and
-            not (not builtins.__xonsh_env__.get('FORCE_POSIX_PATHS') and
-                 ON_WINDOWS)):
+    elif (doc.current_line.endswith(str(LINE_CONTINUATION))):
         b.newline(copy_margin=autoindent)
     elif (doc.find_next_word_beginning() is not None and
             (any(not _is_blank(i) for i in doc.lines_from_current[1:]))):
diff --git a/xonsh/tools.py b/xonsh/tools.py
--- a/xonsh/tools.py
+++ b/xonsh/tools.py
@@ -346,6 +346,18 @@ def subproc_toks(line, mincol=-1, maxcol=None, lexer=None, returnline=False):
     return rtn
 
 
+@lazyobject
+def LINE_CONTINUATION():
+    """ The line contiuation characters used in subproc mode. In interactive
+         mode on Windows the backslash must be preseeded by a space. This is because
+         paths on windows may end in a backspace.
+    """
+    if ON_WINDOWS and builtins.__xonsh_env__.get('XONSH_INTERACTIVE'):
+        return ' \\'
+    else:
+        return '\\'
+
+
 def get_logical_line(lines, idx):
     """Returns a single logical line (i.e. one without line continuations)
     from a list of lines.  This line should begin at index idx. This also
@@ -355,7 +367,7 @@ def get_logical_line(lines, idx):
     n = 1
     nlines = len(lines)
     line = lines[idx]
-    while line.endswith('\\') and idx < nlines:
+    while line.endswith(str(LINE_CONTINUATION)) and idx < nlines:
         n += 1
         idx += 1
         line = line[:-1] + lines[idx]
@@ -379,7 +391,7 @@ def replace_logical_line(lines, logical, idx, n):
             logical = ''
         else:
             # found space to split on
-            lines[i] = logical[:b] + '\\'
+            lines[i] = logical[:b] + str(LINE_CONTINUATION)
             logical = logical[b:]
     lines[idx+n-1] = logical
 


Test patch: