Index: third_party/pexpect/pexpect/screen.py |
diff --git a/third_party/pexpect/pexpect/screen.py b/third_party/pexpect/pexpect/screen.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0bced89d3172f1f3b2d3e585d5c95f60368e5eda |
--- /dev/null |
+++ b/third_party/pexpect/pexpect/screen.py |
@@ -0,0 +1,431 @@ |
+'''This implements a virtual screen. This is used to support ANSI terminal |
+emulation. The screen representation and state is implemented in this class. |
+Most of the methods are inspired by ANSI screen control codes. The |
+:class:`~pexpect.ANSI.ANSI` class extends this class to add parsing of ANSI |
+escape codes. |
+ |
+PEXPECT LICENSE |
+ |
+ This license is approved by the OSI and FSF as GPL-compatible. |
+ http://opensource.org/licenses/isc-license.txt |
+ |
+ Copyright (c) 2012, Noah Spurrier <noah@noah.org> |
+ PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY |
+ PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE |
+ COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. |
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
+ |
+''' |
+ |
+import codecs |
+import copy |
+import sys |
+ |
+import warnings |
+ |
+warnings.warn(("pexpect.screen and pexpect.ANSI are deprecated. " |
+ "We recommend using pyte to emulate a terminal screen: " |
+ "https://pypi.python.org/pypi/pyte"), |
+ stacklevel=2) |
+ |
+NUL = 0 # Fill character; ignored on input. |
+ENQ = 5 # Transmit answerback message. |
+BEL = 7 # Ring the bell. |
+BS = 8 # Move cursor left. |
+HT = 9 # Move cursor to next tab stop. |
+LF = 10 # Line feed. |
+VT = 11 # Same as LF. |
+FF = 12 # Same as LF. |
+CR = 13 # Move cursor to left margin or newline. |
+SO = 14 # Invoke G1 character set. |
+SI = 15 # Invoke G0 character set. |
+XON = 17 # Resume transmission. |
+XOFF = 19 # Halt transmission. |
+CAN = 24 # Cancel escape sequence. |
+SUB = 26 # Same as CAN. |
+ESC = 27 # Introduce a control sequence. |
+DEL = 127 # Fill character; ignored on input. |
+SPACE = u' ' # Space or blank character. |
+ |
+PY3 = (sys.version_info[0] >= 3) |
+if PY3: |
+ unicode = str |
+ |
+def constrain (n, min, max): |
+ |
+ '''This returns a number, n constrained to the min and max bounds. ''' |
+ |
+ if n < min: |
+ return min |
+ if n > max: |
+ return max |
+ return n |
+ |
+class screen: |
+ '''This object maintains the state of a virtual text screen as a |
+ rectangluar array. This maintains a virtual cursor position and handles |
+ scrolling as characters are added. This supports most of the methods needed |
+ by an ANSI text screen. Row and column indexes are 1-based (not zero-based, |
+ like arrays). |
+ |
+ Characters are represented internally using unicode. Methods that accept |
+ input characters, when passed 'bytes' (which in Python 2 is equivalent to |
+ 'str'), convert them from the encoding specified in the 'encoding' |
+ parameter to the constructor. Methods that return screen contents return |
+ unicode strings, with the exception of __str__() under Python 2. Passing |
+ ``encoding=None`` limits the API to only accept unicode input, so passing |
+ bytes in will raise :exc:`TypeError`. |
+ ''' |
+ def __init__(self, r=24, c=80, encoding='latin-1', encoding_errors='replace'): |
+ '''This initializes a blank screen of the given dimensions.''' |
+ |
+ self.rows = r |
+ self.cols = c |
+ self.encoding = encoding |
+ self.encoding_errors = encoding_errors |
+ if encoding is not None: |
+ self.decoder = codecs.getincrementaldecoder(encoding)(encoding_errors) |
+ else: |
+ self.decoder = None |
+ self.cur_r = 1 |
+ self.cur_c = 1 |
+ self.cur_saved_r = 1 |
+ self.cur_saved_c = 1 |
+ self.scroll_row_start = 1 |
+ self.scroll_row_end = self.rows |
+ self.w = [ [SPACE] * self.cols for _ in range(self.rows)] |
+ |
+ def _decode(self, s): |
+ '''This converts from the external coding system (as passed to |
+ the constructor) to the internal one (unicode). ''' |
+ if self.decoder is not None: |
+ return self.decoder.decode(s) |
+ else: |
+ raise TypeError("This screen was constructed with encoding=None, " |
+ "so it does not handle bytes.") |
+ |
+ def _unicode(self): |
+ '''This returns a printable representation of the screen as a unicode |
+ string (which, under Python 3.x, is the same as 'str'). The end of each |
+ screen line is terminated by a newline.''' |
+ |
+ return u'\n'.join ([ u''.join(c) for c in self.w ]) |
+ |
+ if PY3: |
+ __str__ = _unicode |
+ else: |
+ __unicode__ = _unicode |
+ |
+ def __str__(self): |
+ '''This returns a printable representation of the screen. The end of |
+ each screen line is terminated by a newline. ''' |
+ encoding = self.encoding or 'ascii' |
+ return self._unicode().encode(encoding, 'replace') |
+ |
+ def dump (self): |
+ '''This returns a copy of the screen as a unicode string. This is similar to |
+ __str__/__unicode__ except that lines are not terminated with line |
+ feeds.''' |
+ |
+ return u''.join ([ u''.join(c) for c in self.w ]) |
+ |
+ def pretty (self): |
+ '''This returns a copy of the screen as a unicode string with an ASCII |
+ text box around the screen border. This is similar to |
+ __str__/__unicode__ except that it adds a box.''' |
+ |
+ top_bot = u'+' + u'-'*self.cols + u'+\n' |
+ return top_bot + u'\n'.join([u'|'+line+u'|' for line in unicode(self).split(u'\n')]) + u'\n' + top_bot |
+ |
+ def fill (self, ch=SPACE): |
+ |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch) |
+ |
+ self.fill_region (1,1,self.rows,self.cols, ch) |
+ |
+ def fill_region (self, rs,cs, re,ce, ch=SPACE): |
+ |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch) |
+ |
+ rs = constrain (rs, 1, self.rows) |
+ re = constrain (re, 1, self.rows) |
+ cs = constrain (cs, 1, self.cols) |
+ ce = constrain (ce, 1, self.cols) |
+ if rs > re: |
+ rs, re = re, rs |
+ if cs > ce: |
+ cs, ce = ce, cs |
+ for r in range (rs, re+1): |
+ for c in range (cs, ce + 1): |
+ self.put_abs (r,c,ch) |
+ |
+ def cr (self): |
+ '''This moves the cursor to the beginning (col 1) of the current row. |
+ ''' |
+ |
+ self.cursor_home (self.cur_r, 1) |
+ |
+ def lf (self): |
+ '''This moves the cursor down with scrolling. |
+ ''' |
+ |
+ old_r = self.cur_r |
+ self.cursor_down() |
+ if old_r == self.cur_r: |
+ self.scroll_up () |
+ self.erase_line() |
+ |
+ def crlf (self): |
+ '''This advances the cursor with CRLF properties. |
+ The cursor will line wrap and the screen may scroll. |
+ ''' |
+ |
+ self.cr () |
+ self.lf () |
+ |
+ def newline (self): |
+ '''This is an alias for crlf(). |
+ ''' |
+ |
+ self.crlf() |
+ |
+ def put_abs (self, r, c, ch): |
+ '''Screen array starts at 1 index.''' |
+ |
+ r = constrain (r, 1, self.rows) |
+ c = constrain (c, 1, self.cols) |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch)[0] |
+ else: |
+ ch = ch[0] |
+ self.w[r-1][c-1] = ch |
+ |
+ def put (self, ch): |
+ '''This puts a characters at the current cursor position. |
+ ''' |
+ |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch) |
+ |
+ self.put_abs (self.cur_r, self.cur_c, ch) |
+ |
+ def insert_abs (self, r, c, ch): |
+ '''This inserts a character at (r,c). Everything under |
+ and to the right is shifted right one character. |
+ The last character of the line is lost. |
+ ''' |
+ |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch) |
+ |
+ r = constrain (r, 1, self.rows) |
+ c = constrain (c, 1, self.cols) |
+ for ci in range (self.cols, c, -1): |
+ self.put_abs (r,ci, self.get_abs(r,ci-1)) |
+ self.put_abs (r,c,ch) |
+ |
+ def insert (self, ch): |
+ |
+ if isinstance(ch, bytes): |
+ ch = self._decode(ch) |
+ |
+ self.insert_abs (self.cur_r, self.cur_c, ch) |
+ |
+ def get_abs (self, r, c): |
+ |
+ r = constrain (r, 1, self.rows) |
+ c = constrain (c, 1, self.cols) |
+ return self.w[r-1][c-1] |
+ |
+ def get (self): |
+ |
+ self.get_abs (self.cur_r, self.cur_c) |
+ |
+ def get_region (self, rs,cs, re,ce): |
+ '''This returns a list of lines representing the region. |
+ ''' |
+ |
+ rs = constrain (rs, 1, self.rows) |
+ re = constrain (re, 1, self.rows) |
+ cs = constrain (cs, 1, self.cols) |
+ ce = constrain (ce, 1, self.cols) |
+ if rs > re: |
+ rs, re = re, rs |
+ if cs > ce: |
+ cs, ce = ce, cs |
+ sc = [] |
+ for r in range (rs, re+1): |
+ line = u'' |
+ for c in range (cs, ce + 1): |
+ ch = self.get_abs (r,c) |
+ line = line + ch |
+ sc.append (line) |
+ return sc |
+ |
+ def cursor_constrain (self): |
+ '''This keeps the cursor within the screen area. |
+ ''' |
+ |
+ self.cur_r = constrain (self.cur_r, 1, self.rows) |
+ self.cur_c = constrain (self.cur_c, 1, self.cols) |
+ |
+ def cursor_home (self, r=1, c=1): # <ESC>[{ROW};{COLUMN}H |
+ |
+ self.cur_r = r |
+ self.cur_c = c |
+ self.cursor_constrain () |
+ |
+ def cursor_back (self,count=1): # <ESC>[{COUNT}D (not confused with down) |
+ |
+ self.cur_c = self.cur_c - count |
+ self.cursor_constrain () |
+ |
+ def cursor_down (self,count=1): # <ESC>[{COUNT}B (not confused with back) |
+ |
+ self.cur_r = self.cur_r + count |
+ self.cursor_constrain () |
+ |
+ def cursor_forward (self,count=1): # <ESC>[{COUNT}C |
+ |
+ self.cur_c = self.cur_c + count |
+ self.cursor_constrain () |
+ |
+ def cursor_up (self,count=1): # <ESC>[{COUNT}A |
+ |
+ self.cur_r = self.cur_r - count |
+ self.cursor_constrain () |
+ |
+ def cursor_up_reverse (self): # <ESC> M (called RI -- Reverse Index) |
+ |
+ old_r = self.cur_r |
+ self.cursor_up() |
+ if old_r == self.cur_r: |
+ self.scroll_up() |
+ |
+ def cursor_force_position (self, r, c): # <ESC>[{ROW};{COLUMN}f |
+ '''Identical to Cursor Home.''' |
+ |
+ self.cursor_home (r, c) |
+ |
+ def cursor_save (self): # <ESC>[s |
+ '''Save current cursor position.''' |
+ |
+ self.cursor_save_attrs() |
+ |
+ def cursor_unsave (self): # <ESC>[u |
+ '''Restores cursor position after a Save Cursor.''' |
+ |
+ self.cursor_restore_attrs() |
+ |
+ def cursor_save_attrs (self): # <ESC>7 |
+ '''Save current cursor position.''' |
+ |
+ self.cur_saved_r = self.cur_r |
+ self.cur_saved_c = self.cur_c |
+ |
+ def cursor_restore_attrs (self): # <ESC>8 |
+ '''Restores cursor position after a Save Cursor.''' |
+ |
+ self.cursor_home (self.cur_saved_r, self.cur_saved_c) |
+ |
+ def scroll_constrain (self): |
+ '''This keeps the scroll region within the screen region.''' |
+ |
+ if self.scroll_row_start <= 0: |
+ self.scroll_row_start = 1 |
+ if self.scroll_row_end > self.rows: |
+ self.scroll_row_end = self.rows |
+ |
+ def scroll_screen (self): # <ESC>[r |
+ '''Enable scrolling for entire display.''' |
+ |
+ self.scroll_row_start = 1 |
+ self.scroll_row_end = self.rows |
+ |
+ def scroll_screen_rows (self, rs, re): # <ESC>[{start};{end}r |
+ '''Enable scrolling from row {start} to row {end}.''' |
+ |
+ self.scroll_row_start = rs |
+ self.scroll_row_end = re |
+ self.scroll_constrain() |
+ |
+ def scroll_down (self): # <ESC>D |
+ '''Scroll display down one line.''' |
+ |
+ # Screen is indexed from 1, but arrays are indexed from 0. |
+ s = self.scroll_row_start - 1 |
+ e = self.scroll_row_end - 1 |
+ self.w[s+1:e+1] = copy.deepcopy(self.w[s:e]) |
+ |
+ def scroll_up (self): # <ESC>M |
+ '''Scroll display up one line.''' |
+ |
+ # Screen is indexed from 1, but arrays are indexed from 0. |
+ s = self.scroll_row_start - 1 |
+ e = self.scroll_row_end - 1 |
+ self.w[s:e] = copy.deepcopy(self.w[s+1:e+1]) |
+ |
+ def erase_end_of_line (self): # <ESC>[0K -or- <ESC>[K |
+ '''Erases from the current cursor position to the end of the current |
+ line.''' |
+ |
+ self.fill_region (self.cur_r, self.cur_c, self.cur_r, self.cols) |
+ |
+ def erase_start_of_line (self): # <ESC>[1K |
+ '''Erases from the current cursor position to the start of the current |
+ line.''' |
+ |
+ self.fill_region (self.cur_r, 1, self.cur_r, self.cur_c) |
+ |
+ def erase_line (self): # <ESC>[2K |
+ '''Erases the entire current line.''' |
+ |
+ self.fill_region (self.cur_r, 1, self.cur_r, self.cols) |
+ |
+ def erase_down (self): # <ESC>[0J -or- <ESC>[J |
+ '''Erases the screen from the current line down to the bottom of the |
+ screen.''' |
+ |
+ self.erase_end_of_line () |
+ self.fill_region (self.cur_r + 1, 1, self.rows, self.cols) |
+ |
+ def erase_up (self): # <ESC>[1J |
+ '''Erases the screen from the current line up to the top of the |
+ screen.''' |
+ |
+ self.erase_start_of_line () |
+ self.fill_region (self.cur_r-1, 1, 1, self.cols) |
+ |
+ def erase_screen (self): # <ESC>[2J |
+ '''Erases the screen with the background color.''' |
+ |
+ self.fill () |
+ |
+ def set_tab (self): # <ESC>H |
+ '''Sets a tab at the current position.''' |
+ |
+ pass |
+ |
+ def clear_tab (self): # <ESC>[g |
+ '''Clears tab at the current position.''' |
+ |
+ pass |
+ |
+ def clear_all_tabs (self): # <ESC>[3g |
+ '''Clears all tabs.''' |
+ |
+ pass |
+ |
+# Insert line Esc [ Pn L |
+# Delete line Esc [ Pn M |
+# Delete character Esc [ Pn P |
+# Scrolling region Esc [ Pn(top);Pn(bot) r |
+ |