1818# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1919# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2020
21- # Bah, this would be easier to test if curses/terminfo didn't have so
22- # much non-introspectable global state.
23-
2421from collections import deque
2522
2623from . import keymap
3128import os
3229
3330
34- _keynames = {
31+ # Mapping of human-readable key names to their terminal-specific codes
32+ TERMINAL_KEYNAMES = {
3533 "delete" : "kdch1" ,
3634 "down" : "kcud1" ,
3735 "end" : "kend" ,
4644}
4745
4846
49- #function keys x in 1-20 -> fX: kfX
50- _keynames .update ((' f%d' % i , ' kf%d' % i ) for i in range (1 , 21 ))
47+ # Function keys F1-F20 mapping
48+ TERMINAL_KEYNAMES .update ((" f%d" % i , " kf%d" % i ) for i in range (1 , 21 ))
5149
52- # this is a bit of a hack: CTRL-left and CTRL-right are not standardized
53- # termios sequences: each terminal emulator implements its own slightly
54- # different incarnation, and as far as I know, there is no way to know
55- # programmatically which sequences correspond to CTRL-left and
56- # CTRL-right. In bash, these keys usually work because there are bindings
57- # in ~/.inputrc, but pyrepl does not support it. The workaround is to
58- # hard-code here a bunch of known sequences, which will be seen as "ctrl
59- # left" and "ctrl right" keys, which can be finally be mapped to commands
60- # by the reader's keymaps.
61- #
62- CTRL_ARROW_KEYCODE = {
50+ # Known CTRL-arrow keycodes
51+ CTRL_ARROW_KEYCODES = {
6352 # for xterm, gnome-terminal, xfce terminal, etc.
6453 b'\033 [1;5D' : 'ctrl left' ,
6554 b'\033 [1;5C' : 'ctrl right' ,
6857 b'\033 Oc' : 'ctrl right' ,
6958}
7059
71- def general_keycodes ():
60+ def get_terminal_keycodes ():
61+ """
62+ Generates a dictionary mapping terminal keycodes to human-readable names.
63+ """
7264 keycodes = {}
73- for key , tiname in _keynames .items ():
74- keycode = curses .tigetstr (tiname )
65+ for key , terminal_code in TERMINAL_KEYNAMES .items ():
66+ keycode = curses .tigetstr (terminal_code )
7567 trace ('key {key} tiname {tiname} keycode {keycode!r}' , ** locals ())
7668 if keycode :
7769 keycodes [keycode ] = key
78- keycodes .update (CTRL_ARROW_KEYCODE )
70+ keycodes .update (CTRL_ARROW_KEYCODES )
7971 return keycodes
8072
81-
82- def EventQueue (fd , encoding ):
83- keycodes = general_keycodes ()
84- if os .isatty (fd ):
85- backspace = tcgetattr (fd )[6 ][VERASE ]
86- keycodes [backspace ] = 'backspace'
87- k = keymap .compile_keymap (keycodes )
88- trace ('keymap {k!r}' , k = k )
89- return EncodedQueue (k , encoding )
90-
91-
92- class EncodedQueue (object ):
93- def __init__ (self , keymap , encoding ):
94- self .k = self .ck = keymap
73+ class EventQueue (object ):
74+ def __init__ (self , fd , encoding ):
75+ self .keycodes = get_terminal_keycodes ()
76+ if os .isatty (fd ):
77+ backspace = tcgetattr (fd )[6 ][VERASE ]
78+ self .keycodes [backspace ] = "backspace"
79+ self .compiled_keymap = keymap .compile_keymap (self .keycodes )
80+ self .keymap = self .compiled_keymap
81+ trace ("keymap {k!r}" , k = self .keymap )
82+ self .encoding = encoding
9583 self .events = deque ()
9684 self .buf = bytearray ()
97- self .encoding = encoding
9885
9986 def get (self ):
87+ """
88+ Retrieves the next event from the queue.
89+ """
10090 if self .events :
10191 return self .events .popleft ()
10292 else :
10393 return None
10494
10595 def empty (self ):
96+ """
97+ Checks if the queue is empty.
98+ """
10699 return not self .events
107100
108101 def flush_buf (self ):
102+ """
103+ Flushes the buffer and returns its contents.
104+ """
109105 old = self .buf
110106 self .buf = bytearray ()
111107 return old
112108
113109 def insert (self , event ):
110+ """
111+ Inserts an event into the queue.
112+ """
114113 trace ('added event {event}' , event = event )
115114 self .events .append (event )
116115
117116 def push (self , char ):
117+ """
118+ Processes a character by updating the buffer and handling special key mappings.
119+ """
118120 ord_char = char if isinstance (char , int ) else ord (char )
119121 char = bytes (bytearray ((ord_char ,)))
120122 self .buf .append (ord_char )
121- if char in self .k :
122- if self .k is self .ck :
123+ if char in self .keymap :
124+ if self .keymap is self .compiled_keymap :
123125 #sanity check, buffer is empty when a special key comes
124126 assert len (self .buf ) == 1
125- k = self .k [char ]
127+ k = self .keymap [char ]
126128 trace ('found map {k!r}' , k = k )
127129 if isinstance (k , dict ):
128- self .k = k
130+ self .keymap = k
129131 else :
130132 self .insert (Event ('key' , k , self .flush_buf ()))
131- self .k = self .ck
133+ self .keymap = self .compiled_keymap
132134
133135 elif self .buf and self .buf [0 ] == 27 : # escape
134136 # escape sequence not recognized by our keymap: propagate it
135137 # outside so that i can be recognized as an M-... key (see also
136- # the docstring in keymap.py, in particular the line \\E.
138+ # the docstring in keymap.py
137139 trace ('unrecognized escape sequence, propagating...' )
138- self .k = self .ck
140+ self .keymap = self .compiled_keymap
139141 self .insert (Event ('key' , '\033 ' , bytearray (b'\033 ' )))
140142 for c in self .flush_buf ()[1 :]:
141143 self .push (chr (c ))
@@ -147,4 +149,4 @@ def push(self, char):
147149 return
148150 else :
149151 self .insert (Event ('key' , decoded , self .flush_buf ()))
150- self .k = self .ck
152+ self .keymap = self .compiled_keymap
0 commit comments