def test_partial_string(leaders, prefix, quote): (l, l_len), (f, f_len) = leaders s = prefix + quote t = s + "test string" + quote t_len = len(t) # single string test_string = l + t + f obs = check_for_partial_string(test_string) exp = l_len, l_len + t_len, s assert obs == exp # single partial test_string = l + f + s + "test string" obs = check_for_partial_string(test_string) exp = l_len + f_len, None, s assert obs == exp # two strings test_string = l + t + f + l + t + f obs = check_for_partial_string(test_string) exp = (l_len + t_len + f_len + l_len), (l_len + t_len + f_len + l_len + t_len), s assert obs == exp # one string, one partial test_string = l + t + f + l + s + "test string" obs = check_for_partial_string(test_string) exp = l_len + t_len + f_len + l_len, None, s assert obs == exp
def test_partial_string(leaders, prefix, quote): (l, l_len), (f, f_len) = leaders s = prefix + quote t = s + "test string" + quote t_len = len(t) # single string test_string = l + t + f obs = check_for_partial_string(test_string) exp = l_len, l_len + t_len, s assert obs == exp # single partial test_string = l + f + s + "test string" obs = check_for_partial_string(test_string) exp = l_len + f_len, None, s assert obs == exp # two strings test_string = l + t + f + l + t + f obs = check_for_partial_string(test_string) exp = (l_len + t_len + f_len + l_len), (l_len + t_len + f_len + l_len + t_len), s assert obs == exp # one string, one partial test_string = l + t + f + l + s + "test string" obs = check_for_partial_string(test_string) exp = l_len + t_len + f_len + l_len, None, s assert obs == exp
def try_parse_string_literal(raw_arg: str) -> Optional[CommandArg]: """Try to parse this as a single string literal. can be partial For example: "wow" "a b '''a b 'c' "d" """ startix, endix, quote = check_for_partial_string(raw_arg) if startix != 0 or endix not in ( None, # the arg doesn't start with a string literal len(raw_arg), # the string literal ends in the middle of the arg ): # xonsh won't treat it as a string literal return None else: if endix is None: # no closing quote return CommandArg(raw_arg[len(quote) : endix], opening_quote=quote) else: closing_quote_len = quote.count('"') + quote.count("'") return CommandArg( value=raw_arg[len(quote) : -closing_quote_len], closing_quote=raw_arg[-closing_quote_len:], opening_quote=quote, )
def compile(self, src): """Compiles source code and returns the (possibly modified) source and a valid code object. """ _cache = should_use_cache(self.execer, 'single') if _cache: codefname = code_cache_name(src) cachefname = get_cache_filename(codefname, code=True) usecache, code = code_cache_check(cachefname) if usecache: self.reset_buffer() return src, code try: code = self.execer.compile(src, mode='single', glbs=self.ctx, locs=None) if _cache: update_cache(code, cachefname) self.reset_buffer() except SyntaxError: partial_string_info = check_for_partial_string(src) in_partial_string = (partial_string_info[0] is not None and partial_string_info[1] is None) if (src == '\n' or src.endswith('\n\n')) and not in_partial_string: self.reset_buffer() print_exception() return src, None self.need_more_lines = True except Exception: # pylint: disable=broad-except self.reset_buffer() print_exception() return src, None return src, code
def _path_from_partial_string(inp, pos=None): if pos is None: pos = len(inp) partial = inp[:pos] startix, endix, quote = check_for_partial_string(partial) _post = "" if startix is None: return None elif endix is None: string = partial[startix:] else: if endix != pos: _test = partial[endix:pos] if not any(i == ' ' for i in _test): _post = _test else: return None string = partial[startix:endix] end = re.sub(RE_STRING_START, '', quote) _string = string if not _string.endswith(end): _string = _string + end try: val = ast.literal_eval(_string) except SyntaxError: return None if isinstance(val, bytes): env = builtins.__xonsh_env__ val = val.decode(encoding=env.get('XONSH_ENCODING'), errors=env.get('XONSH_ENCODING_ERRORS')) return string + _post, val + _post, quote, end
def compile(self, src): """Compiles source code and returns the (possibly modified) source and a valid code object. """ _cache = should_use_cache(self.execer, 'single') if _cache: codefname = code_cache_name(src) cachefname = get_cache_filename(codefname, code=True) usecache, code = code_cache_check(cachefname) if usecache: self.reset_buffer() return src, code try: code = self.execer.compile(src, mode='single', glbs=self.ctx, locs=None) if _cache: update_cache(code, cachefname) self.reset_buffer() except SyntaxError: partial_string_info = check_for_partial_string(src) in_partial_string = (partial_string_info[0] is not None and partial_string_info[1] is None) if (src == '\n' or src.endswith('\n\n')) and not in_partial_string: self.reset_buffer() print_exception() return src, None self.need_more_lines = True code = None except Exception: # pylint: disable=broad-except self.reset_buffer() print_exception() code = None return src, code
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) 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 (doc.current_line_before_cursor.strip().endswith(':') 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.char_before_cursor == '\\' and not (not builtins.__xonsh_env__.get('FORCE_POSIX_PATHS') and ON_WINDOWS)): 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.accept_action.validate_and_handle(cli, b)
def test_partial_string(): # single string at start yield assert_equal, check_for_partial_string('no strings here'), (None, None, None) yield assert_equal, check_for_partial_string(''), (None, None, None) for s, e in _startend.items(): _test = s + inners + e for l in _leaders: for f in _leaders: # single string _res = check_for_partial_string(l + _test + f) yield assert_equal, _res, (len(l), len(l) + len(_test), s) # single partial _res = check_for_partial_string(l + f + s + inners) yield assert_equal, _res, (len(l + f), None, s) for s2, e2 in _startend.items(): _test2 = s2 + inners + e2 for l2 in _leaders: for f2 in _leaders: # two strings _res = check_for_partial_string(l + _test + f + l2 + _test2 + f2) yield assert_equal, _res, (len(l + _test + f + l2), len(l + _test + f + l2 + _test2), s2) # one string, one partial _res = check_for_partial_string(l + _test + f + l2 + s2 + inners) yield assert_equal, _res, (len(l + _test + f + l2), None, s2)
def expand_abbrev(buffer): abbrevs = getattr(builtins, "abbrevs", None) if abbrevs is None: return document = buffer.document word = document.get_word_before_cursor(WORD=True) if word in abbrevs.keys(): partial = document.text[: document.cursor_position] startix, endix, quote = check_for_partial_string(partial) if startix is not None and endix is None: return buffer.delete_before_cursor(count=len(word)) buffer.insert_text(abbrevs[word])
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) 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 (doc.current_line_before_cursor.strip().endswith(':') 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.char_before_cursor == '\\' and not (not builtins.__xonsh_env__.get('FORCE_POSIX_PATHS') and ON_WINDOWS)): 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.accept_action.validate_and_handle(cli, b)
def completedefault(self, prefix, line, begidx, endidx): """Implements tab-completion for text.""" if self.completer is None: return [] rl_completion_suppress_append() # this needs to be called each time _rebind_case_sensitive_completions() _s, _e, _q = check_for_partial_string(line) if _s is not None: if _e is not None and ' ' in line[_e:]: mline = line.rpartition(' ')[2] else: mline = line[_s:] else: mline = line.rpartition(' ')[2] offs = len(mline) - len(prefix) return [i[offs:] for i in self.completer.complete(prefix, line, begidx, endidx, ctx=self.ctx)[0]]
def compile(self, src): """Compiles source code and returns the (possibly modified) source and a valid code object. """ _cache = should_use_cache(self.execer, "single") if _cache: codefname = code_cache_name(src) cachefname = get_cache_filename(codefname, code=True) usecache, code = code_cache_check(cachefname) if usecache: self.reset_buffer() return src, code lincont = get_line_continuation() if src.endswith(lincont + "\n"): self.need_more_lines = True return src, None try: code = self.execer.compile( src, mode="single", glbs=self.ctx, locs=None, filename="<stdin>", compile_empty_tree=False, ) if _cache: update_cache(code, cachefname) self.reset_buffer() except SyntaxError: partial_string_info = check_for_partial_string(src) in_partial_string = (partial_string_info[0] is not None and partial_string_info[1] is None) if (src == "\n" or src.endswith("\n\n")) and not in_partial_string: self.reset_buffer() print_exception() return src, None self.need_more_lines = True code = None except Exception: # pylint: disable=broad-except self.reset_buffer() print_exception() code = None return src, code
def expand(self, buffer: Buffer) -> bool: """expand the given abbr text. Return true if cursor position changed.""" if not abbrevs: return False document = buffer.document word = document.get_word_before_cursor(WORD=True) if word in abbrevs.keys(): partial = document.text[:document.cursor_position] startix, endix, quote = check_for_partial_string(partial) if startix is not None and endix is None: return False text = get_abbreviated(word, buffer) buffer.delete_before_cursor(count=len(word)) buffer.insert_text(text) self.last_expanded = _LastExpanded(word, text) if EDIT_SYMBOL in text: set_cursor_position(buffer, text) return True return False
def _path_from_partial_string(inp, pos=None): if pos is None: pos = len(inp) partial = inp[:pos] startix, endix, quote = xt.check_for_partial_string(partial) _post = "" if startix is None: return None elif endix is None: string = partial[startix:] else: if endix != pos: _test = partial[endix:pos] if not any(i == " " for i in _test): _post = _test else: return None string = partial[startix:endix] # If 'pr'/'rp', treat as raw string, otherwise strip leading 'p' pstring_pre = _get_normalized_pstring_quote(quote)[0] if pstring_pre == "pr": string = f"r{string[2:]}" elif pstring_pre == "p": string = string[1:] end = xt.RE_STRING_START.sub("", quote) _string = string if not _string.endswith(end): _string = _string + end try: val = ast.literal_eval(_string) except (SyntaxError, ValueError): return None if isinstance(val, bytes): env = builtins.__xonsh__.env val = val.decode( encoding=env.get("XONSH_ENCODING"), errors=env.get("XONSH_ENCODING_ERRORS") ) return string + _post, val + _post, quote, end
def push(self, line): """Pushes a line onto the buffer and compiles the code in a way that enables multiline input. """ code = None self.buffer.append(line) if self.need_more_lines: return None, code src = ''.join(self.buffer) _cache = should_use_cache(self.execer, 'single') if _cache: codefname = code_cache_name(src) cachefname = get_cache_filename(codefname, code=True) usecache, code = code_cache_check(cachefname) if usecache: self.reset_buffer() return src, code try: code = self.execer.compile(src, mode='single', glbs=self.ctx, locs=None) if _cache: update_cache(code, cachefname) self.reset_buffer() except SyntaxError: partial_string_info = check_for_partial_string(src) in_partial_string = (partial_string_info[0] is not None and partial_string_info[1] is None) if ((line == '\n' and not in_partial_string)): self.reset_buffer() print_exception() return src, None self.need_more_lines = True except Exception: # pylint: disable=broad-except self.reset_buffer() print_exception() return src, None return src, code
def push(self, line): """Pushes a line onto the buffer and compiles the code in a way that enables multiline input. """ code = None self.buffer.append(line) if self.need_more_lines: return None, code src = ''.join(self.buffer) _cache = should_use_cache(self.execer, 'single') if _cache: codefname = code_cache_name(src) cachefname = get_cache_filename(codefname, code=True) usecache, code = code_cache_check(cachefname) if usecache: self.reset_buffer() return src, code try: code = self.execer.compile(src, mode='single', glbs=self.ctx, locs=None) if _cache: update_cache(code, cachefname) self.reset_buffer() except SyntaxError: partial_string_info = check_for_partial_string(src) in_partial_string = (partial_string_info[0] is not None and partial_string_info[1] is None) if ((line == '\n' and not in_partial_string)): self.reset_buffer() print_exception() return src, None self.need_more_lines = True except Exception: # pylint: disable=broad-except self.reset_buffer() print_exception() return src, None return src, code
def test_partial_string(): # single string at start yield assert_equal, check_for_partial_string('no strings here'), (None, None, None) yield assert_equal, check_for_partial_string(''), (None, None, None) for s,e in _startend.items(): _test = s + inners + e for l in _leaders: for f in _leaders: # single string _res = check_for_partial_string(l + _test + f) yield assert_equal, _res, (len(l), len(l) + len(_test), s) # single partial _res = check_for_partial_string(l + f + s + inners) yield assert_equal, _res, (len(l+f), None, s) for s2, e2 in _startend.items(): _test2 = s2 + inners + e2 for l2 in _leaders: for f2 in _leaders: # two strings _res = check_for_partial_string(l + _test + f + l2 + _test2 + f2) yield assert_equal, _res, (len(l+_test+f+l2), len(l+_test+f+l2+_test2), s2) # one string, one partial _res = check_for_partial_string(l + _test + f + l2 + s2 + inners) yield assert_equal, _res, (len(l+_test+f+l2), None, s2)
def test_partial_string_none(inp): assert check_for_partial_string(inp) == (None, None, None)
def test_partial_string_none(inp): assert check_for_partial_string(inp) == (None, None, None)