def postcmd(self, stop, line): """Called just before execution of line. For readline, this handles the automatic indentation of code blocks. """ try: import readline except ImportError: return stop if self.need_more_lines: if len(line.strip()) == 0: readline.set_pre_input_hook(None) self._current_indent = "" elif ends_with_colon_token(line): ind = line[:len(line) - len(line.lstrip())] ind += XSH.env.get("INDENT") readline.set_pre_input_hook(_insert_text_func(ind, readline)) self._current_indent = ind elif line.split(maxsplit=1)[0] in DEDENT_TOKENS: env = XSH.env ind = self._current_indent[:-len(env.get("INDENT"))] readline.set_pre_input_hook(_insert_text_func(ind, readline)) self._current_indent = ind else: ind = line[:len(line) - len(line.lstrip())] if ind != self._current_indent: insert_func = _insert_text_func(ind, readline) readline.set_pre_input_hook(insert_func) self._current_indent = ind else: readline.set_pre_input_hook(None) return stop
def carriage_return(b, cli, *, autoindent=True): """Preliminary parser to determine if 'Enter' key should send command to the xonsh parser for execution or should insert a newline for continued input. Current 'triggers' for inserting a newline are: - Not on first line of buffer and line is non-empty - Previous character is a colon (covers if, for, etc...) - User is in an open paren-block - Line ends with backslash - Any text exists below cursor position (relevant when editing previous multiline blocks) """ doc = b.document at_end_of_line = _is_blank(doc.current_line_after_cursor) current_line_blank = _is_blank(doc.current_line) env = XSH.env indent = env.get("INDENT") if autoindent else "" partial_string_info = check_for_partial_string(doc.text) in_partial_string = ( partial_string_info[0] is not None and partial_string_info[1] is None ) # indent after a colon if ends_with_colon_token(doc.current_line_before_cursor) and at_end_of_line: b.newline(copy_margin=autoindent) b.insert_text(indent, fire_event=False) # if current line isn't blank, check dedent tokens elif ( not current_line_blank and doc.current_line.split(maxsplit=1)[0] in DEDENT_TOKENS and doc.line_count > 1 ): b.newline(copy_margin=autoindent) 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.current_line.endswith(get_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:]) ): b.newline(copy_margin=autoindent) elif not current_line_blank and not can_compile(doc.text): b.newline(copy_margin=autoindent) elif current_line_blank and in_partial_string: b.newline(copy_margin=autoindent) else: b.validate_and_handle()
def _parse_ctx_free(self, input, mode="exec", filename=None, logical_input=False): last_error_line = last_error_col = -1 parsed = False original_error = None greedy = False if filename is None: filename = self.filename if logical_input: beg_spaces = starting_whitespace(input) input = input[len(beg_spaces):] while not parsed: try: tree = self.parser.parse( input, filename=filename, mode=mode, debug_level=(self.debug_level >= 2), ) parsed = True except IndentationError as e: if original_error is None: raise e else: raise original_error except SyntaxError as e: if original_error is None: original_error = e if (e.loc is None) or (last_error_line == e.loc.lineno and last_error_col in (e.loc.column + 1, e.loc.column)): raise original_error from None elif last_error_line != e.loc.lineno: original_error = e last_error_col = e.loc.column last_error_line = e.loc.lineno idx = last_error_line - 1 lines = input.splitlines() if input.endswith("\n"): lines.append("") line, nlogical, idx = get_logical_line(lines, idx) if nlogical > 1 and not logical_input: _, sbpline = self._parse_ctx_free(line, mode=mode, filename=filename, logical_input=True) self._print_debug_wrapping(line, sbpline, last_error_line, last_error_col, maxcol=None) replace_logical_line(lines, sbpline, idx, nlogical) last_error_col += 3 input = "\n".join(lines) continue if len(line.strip()) == 0: # whitespace only lines are not valid syntax in Python's # interactive mode='single', who knew?! Just ignore them. # this might cause actual syntax errors to have bad line # numbers reported, but should only affect interactive mode del lines[idx] last_error_line = last_error_col = -1 input = "\n".join(lines) continue if last_error_line > 1 and ends_with_colon_token( lines[idx - 1]): # catch non-indented blocks and raise error. prev_indent = len(lines[idx - 1]) - len( lines[idx - 1].lstrip()) curr_indent = len(lines[idx]) - len(lines[idx].lstrip()) if prev_indent == curr_indent: raise original_error lexer = self.parser.lexer maxcol = (None if greedy else find_next_break( line, mincol=last_error_col, lexer=lexer)) if not greedy and maxcol in (e.loc.column + 1, e.loc.column): # go greedy the first time if the syntax error was because # we hit an end token out of place. This usually indicates # a subshell or maybe a macro. if not balanced_parens(line, maxcol=maxcol): greedy = True maxcol = None sbpline = subproc_toks(line, returnline=True, greedy=greedy, maxcol=maxcol, lexer=lexer) if sbpline is None: # subprocess line had no valid tokens, if len(line.partition("#")[0].strip()) == 0: # likely because it only contained a comment. del lines[idx] last_error_line = last_error_col = -1 input = "\n".join(lines) continue elif not greedy: greedy = True continue else: # or for some other syntax error raise original_error elif sbpline[last_error_col:].startswith( "![![") or sbpline.lstrip().startswith("![!["): # if we have already wrapped this in subproc tokens # and it still doesn't work, adding more won't help # anything if not greedy: greedy = True continue else: raise original_error # replace the line self._print_debug_wrapping(line, sbpline, last_error_line, last_error_col, maxcol=maxcol) replace_logical_line(lines, sbpline, idx, nlogical) last_error_col += 3 input = "\n".join(lines) if logical_input: input = beg_spaces + input return tree, input
def test_ends_with_colon_token(line, exp): obs = ends_with_colon_token(line, lexer=LEXER) if exp: assert obs else: assert not obs