Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit64d875e

Browse files
Merge branch 'paste-detection'
2 parents2a9b51f +fa08b74 commit64d875e

File tree

4 files changed

+192
-13
lines changed

4 files changed

+192
-13
lines changed

‎bpython/curtsies.py‎

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__importabsolute_import
22

33
importcode
4+
importcollections
45
importio
56
importlogging
67
importsys
@@ -91,10 +92,45 @@ def main(args=None, locals_=None, banner=None, welcome_message=None):
9192
returnextract_exit_value(exit_value)
9293

9394

95+
def_combined_events(event_provider,paste_threshold):
96+
"""Combines consecutive keypress events into paste events."""
97+
timeout=yield'nonsense_event'# so send can be used immediately
98+
queue=collections.deque()
99+
whileTrue:
100+
e=event_provider.send(timeout)
101+
ifisinstance(e,curtsies.events.Event):
102+
timeout=yielde
103+
continue
104+
elifeisNone:
105+
timeout=yieldNone
106+
continue
107+
else:
108+
queue.append(e)
109+
e=event_provider.send(0)
110+
whilenot (eisNoneorisinstance(e,curtsies.events.Event)):
111+
queue.append(e)
112+
e=event_provider.send(0)
113+
iflen(queue)>=paste_threshold:
114+
paste=curtsies.events.PasteEvent()
115+
paste.events.extend(queue)
116+
queue.clear()
117+
timeout=yieldpaste
118+
else:
119+
whilelen(queue):
120+
timeout=yieldqueue.popleft()
121+
122+
123+
defcombined_events(event_provider,paste_threshold=3):
124+
g=_combined_events(event_provider,paste_threshold)
125+
next(g)
126+
returng
127+
128+
94129
defmainloop(config,locals_,banner,interp=None,paste=None,
95130
interactive=True):
96-
withcurtsies.input.Input(keynames='curtsies',sigint_event=True)as \
97-
input_generator:
131+
withcurtsies.input.Input(keynames='curtsies',
132+
sigint_event=True,
133+
paste_threshold=None)asinput_generator:
98134
withcurtsies.window.CursorAwareWindow(
99135
sys.stdout,
100136
sys.stdin,
@@ -180,12 +216,13 @@ def process_event(e):
180216

181217
# do a display before waiting for first event
182218
process_event(None)
219+
inputs=combined_events(input_generator)
183220
forunusedinfind_iterator:
184-
e=input_generator.send(0)
221+
e=inputs.send(0)
185222
ifeisnotNone:
186223
process_event(e)
187224

188-
foreininput_generator:
225+
foreininputs:
189226
process_event(e)
190227

191228

‎bpython/curtsiesfrontend/repl.py‎

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
Press {config.edit_config_key} to edit this config file.
8585
"""
8686
EXAMPLE_CONFIG_URL='https://raw.githubusercontent.com/bpython/bpython/master/bpython/sample-config'
87+
MAX_EVENTS_POSSIBLY_NOT_PASTE=20# more than this many events will be assumed to
88+
# be a true paste event, i.e. control characters
89+
# like '<Ctrl-a>' will be stripped
8790

8891
# This is needed for is_nop and should be removed once is_nop is fixed.
8992
ifpy3:
@@ -545,16 +548,25 @@ def process_control_event(self, e):
545548
ctrl_char=compress_paste_event(e)
546549
ifctrl_charisnotNone:
547550
returnself.process_event(ctrl_char)
548-
simple_events=just_simple_events(e.events)
549-
source=preprocess(''.join(simple_events),
550-
self.interp.compile)
551-
552551
withself.in_paste_mode():
553-
foreeinsource:
554-
ifself.stdin.has_focus:
555-
self.stdin.process_event(ee)
556-
else:
557-
self.process_simple_keypress(ee)
552+
# Might not really be a paste, UI might just be lagging
553+
if (len(e.events)<=MAX_EVENTS_POSSIBLY_NOT_PASTEand
554+
any(notis_simple_event(ee)foreeine.events)):
555+
foreeine.events:
556+
ifself.stdin.has_focus:
557+
self.stdin.process_event(ee)
558+
else:
559+
self.process_event(ee)
560+
else:
561+
simple_events=just_simple_events(e.events)
562+
source=preprocess(''.join(simple_events),
563+
self.interp.compile)
564+
foreeinsource:
565+
ifself.stdin.has_focus:
566+
self.stdin.process_event(ee)
567+
else:
568+
self.process_simple_keypress(ee)
569+
558570

559571
elifisinstance(e,bpythonevents.RunStartupFileEvent):
560572
try:
@@ -1619,11 +1631,24 @@ def just_simple_events(event_list):
16191631
pass# ignore events
16201632
elife=='<SPACE>':
16211633
simple_events.append(' ')
1634+
eliflen(e)>1:
1635+
pass# get rid of <Ctrl-a> etc.
16221636
else:
16231637
simple_events.append(e)
16241638
returnsimple_events
16251639

16261640

1641+
defis_simple_event(e):
1642+
ifisinstance(e,events.Event):
1643+
returnFalse
1644+
ifein ("<Ctrl-j>","<Ctrl-m>","<PADENTER>","\n","\r","<SPACE>"):
1645+
returnTrue
1646+
iflen(e)>1:
1647+
returnFalse
1648+
else:
1649+
returnTrue
1650+
1651+
16271652
# TODO this needs some work to function again and be useful for embedding
16281653
defsimple_repl():
16291654
refreshes= []

‎bpython/test/test_curtsies.py‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# coding: utf-8
2+
from __future__importunicode_literals
3+
4+
fromcollectionsimportnamedtuple
5+
6+
frombpython.curtsiesimportcombined_events
7+
frombpython.testimport (FixLanguageTestCaseasTestCase,unittest)
8+
9+
importcurtsies.events
10+
11+
12+
ScheduledEvent=namedtuple('ScheduledEvent', ['when','event'])
13+
14+
15+
classEventGenerator(object):
16+
def__init__(self,initial_events=(),scheduled_events=()):
17+
self._events= []
18+
self._current_tick=0
19+
foreininitial_events:
20+
self.schedule_event(e,0)
21+
fore,winscheduled_events:
22+
self.schedule_event(e,w)
23+
24+
defschedule_event(self,event,when):
25+
self._events.append(ScheduledEvent(when,event))
26+
self._events.sort()
27+
28+
defsend(self,timeout=None):
29+
iftimeoutnotin [None,0]:
30+
raiseValueError('timeout value %r not supported'%timeout)
31+
ifnotself._events:
32+
returnNone
33+
ifself._events[0].when<=self._current_tick:
34+
returnself._events.pop(0).event
35+
36+
iftimeout==0:
37+
returnNone
38+
eliftimeoutisNone:
39+
e=self._events.pop(0)
40+
self._current_tick=e.when
41+
returne.event
42+
else:
43+
raiseValueError('timeout value %r not supported'%timeout)
44+
45+
deftick(self,dt=1):
46+
self._current_tick+=dt
47+
returnself._current_tick
48+
49+
50+
classTestCurtsiesPasteDetection(TestCase):
51+
deftest_paste_threshold(self):
52+
eg=EventGenerator(list('abc'))
53+
cb=combined_events(eg,paste_threshold=3)
54+
e=next(cb)
55+
self.assertIsInstance(e,curtsies.events.PasteEvent)
56+
self.assertEqual(e.events,list('abc'))
57+
self.assertEqual(next(cb),None)
58+
59+
eg=EventGenerator(list('abc'))
60+
cb=combined_events(eg,paste_threshold=4)
61+
self.assertEqual(next(cb),'a')
62+
self.assertEqual(next(cb),'b')
63+
self.assertEqual(next(cb),'c')
64+
self.assertEqual(next(cb),None)
65+
66+
deftest_set_timeout(self):
67+
eg=EventGenerator('a',zip('bcdefg', [1,2,3,3,3,4]))
68+
eg.schedule_event(curtsies.events.SigIntEvent(),5)
69+
eg.schedule_event('h',6)
70+
cb=combined_events(eg,paste_threshold=3)
71+
self.assertEqual(next(cb),'a')
72+
self.assertEqual(cb.send(0),None)
73+
self.assertEqual(next(cb),'b')
74+
self.assertEqual(cb.send(0),None)
75+
eg.tick()
76+
self.assertEqual(cb.send(0),'c')
77+
self.assertEqual(cb.send(0),None)
78+
eg.tick()
79+
self.assertIsInstance(cb.send(0),curtsies.events.PasteEvent)
80+
self.assertEqual(cb.send(0),None)
81+
self.assertEqual(cb.send(None),'g')
82+
self.assertEqual(cb.send(0),None)
83+
eg.tick(1)
84+
self.assertIsInstance(cb.send(0),curtsies.events.SigIntEvent)
85+
self.assertEqual(cb.send(0),None)
86+
self.assertEqual(cb.send(None),'h')
87+
self.assertEqual(cb.send(None),None)
88+
89+
90+
if__name__=='__main__':
91+
unittest.main()

‎bpython/test/test_curtsies_repl.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
frombpython.testimport (FixLanguageTestCaseasTestCase,MagicIterMock,mock,
2121
builtin_target,unittest)
2222

23+
fromcurtsiesimportevents
24+
2325
ifpy3:
2426
fromimportlibimportinvalidate_caches
2527
else:
@@ -422,5 +424,29 @@ def test_startup_event_latin1(self):
422424
self.assertIn('a',self.repl.interp.locals)
423425

424426

427+
classTestCurtsiesPasteEvents(TestCase):
428+
429+
defsetUp(self):
430+
self.repl=create_repl()
431+
432+
deftest_control_events_in_small_paste(self):
433+
self.assertGreaterEqual(curtsiesrepl.MAX_EVENTS_POSSIBLY_NOT_PASTE,6,
434+
'test assumes UI lag could cause 6 events')
435+
p=events.PasteEvent()
436+
p.events= ['a','b','c','d','<Ctrl-a>','e']
437+
self.repl.process_event(p)
438+
self.assertEqual(self.repl.current_line,'eabcd')
439+
440+
441+
deftest_control_events_in_large_paste(self):
442+
"""Large paste events should ignore control characters"""
443+
p=events.PasteEvent()
444+
p.events= (['a','<Ctrl-a>']+
445+
['e']*curtsiesrepl.MAX_EVENTS_POSSIBLY_NOT_PASTE)
446+
self.repl.process_event(p)
447+
self.assertEqual(self.repl.current_line,
448+
'a'+'e'*curtsiesrepl.MAX_EVENTS_POSSIBLY_NOT_PASTE)
449+
450+
425451
if__name__=='__main__':
426452
unittest.main()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp