Skip to content

Commit c92b043

Browse files
yank working!
1 parent 60a0687 commit c92b043

File tree

3 files changed

+70
-18
lines changed

3 files changed

+70
-18
lines changed

bpython/curtsiesfrontend/manual_readline.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class AbstractEdits(object):
1717
'line': 'hello world',
1818
'cursor_offset': 5,
1919
'cut_buffer': 'there',
20-
'indent': 4,
2120
}
2221

2322
def __contains__(self, key):
@@ -28,15 +27,22 @@ def __contains__(self, key):
2827
else:
2928
return True
3029

31-
def add(self, key, func):
30+
def add(self, key, func, overwrite=False):
3231
if key in self:
33-
raise ValueError('key %r already has a mapping' % (key,))
32+
if overwrite:
33+
del self[key]
34+
else:
35+
raise ValueError('key %r already has a mapping' % (key,))
3436
params = inspect.getargspec(func)[0]
3537
args = dict((k, v) for k, v in self.default_kwargs.items() if k in params)
3638
r = func(**args)
3739
if len(r) == 2:
40+
if hasattr(func, 'kills'):
41+
raise ValueError('function %r returns two values, but has a kills attribute' % (func,))
3842
self.simple_edits[key] = func
3943
elif len(r) == 3:
44+
if not hasattr(func, 'kills'):
45+
raise ValueError('function %r returns three values, but has no kills attribute' % (func,))
4046
self.cut_buffer_edits[key] = func
4147
else:
4248
raise ValueError('return type of function %r not recognized' % (func,))
@@ -52,11 +58,20 @@ def call(self, key, **kwargs):
5258
args = dict((k, v) for k, v in kwargs.items() if k in params)
5359
return func(**args)
5460

61+
def call_without_cut(self, key, **kwargs):
62+
"""Looks up the function and calls it, returning only line and cursor offset"""
63+
r = self.call_for_two(key, **kwargs)
64+
return r[:2]
65+
5566
def __getitem__(self, key):
5667
if key in self.simple_edits: return self.simple_edits[key]
5768
if key in self.cut_buffer_edits: return self.cut_buffer_edits[key]
5869
raise KeyError("key %r not mapped" % (key,))
5970

71+
def __delitem__(self, key):
72+
if key in self.simple_edits: del self.simple_edits[key]
73+
elif key in self.cut_buffer_edits: del self.cut_buffer_edits[key]
74+
else: raise KeyError("key %r not mapped" % (key,))
6075

6176
class UnconfiguredEdits(AbstractEdits):
6277
"""Maps key to edit functions, and bins them by what parameters they take.
@@ -104,7 +119,8 @@ def __init__(self, simple_edits, cut_buffer_edits, awaiting_config, config, key_
104119
self.simple_edits = dict(simple_edits)
105120
self.cut_buffer_edits = dict(cut_buffer_edits)
106121
for attr, func in awaiting_config.items():
107-
super(ConfiguredEdits, self).add(key_dispatch[getattr(config, attr)], func)
122+
for key in key_dispatch[getattr(config, attr)]:
123+
super(ConfiguredEdits, self).add(key, func, overwrite=True)
108124

109125
def add_config_attr(self, config_attr, func):
110126
raise NotImplementedError("Config already set on this mapping")
@@ -117,6 +133,14 @@ def add(self, key, func):
117133
# Because the edits.on decorator runs the functions, functions which depend
118134
# on other functions must be declared after their dependencies
119135

136+
def kills_behind(func):
137+
func.kills = 'behind'
138+
return func
139+
140+
def kills_ahead(func):
141+
func.kills = 'ahead'
142+
return func
143+
120144
@edit_keys.on('<Ctrl-b>')
121145
@edit_keys.on('<LEFT>')
122146
def left_arrow(cursor_offset, line):
@@ -183,22 +207,29 @@ def delete_from_cursor_back(cursor_offset, line):
183207
return 0, line[cursor_offset:]
184208

185209
@edit_keys.on('<Esc+d>') # option-d
210+
@kills_ahead
186211
def delete_rest_of_word(cursor_offset, line):
187212
m = re.search(r'\w\b', line[cursor_offset:])
188213
if not m:
189-
return cursor_offset, line
190-
return cursor_offset, line[:cursor_offset] + line[m.start()+cursor_offset+1:]
214+
return cursor_offset, line, ''
215+
return (cursor_offset, line[:cursor_offset] + line[m.start()+cursor_offset+1:],
216+
line[cursor_offset:m.start()+cursor_offset+1])
191217

192218
@edit_keys.on('<Ctrl-w>')
193219
@edit_keys.on(config='clear_word_key')
220+
@kills_behind
194221
def delete_word_to_cursor(cursor_offset, line):
195222
matches = list(re.finditer(r'\s\S', line[:cursor_offset]))
196223
start = matches[-1].start()+1 if matches else 0
197-
return start, line[:start] + line[cursor_offset:]
224+
return start, line[:start] + line[cursor_offset:], line[start:cursor_offset]
198225

199226
@edit_keys.on('<Esc+y>')
200-
def yank_prev_prev_killed_text(cursor_offset, line):
201-
return cursor_offset, line #TODO Not implemented
227+
def yank_prev_prev_killed_text(cursor_offset, line, cut_buffer): #TODO not implemented - just prev
228+
return cursor_offset+len(cut_buffer), line[:cursor_offset] + cut_buffer + line[cursor_offset:]
229+
230+
@edit_keys.on(config='yank_from_buffer_key')
231+
def yank_prev_killed_text(cursor_offset, line, cut_buffer):
232+
return cursor_offset+len(cut_buffer), line[:cursor_offset] + cut_buffer + line[cursor_offset:]
202233

203234
@edit_keys.on('<Ctrl-t>')
204235
def transpose_character_before_cursor(cursor_offset, line):
@@ -223,21 +254,24 @@ def uppercase_next_word(cursor_offset, line):
223254
return cursor_offset, line #TODO Not implemented
224255

225256
@edit_keys.on('<Ctrl-k>')
257+
@kills_ahead
226258
def delete_from_cursor_forward(cursor_offset, line):
227-
return cursor_offset, line[:cursor_offset]
259+
return cursor_offset, line[:cursor_offset], line[cursor_offset:]
228260

229261
@edit_keys.on('<Esc+c>')
230262
def titlecase_next_word(cursor_offset, line):
231263
return cursor_offset, line #TODO Not implemented
232264

233265
@edit_keys.on('<Esc+BACKSPACE>')
234266
@edit_keys.on('<Meta-BACKSPACE>')
267+
@kills_behind
235268
def delete_word_from_cursor_back(cursor_offset, line):
236269
"""Whatever my option-delete does in bash on my mac"""
237270
if not line:
238271
return cursor_offset, line
239272
starts = [m.start() for m in list(re.finditer(r'\b\w', line)) if m.start() < cursor_offset]
240273
if starts:
241-
return starts[-1], line[:starts[-1]] + line[cursor_offset:]
242-
return cursor_offset, line
274+
return starts[-1], line[:starts[-1]] + line[cursor_offset:], line[starts[-1]:cursor_offset]
275+
return cursor_offset, line, ''
276+
243277

bpython/curtsiesfrontend/repl.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,17 @@ def process_key_event(self, e):
458458
self.up_one_line()
459459
elif e in ("<DOWN>",) + key_dispatch[self.config.down_one_line_key]:
460460
self.down_one_line()
461-
elif e in self.edit_keys:
462-
self.cursor_offset, self.current_line = self.edit_keys[e](self.cursor_offset, self.current_line)
461+
elif e in ("<Ctrl-d>",):
462+
self.on_control_d()
463+
elif e in self.edit_keys.cut_buffer_edits:
464+
self.readline_kill(e)
465+
elif e in self.edit_keys.simple_edits:
466+
self.cursor_offset, self.current_line = self.edit_keys.call(e,
467+
cursor_offset=self.cursor_offset,
468+
line=self.current_line,
469+
cut_buffer=self.cut_buffer)
463470
elif e in key_dispatch[self.config.cut_to_buffer_key]:
464471
self.cut_to_buffer()
465-
elif e in key_dispatch[self.config.yank_from_buffer_key]:
466-
self.yank_from_buffer()
467472
elif e in key_dispatch[self.config.reimport_key]:
468473
self.clear_modules_and_reevaluate()
469474
elif e in key_dispatch[self.config.toggle_file_watch_key]:
@@ -476,8 +481,6 @@ def process_key_event(self, e):
476481
self.pager(self.help_text())
477482
elif e in key_dispatch[self.config.suspend_key]:
478483
raise SystemExit()
479-
elif e in ("<Ctrl-d>",):
480-
self.on_control_d()
481484
elif e in key_dispatch[self.config.exit_key]:
482485
raise SystemExit()
483486
elif e in ("\n", "\r", "<PADENTER>", "<Ctrl-j>", "<Ctrl-m>"):
@@ -504,6 +507,16 @@ def process_key_event(self, e):
504507
else:
505508
self.add_normal_character(e)
506509

510+
def readline_kill(self, e):
511+
func = self.edit_keys[e]
512+
self.cursor_offset, self.current_line, cut = func(self.cursor_offset, self.current_line)
513+
if self.last_events[-2] == e: # consecutive kill commands are cumulative
514+
if func.kills == 'ahead': self.cut_buffer += cut
515+
elif func.kills == 'behind': self.cut_buffer = cut + self.cut_buffer
516+
else: raise ValueError("cut had value other than 'ahead' or 'behind'")
517+
else:
518+
self.cut_buffer = cut
519+
507520
def on_enter(self, insert_into_history=True):
508521
self.cursor_offset = -1 # so the cursor isn't touching a paren
509522
self.unhighlight_paren() # in unhighlight_paren

bpython/test/test_manual_readline.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ class config: att = 'c'
243243
self.assertEqual(configured_edits.call('c', cursor_offset=5, line='asfd'),
244244
('hi', 2))
245245

246+
def test_actual(self):
247+
class config: att = 'c'
248+
key_dispatch = {'c': 'c'}
249+
configured_edits = self.edits.mapping_with_config(config, key_dispatch)
250+
246251

247252
if __name__ == '__main__':
248253
unittest.main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy