|
1 # Copyright 2000-2004 Michael Hudson mwh@python.net |
|
2 # |
|
3 # All Rights Reserved |
|
4 # |
|
5 # Portions Copyright (c) 2005 Nokia Corporation |
|
6 # |
|
7 # Permission to use, copy, modify, and distribute this software and |
|
8 # its documentation for any purpose is hereby granted without fee, |
|
9 # provided that the above copyright notice appear in all copies and |
|
10 # that both that copyright notice and this permission notice appear in |
|
11 # supporting documentation. |
|
12 # |
|
13 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO |
|
14 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
|
15 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, |
|
16 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
|
17 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF |
|
18 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
19 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
20 |
|
21 import ascii |
|
22 import sys, os |
|
23 |
|
24 # Catgories of actions: |
|
25 # killing |
|
26 # yanking |
|
27 # motion |
|
28 # editing |
|
29 # history |
|
30 # finishing |
|
31 # [completion] |
|
32 |
|
33 class Command(object): |
|
34 finish = 0 |
|
35 kills_digit_arg = 1 |
|
36 def __init__(self, reader, (event_name, event)): |
|
37 self.reader = reader |
|
38 self.event = event |
|
39 self.event_name = event_name |
|
40 def do(self): |
|
41 pass |
|
42 |
|
43 class KillCommand(Command): |
|
44 def kill_range(self, start, end): |
|
45 if start == end: |
|
46 return |
|
47 r = self.reader |
|
48 b = r.buffer |
|
49 text = b[start:end] |
|
50 del b[start:end] |
|
51 if is_kill(r.last_command): |
|
52 if start < r.pos: |
|
53 r.kill_ring[-1] = text + r.kill_ring[-1] |
|
54 else: |
|
55 r.kill_ring[-1] = r.kill_ring[-1] + text |
|
56 else: |
|
57 r.kill_ring.append(text) |
|
58 r.pos = start |
|
59 r.dirty = 1 |
|
60 |
|
61 class YankCommand(Command): |
|
62 pass |
|
63 |
|
64 class MotionCommand(Command): |
|
65 pass |
|
66 |
|
67 class EditCommand(Command): |
|
68 pass |
|
69 |
|
70 class FinishCommand(Command): |
|
71 finish = 1 |
|
72 pass |
|
73 |
|
74 def is_kill(command): |
|
75 return command and issubclass(command, KillCommand) |
|
76 |
|
77 def is_yank(command): |
|
78 return command and issubclass(command, YankCommand) |
|
79 |
|
80 # etc |
|
81 |
|
82 class digit_arg(Command): |
|
83 kills_digit_arg = 0 |
|
84 def do(self): |
|
85 r = self.reader |
|
86 c = self.event[-1] |
|
87 if c == "-": |
|
88 if r.arg is not None: |
|
89 r.arg = -r.arg |
|
90 else: |
|
91 r.arg = -1 |
|
92 else: |
|
93 d = int(c) |
|
94 if r.arg is None: |
|
95 r.arg = d |
|
96 else: |
|
97 if r.arg < 0: |
|
98 r.arg = 10*r.arg - d |
|
99 else: |
|
100 r.arg = 10*r.arg + d |
|
101 r.dirty = 1 |
|
102 |
|
103 class clear_screen(Command): |
|
104 def do(self): |
|
105 r = self.reader |
|
106 r.console.clear() |
|
107 r.dirty = 1 |
|
108 |
|
109 class refresh(Command): |
|
110 def do(self): |
|
111 self.reader.dirty = 1 |
|
112 |
|
113 class repaint(Command): |
|
114 def do(self): |
|
115 self.reader.dirty = 1 |
|
116 self.reader.console.repaint_prep() |
|
117 |
|
118 class kill_line(KillCommand): |
|
119 def do(self): |
|
120 r = self.reader |
|
121 b = r.buffer |
|
122 eol = r.eol() |
|
123 for c in b[r.pos:eol]: |
|
124 if not ascii.isspace(c): |
|
125 self.kill_range(r.pos, eol) |
|
126 return |
|
127 else: |
|
128 self.kill_range(r.pos, eol+1) |
|
129 |
|
130 class unix_line_discard(KillCommand): |
|
131 def do(self): |
|
132 r = self.reader |
|
133 self.kill_range(r.bol(), r.pos) |
|
134 |
|
135 # XXX unix_word_rubout and backward_kill_word should actually |
|
136 # do different things... |
|
137 |
|
138 class unix_word_rubout(KillCommand): |
|
139 def do(self): |
|
140 r = self.reader |
|
141 for i in range(r.get_arg()): |
|
142 self.kill_range(r.bow(), r.pos) |
|
143 |
|
144 class kill_word(KillCommand): |
|
145 def do(self): |
|
146 r = self.reader |
|
147 for i in range(r.get_arg()): |
|
148 self.kill_range(r.pos, r.eow()) |
|
149 |
|
150 class backward_kill_word(KillCommand): |
|
151 def do(self): |
|
152 r = self.reader |
|
153 for i in range(r.get_arg()): |
|
154 self.kill_range(r.bow(), r.pos) |
|
155 |
|
156 class yank(YankCommand): |
|
157 def do(self): |
|
158 r = self.reader |
|
159 if not r.kill_ring: |
|
160 r.error("nothing to yank") |
|
161 return |
|
162 r.insert(r.kill_ring[-1]) |
|
163 |
|
164 class yank_pop(YankCommand): |
|
165 def do(self): |
|
166 r = self.reader |
|
167 b = r.buffer |
|
168 if not r.kill_ring: |
|
169 r.error("nothing to yank") |
|
170 return |
|
171 if not is_yank(r.last_command): |
|
172 r.error("previous command was not a yank") |
|
173 return |
|
174 repl = len(r.kill_ring[-1]) |
|
175 r.kill_ring.insert(0, r.kill_ring.pop()) |
|
176 t = r.kill_ring[-1] |
|
177 b[r.pos - repl:r.pos] = t |
|
178 r.pos = r.pos - repl + len(t) |
|
179 r.dirty = 1 |
|
180 |
|
181 class interrupt(FinishCommand): |
|
182 def do(self): |
|
183 #import signal |
|
184 self.reader.console.finish() |
|
185 #os.kill(os.getpid(), signal.SIGINT) |
|
186 |
|
187 class suspend(Command): |
|
188 def do(self): |
|
189 raise "suspend not implemented" |
|
190 |
|
191 class up(MotionCommand): |
|
192 def do(self): |
|
193 r = self.reader |
|
194 for i in range(r.get_arg()): |
|
195 bol1 = r.bol() |
|
196 if bol1 == 0: |
|
197 r.pos = 0 |
|
198 r.error("start of buffer") |
|
199 return |
|
200 bol2 = r.bol(bol1-1) |
|
201 line_pos = r.pos - bol1 |
|
202 if line_pos > bol1 - bol2 - 1: |
|
203 r.sticky_y = line_pos |
|
204 r.pos = bol1 - 1 |
|
205 else: |
|
206 r.pos = bol2 + line_pos |
|
207 |
|
208 class down(MotionCommand): |
|
209 def do(self): |
|
210 r = self.reader |
|
211 b = r.buffer |
|
212 for i in range(r.get_arg()): |
|
213 bol1 = r.bol() |
|
214 eol1 = r.eol() |
|
215 if eol1 == len(b): |
|
216 r.pos = len(b) |
|
217 r.error("end of buffer") |
|
218 return |
|
219 eol2 = r.eol(eol1+1) |
|
220 if r.pos - bol1 > eol2 - eol1 - 1: |
|
221 r.pos = eol2 |
|
222 else: |
|
223 r.pos = eol1 + (r.pos - bol1) + 1 |
|
224 |
|
225 class left(MotionCommand): |
|
226 def do(self): |
|
227 r = self.reader |
|
228 for i in range(r.get_arg()): |
|
229 p = r.pos - 1 |
|
230 if p >= 0: |
|
231 r.pos = p |
|
232 else: |
|
233 self.reader.error("start of buffer") |
|
234 |
|
235 class right(MotionCommand): |
|
236 def do(self): |
|
237 r = self.reader |
|
238 b = r.buffer |
|
239 for i in range(r.get_arg()): |
|
240 p = r.pos + 1 |
|
241 if p <= len(b): |
|
242 r.pos = p |
|
243 else: |
|
244 self.reader.error("end of buffer") |
|
245 |
|
246 class beginning_of_line(MotionCommand): |
|
247 def do(self): |
|
248 self.reader.pos = self.reader.bol() |
|
249 |
|
250 class end_of_line(MotionCommand): |
|
251 def do(self): |
|
252 r = self.reader |
|
253 self.reader.pos = self.reader.eol() |
|
254 |
|
255 class home(MotionCommand): |
|
256 def do(self): |
|
257 self.reader.pos = 0 |
|
258 |
|
259 class end(MotionCommand): |
|
260 def do(self): |
|
261 self.reader.pos = len(self.reader.buffer) |
|
262 |
|
263 class forward_word(MotionCommand): |
|
264 def do(self): |
|
265 r = self.reader |
|
266 for i in range(r.get_arg()): |
|
267 r.pos = r.eow() |
|
268 |
|
269 class backward_word(MotionCommand): |
|
270 def do(self): |
|
271 r = self.reader |
|
272 for i in range(r.get_arg()): |
|
273 r.pos = r.bow() |
|
274 |
|
275 class self_insert(EditCommand): |
|
276 def do(self): |
|
277 r = self.reader |
|
278 r.insert(self.event * r.get_arg()) |
|
279 |
|
280 class insert_nl(EditCommand): |
|
281 def do(self): |
|
282 r = self.reader |
|
283 r.insert("\n" * r.get_arg()) |
|
284 |
|
285 class transpose_characters(EditCommand): |
|
286 def do(self): |
|
287 r = self.reader |
|
288 b = r.buffer |
|
289 s = r.pos - 1 |
|
290 if s < 0: |
|
291 r.error("cannot transpose at start of buffer") |
|
292 else: |
|
293 if s == len(b): |
|
294 s -= 1 |
|
295 t = min(s + r.get_arg(), len(b) - 1) |
|
296 c = b[s] |
|
297 del b[s] |
|
298 b.insert(t, c) |
|
299 r.pos = t |
|
300 r.dirty = 1 |
|
301 |
|
302 class backspace(EditCommand): |
|
303 def do(self): |
|
304 r = self.reader |
|
305 b = r.buffer |
|
306 for i in range(r.get_arg()): |
|
307 if r.pos > 0: |
|
308 r.pos -= 1 |
|
309 del b[r.pos] |
|
310 r.dirty = 1 |
|
311 else: |
|
312 self.reader.error("can't backspace at start") |
|
313 |
|
314 class delete(EditCommand): |
|
315 def do(self): |
|
316 r = self.reader |
|
317 b = r.buffer |
|
318 if ( r.pos == 0 and len(b) == 0 # this is something of a hack |
|
319 and self.event[-1] == "\004"): |
|
320 r.console.finish() |
|
321 raise EOFError |
|
322 for i in range(r.get_arg()): |
|
323 if r.pos != len(b): |
|
324 del b[r.pos] |
|
325 r.dirty = 1 |
|
326 else: |
|
327 self.reader.error("end of buffer") |
|
328 |
|
329 class accept(FinishCommand): |
|
330 def do(self): |
|
331 pass |
|
332 |
|
333 class help(Command): |
|
334 def do(self): |
|
335 self.reader.msg = self.reader.help_text |
|
336 self.reader.dirty = 1 |
|
337 |
|
338 class invalid_key(Command): |
|
339 def do(self): |
|
340 pending = self.reader.console.getpending() |
|
341 s = ''.join(self.event) + pending.data |
|
342 self.reader.error("`%r' not bound"%s) |
|
343 |
|
344 class invalid_command(Command): |
|
345 def do(self): |
|
346 s = self.event_name |
|
347 self.reader.error("command `%s' not known"%s) |
|
348 |
|
349 class qIHelp(Command): |
|
350 def do(self): |
|
351 r = self.reader |
|
352 r.insert((self.event + r.console.getpending().data) * r.get_arg()) |
|
353 r.pop_input_trans() |
|
354 |
|
355 from pyrepl import input |
|
356 |
|
357 class QITrans(object): |
|
358 def push(self, evt): |
|
359 self.evt = evt |
|
360 def get(self): |
|
361 return ('qIHelp', self.evt.raw) |
|
362 |
|
363 class quoted_insert(Command): |
|
364 kills_digit_arg = 0 |
|
365 def do(self): |
|
366 self.reader.push_input_trans(QITrans()) |
|
367 |
|
368 class save_history(Command): |
|
369 def do(self): |
|
370 self.reader.save_history() |
|
371 self.reader.message("history saved to file") |