~ubuntu-branches/ubuntu/trusty/pexpect/trusty-proposed

« back to all changes in this revision

Viewing changes to pexpect/screen.py

  • Committer: Package Import Robot
  • Author(s): Andrew Starr-Bochicchio, Thomas Kluyver, Jakub Wilk, Jackson Doak, Andrew Starr-Bochicchio
  • Date: 2013-12-06 20:20:26 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20131206202026-9k9oixbv7e8ke30q
Tags: 3.0-1
* Team upload.

[ Thomas Kluyver ]
* New upstream release. Closes: #729518
* Add packaging for Python 3.
* Use pybuild for packaging.

[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Jackson Doak ]
* Create debian/python3-pexpect.docs

[ Andrew Starr-Bochicchio ]
* Remove empty debian/patches dir.
* Move documentation and examples into a new python-pexpect-doc
  package. They are shared between the Python 2 and Python 3 packages,
  so there is no need to install them with both.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''This implements a virtual screen. This is used to support ANSI terminal
 
2
emulation. The screen representation and state is implemented in this class.
 
3
Most of the methods are inspired by ANSI screen control codes. The
 
4
:class:`~pexpect.ANSI.ANSI` class extends this class to add parsing of ANSI
 
5
escape codes.
 
6
 
 
7
PEXPECT LICENSE
 
8
 
 
9
    This license is approved by the OSI and FSF as GPL-compatible.
 
10
        http://opensource.org/licenses/isc-license.txt
 
11
 
 
12
    Copyright (c) 2012, Noah Spurrier <noah@noah.org>
 
13
    PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY
 
14
    PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE
 
15
    COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES.
 
16
    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 
17
    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
18
    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 
19
    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
20
    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
21
    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 
22
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
23
 
 
24
'''
 
25
 
 
26
import copy
 
27
 
 
28
NUL = 0    # Fill character; ignored on input.
 
29
ENQ = 5    # Transmit answerback message.
 
30
BEL = 7    # Ring the bell.
 
31
BS  = 8    # Move cursor left.
 
32
HT  = 9    # Move cursor to next tab stop.
 
33
LF = 10    # Line feed.
 
34
VT = 11    # Same as LF.
 
35
FF = 12    # Same as LF.
 
36
CR = 13    # Move cursor to left margin or newline.
 
37
SO = 14    # Invoke G1 character set.
 
38
SI = 15    # Invoke G0 character set.
 
39
XON = 17   # Resume transmission.
 
40
XOFF = 19  # Halt transmission.
 
41
CAN = 24   # Cancel escape sequence.
 
42
SUB = 26   # Same as CAN.
 
43
ESC = 27   # Introduce a control sequence.
 
44
DEL = 127  # Fill character; ignored on input.
 
45
SPACE = chr(32) # Space or blank character.
 
46
 
 
47
def constrain (n, min, max):
 
48
 
 
49
    '''This returns a number, n constrained to the min and max bounds. '''
 
50
 
 
51
    if n < min:
 
52
        return min
 
53
    if n > max:
 
54
        return max
 
55
    return n
 
56
 
 
57
class screen:
 
58
    '''This object maintains the state of a virtual text screen as a
 
59
    rectangluar array. This maintains a virtual cursor position and handles
 
60
    scrolling as characters are added. This supports most of the methods needed
 
61
    by an ANSI text screen. Row and column indexes are 1-based (not zero-based,
 
62
    like arrays). '''
 
63
 
 
64
    def __init__ (self, r=24,c=80):
 
65
        '''This initializes a blank screen of the given dimensions.'''
 
66
 
 
67
        self.rows = r
 
68
        self.cols = c
 
69
        self.cur_r = 1
 
70
        self.cur_c = 1
 
71
        self.cur_saved_r = 1
 
72
        self.cur_saved_c = 1
 
73
        self.scroll_row_start = 1
 
74
        self.scroll_row_end = self.rows
 
75
        self.w = [ [SPACE] * self.cols for c in range(self.rows)]
 
76
 
 
77
    def __str__ (self):
 
78
        '''This returns a printable representation of the screen. The end of
 
79
        each screen line is terminated by a newline. '''
 
80
 
 
81
        return '\n'.join ([ ''.join(c) for c in self.w ])
 
82
 
 
83
    def dump (self):
 
84
        '''This returns a copy of the screen as a string. This is similar to
 
85
        __str__ except that lines are not terminated with line feeds. '''
 
86
 
 
87
        return ''.join ([ ''.join(c) for c in self.w ])
 
88
 
 
89
    def pretty (self):
 
90
        '''This returns a copy of the screen as a string with an ASCII text box
 
91
        around the screen border. This is similar to __str__ except that it
 
92
        adds a box. '''
 
93
 
 
94
        top_bot = '+' + '-'*self.cols + '+\n'
 
95
        return top_bot + '\n'.join(['|'+line+'|' for line in str(self).split('\n')]) + '\n' + top_bot
 
96
 
 
97
    def fill (self, ch=SPACE):
 
98
 
 
99
        self.fill_region (1,1,self.rows,self.cols, ch)
 
100
 
 
101
    def fill_region (self, rs,cs, re,ce, ch=SPACE):
 
102
 
 
103
        rs = constrain (rs, 1, self.rows)
 
104
        re = constrain (re, 1, self.rows)
 
105
        cs = constrain (cs, 1, self.cols)
 
106
        ce = constrain (ce, 1, self.cols)
 
107
        if rs > re:
 
108
            rs, re = re, rs
 
109
        if cs > ce:
 
110
            cs, ce = ce, cs
 
111
        for r in range (rs, re+1):
 
112
            for c in range (cs, ce + 1):
 
113
                self.put_abs (r,c,ch)
 
114
 
 
115
    def cr (self):
 
116
        '''This moves the cursor to the beginning (col 1) of the current row.
 
117
        '''
 
118
 
 
119
        self.cursor_home (self.cur_r, 1)
 
120
 
 
121
    def lf (self):
 
122
        '''This moves the cursor down with scrolling.
 
123
        '''
 
124
 
 
125
        old_r = self.cur_r
 
126
        self.cursor_down()
 
127
        if old_r == self.cur_r:
 
128
            self.scroll_up ()
 
129
            self.erase_line()
 
130
 
 
131
    def crlf (self):
 
132
        '''This advances the cursor with CRLF properties.
 
133
        The cursor will line wrap and the screen may scroll.
 
134
        '''
 
135
 
 
136
        self.cr ()
 
137
        self.lf ()
 
138
 
 
139
    def newline (self):
 
140
        '''This is an alias for crlf().
 
141
        '''
 
142
 
 
143
        self.crlf()
 
144
 
 
145
    def put_abs (self, r, c, ch):
 
146
        '''Screen array starts at 1 index.'''
 
147
 
 
148
        r = constrain (r, 1, self.rows)
 
149
        c = constrain (c, 1, self.cols)
 
150
        ch = str(ch)[0]
 
151
        self.w[r-1][c-1] = ch
 
152
 
 
153
    def put (self, ch):
 
154
        '''This puts a characters at the current cursor position.
 
155
        '''
 
156
 
 
157
        self.put_abs (self.cur_r, self.cur_c, ch)
 
158
 
 
159
    def insert_abs (self, r, c, ch):
 
160
        '''This inserts a character at (r,c). Everything under
 
161
        and to the right is shifted right one character.
 
162
        The last character of the line is lost.
 
163
        '''
 
164
 
 
165
        r = constrain (r, 1, self.rows)
 
166
        c = constrain (c, 1, self.cols)
 
167
        for ci in range (self.cols, c, -1):
 
168
            self.put_abs (r,ci, self.get_abs(r,ci-1))
 
169
        self.put_abs (r,c,ch)
 
170
 
 
171
    def insert (self, ch):
 
172
 
 
173
        self.insert_abs (self.cur_r, self.cur_c, ch)
 
174
 
 
175
    def get_abs (self, r, c):
 
176
 
 
177
        r = constrain (r, 1, self.rows)
 
178
        c = constrain (c, 1, self.cols)
 
179
        return self.w[r-1][c-1]
 
180
 
 
181
    def get (self):
 
182
 
 
183
        self.get_abs (self.cur_r, self.cur_c)
 
184
 
 
185
    def get_region (self, rs,cs, re,ce):
 
186
        '''This returns a list of lines representing the region.
 
187
        '''
 
188
 
 
189
        rs = constrain (rs, 1, self.rows)
 
190
        re = constrain (re, 1, self.rows)
 
191
        cs = constrain (cs, 1, self.cols)
 
192
        ce = constrain (ce, 1, self.cols)
 
193
        if rs > re:
 
194
            rs, re = re, rs
 
195
        if cs > ce:
 
196
            cs, ce = ce, cs
 
197
        sc = []
 
198
        for r in range (rs, re+1):
 
199
            line = ''
 
200
            for c in range (cs, ce + 1):
 
201
                ch = self.get_abs (r,c)
 
202
                line = line + ch
 
203
            sc.append (line)
 
204
        return sc
 
205
 
 
206
    def cursor_constrain (self):
 
207
        '''This keeps the cursor within the screen area.
 
208
        '''
 
209
 
 
210
        self.cur_r = constrain (self.cur_r, 1, self.rows)
 
211
        self.cur_c = constrain (self.cur_c, 1, self.cols)
 
212
 
 
213
    def cursor_home (self, r=1, c=1): # <ESC>[{ROW};{COLUMN}H
 
214
 
 
215
        self.cur_r = r
 
216
        self.cur_c = c
 
217
        self.cursor_constrain ()
 
218
 
 
219
    def cursor_back (self,count=1): # <ESC>[{COUNT}D (not confused with down)
 
220
 
 
221
        self.cur_c = self.cur_c - count
 
222
        self.cursor_constrain ()
 
223
 
 
224
    def cursor_down (self,count=1): # <ESC>[{COUNT}B (not confused with back)
 
225
 
 
226
        self.cur_r = self.cur_r + count
 
227
        self.cursor_constrain ()
 
228
 
 
229
    def cursor_forward (self,count=1): # <ESC>[{COUNT}C
 
230
 
 
231
        self.cur_c = self.cur_c + count
 
232
        self.cursor_constrain ()
 
233
 
 
234
    def cursor_up (self,count=1): # <ESC>[{COUNT}A
 
235
 
 
236
        self.cur_r = self.cur_r - count
 
237
        self.cursor_constrain ()
 
238
 
 
239
    def cursor_up_reverse (self): # <ESC> M   (called RI -- Reverse Index)
 
240
 
 
241
        old_r = self.cur_r
 
242
        self.cursor_up()
 
243
        if old_r == self.cur_r:
 
244
            self.scroll_up()
 
245
 
 
246
    def cursor_force_position (self, r, c): # <ESC>[{ROW};{COLUMN}f
 
247
        '''Identical to Cursor Home.'''
 
248
 
 
249
        self.cursor_home (r, c)
 
250
 
 
251
    def cursor_save (self): # <ESC>[s
 
252
        '''Save current cursor position.'''
 
253
 
 
254
        self.cursor_save_attrs()
 
255
 
 
256
    def cursor_unsave (self): # <ESC>[u
 
257
        '''Restores cursor position after a Save Cursor.'''
 
258
 
 
259
        self.cursor_restore_attrs()
 
260
 
 
261
    def cursor_save_attrs (self): # <ESC>7
 
262
        '''Save current cursor position.'''
 
263
 
 
264
        self.cur_saved_r = self.cur_r
 
265
        self.cur_saved_c = self.cur_c
 
266
 
 
267
    def cursor_restore_attrs (self): # <ESC>8
 
268
        '''Restores cursor position after a Save Cursor.'''
 
269
 
 
270
        self.cursor_home (self.cur_saved_r, self.cur_saved_c)
 
271
 
 
272
    def scroll_constrain (self):
 
273
        '''This keeps the scroll region within the screen region.'''
 
274
 
 
275
        if self.scroll_row_start <= 0:
 
276
            self.scroll_row_start = 1
 
277
        if self.scroll_row_end > self.rows:
 
278
            self.scroll_row_end = self.rows
 
279
 
 
280
    def scroll_screen (self): # <ESC>[r
 
281
        '''Enable scrolling for entire display.'''
 
282
 
 
283
        self.scroll_row_start = 1
 
284
        self.scroll_row_end = self.rows
 
285
 
 
286
    def scroll_screen_rows (self, rs, re): # <ESC>[{start};{end}r
 
287
        '''Enable scrolling from row {start} to row {end}.'''
 
288
 
 
289
        self.scroll_row_start = rs
 
290
        self.scroll_row_end = re
 
291
        self.scroll_constrain()
 
292
 
 
293
    def scroll_down (self): # <ESC>D
 
294
        '''Scroll display down one line.'''
 
295
 
 
296
        # Screen is indexed from 1, but arrays are indexed from 0.
 
297
        s = self.scroll_row_start - 1
 
298
        e = self.scroll_row_end - 1
 
299
        self.w[s+1:e+1] = copy.deepcopy(self.w[s:e])
 
300
 
 
301
    def scroll_up (self): # <ESC>M
 
302
        '''Scroll display up one line.'''
 
303
 
 
304
        # Screen is indexed from 1, but arrays are indexed from 0.
 
305
        s = self.scroll_row_start - 1
 
306
        e = self.scroll_row_end - 1
 
307
        self.w[s:e] = copy.deepcopy(self.w[s+1:e+1])
 
308
 
 
309
    def erase_end_of_line (self): # <ESC>[0K -or- <ESC>[K
 
310
        '''Erases from the current cursor position to the end of the current
 
311
        line.'''
 
312
 
 
313
        self.fill_region (self.cur_r, self.cur_c, self.cur_r, self.cols)
 
314
 
 
315
    def erase_start_of_line (self): # <ESC>[1K
 
316
        '''Erases from the current cursor position to the start of the current
 
317
        line.'''
 
318
 
 
319
        self.fill_region (self.cur_r, 1, self.cur_r, self.cur_c)
 
320
 
 
321
    def erase_line (self): # <ESC>[2K
 
322
        '''Erases the entire current line.'''
 
323
 
 
324
        self.fill_region (self.cur_r, 1, self.cur_r, self.cols)
 
325
 
 
326
    def erase_down (self): # <ESC>[0J -or- <ESC>[J
 
327
        '''Erases the screen from the current line down to the bottom of the
 
328
        screen.'''
 
329
 
 
330
        self.erase_end_of_line ()
 
331
        self.fill_region (self.cur_r + 1, 1, self.rows, self.cols)
 
332
 
 
333
    def erase_up (self): # <ESC>[1J
 
334
        '''Erases the screen from the current line up to the top of the
 
335
        screen.'''
 
336
 
 
337
        self.erase_start_of_line ()
 
338
        self.fill_region (self.cur_r-1, 1, 1, self.cols)
 
339
 
 
340
    def erase_screen (self): # <ESC>[2J
 
341
        '''Erases the screen with the background color.'''
 
342
 
 
343
        self.fill ()
 
344
 
 
345
    def set_tab (self): # <ESC>H
 
346
        '''Sets a tab at the current position.'''
 
347
 
 
348
        pass
 
349
 
 
350
    def clear_tab (self): # <ESC>[g
 
351
        '''Clears tab at the current position.'''
 
352
 
 
353
        pass
 
354
 
 
355
    def clear_all_tabs (self): # <ESC>[3g
 
356
        '''Clears all tabs.'''
 
357
 
 
358
        pass
 
359
 
 
360
#        Insert line             Esc [ Pn L
 
361
#        Delete line             Esc [ Pn M
 
362
#        Delete character        Esc [ Pn P
 
363
#        Scrolling region        Esc [ Pn(top);Pn(bot) r
 
364