26
26
PROMPT_UNDERLINE = '\x01 %s\x02 ' % ansi .UNDERLINE
27
27
PROMPT_REVERSE = '\x01 %s\x02 ' % ansi .REVERSE
28
28
29
+ DEFAULT_TERM_WIDTH = 80
30
+ DEFAULT_MATCH_LINE_LIMIT = 10
31
+
32
+
33
+ def _GetTerminalWidth ():
34
+ # type: () -> int
35
+ try :
36
+ return libc .get_terminal_width ()
37
+ except (IOError , OSError ):
38
+ # This shouldn't raise IOError because we did it at startup! Under
39
+ # rare circumstances stdin can change, e.g. if you do exec <&
40
+ # input.txt. So we have a fallback.
41
+ return DEFAULT_TERM_WIDTH
42
+
29
43
30
44
def _PromptLen (prompt_str ):
31
45
# type: (str) -> int
@@ -85,13 +99,26 @@ def __init__(self):
85
99
class _IDisplay (object ):
86
100
"""Interface for completion displays."""
87
101
88
- def __init__ (self , comp_state , prompt_state , num_lines_cap , f , debug_f ):
89
- # type: (State, PromptState, int, mylib.Writer, _DebugFile) -> None
102
+ def __init__ (self , comp_state , prompt_state , num_lines_cap , f , debug_f , signal_safe ):
103
+ # type: (State, PromptState, int, mylib.Writer, _DebugFile, iolib.SignalSafe ) -> None
90
104
self .comp_state = comp_state
91
105
self .prompt_state = prompt_state
92
106
self .num_lines_cap = num_lines_cap
93
107
self .f = f
94
108
self .debug_f = debug_f
109
+ self .term_width = _GetTerminalWidth ()
110
+ self .signal_safe = signal_safe
111
+
112
+ def _GetTermWidth (self ):
113
+ # type: () -> int
114
+ if self .signal_safe .PollSigWinch (): # is our value dirty?
115
+ self .term_width = _GetTerminalWidth ()
116
+
117
+ return self .term_width
118
+
119
+ def ReadlineInitCommands (self ):
120
+ # type: () -> List[str]
121
+ return []
95
122
96
123
def PrintCandidates (self , unused_subst , matches , unused_match_len ):
97
124
# type: (Optional[str], List[str], int) -> None
@@ -142,10 +169,11 @@ class MinimalDisplay(_IDisplay):
142
169
without testing it.
143
170
"""
144
171
145
- def __init__ (self , comp_state , prompt_state , debug_f ):
146
- # type: (State, PromptState, _DebugFile) -> None
147
- _IDisplay .__init__ (self , comp_state , prompt_state , 10 , mylib .Stdout (),
148
- debug_f )
172
+ def __init__ (self , comp_state , prompt_state , debug_f , signal_safe ):
173
+ # type: (State, PromptState, _DebugFile, iolib.SignalSafe) -> None
174
+ _IDisplay .__init__ (self , comp_state , prompt_state ,
175
+ DEFAULT_MATCH_LINE_LIMIT , mylib .Stdout (), debug_f ,
176
+ signal_safe )
149
177
150
178
def _RedrawPrompt (self ):
151
179
# type: () -> None
@@ -161,22 +189,11 @@ def _PrintCandidates(self, unused_subst, matches, unused_match_len):
161
189
display_pos = self .comp_state .display_pos
162
190
assert display_pos != - 1
163
191
164
- too_many = False
165
- i = 0
166
- for m in matches :
167
- self .f .write (' %s\n ' % m [display_pos :])
168
-
169
- if i == self .num_lines_cap :
170
- too_many = True
171
- i += 1 # Count this one
172
- break
173
-
174
- i += 1
175
-
176
- if too_many :
177
- num_left = len (matches ) - i
178
- if num_left :
179
- self .f .write (' ... and %d more\n ' % num_left )
192
+ to_display = [m [display_pos :] for m in matches ]
193
+ lens = [len (m ) for m in to_display ]
194
+ max_match_len = max (lens )
195
+ term_width = self ._GetTermWidth ()
196
+ _PrintPacked (to_display , max_match_len , term_width , self .num_lines_cap , self .f )
180
197
181
198
self ._RedrawPrompt ()
182
199
@@ -309,7 +326,6 @@ class NiceDisplay(_IDisplay):
309
326
310
327
def __init__ (
311
328
self ,
312
- term_width , # type: int
313
329
comp_state , # type: State
314
330
prompt_state , # type: PromptState
315
331
debug_f , # type: _DebugFile
@@ -321,13 +337,11 @@ def __init__(
321
337
Args:
322
338
bold_line: Should user's entry be bold?
323
339
"""
324
- _IDisplay .__init__ (self , comp_state , prompt_state , 10 , mylib .Stdout (),
325
- debug_f )
326
-
327
- self .term_width = term_width # initial terminal width; will be invalidated
340
+ _IDisplay .__init__ (self , comp_state , prompt_state ,
341
+ DEFAULT_MATCH_LINE_LIMIT , mylib .Stdout (), debug_f ,
342
+ signal_safe )
328
343
329
344
self .readline = readline
330
- self .signal_safe = signal_safe
331
345
332
346
self .bold_line = False
333
347
@@ -340,6 +354,12 @@ def __init__(
340
354
# hash of matches -> count. Has exactly ONE entry at a time.
341
355
self .dupes = {} # type: Dict[int, int]
342
356
357
+ def ReadlineInitCommands (self ):
358
+ # type: () -> List[str]
359
+ # NOTE: This setting prevents line-wrapping from clobbering completion
360
+ # output. See https://github.com/oils-for-unix/oils/issues/257
361
+ return ['set horizontal-scroll-mode on' ]
362
+
343
363
def Reset (self ):
344
364
# type: () -> None
345
365
"""Call this in between commands."""
@@ -360,7 +380,7 @@ def _ReturnToPrompt(self, num_lines):
360
380
361
381
# Go right, but not more than the terminal width.
362
382
n = orig_len + last_prompt_len
363
- n = n % self ._GetTerminalWidth ()
383
+ n = n % self ._GetTermWidth ()
364
384
self .f .write ('\x1b [%dC' % n ) # RIGHT
365
385
366
386
if self .bold_line :
@@ -370,7 +390,7 @@ def _ReturnToPrompt(self, num_lines):
370
390
371
391
def _PrintCandidates (self , unused_subst , matches , unused_max_match_len ):
372
392
# type: (Optional[str], List[str], int) -> None
373
- term_width = self ._GetTerminalWidth ()
393
+ term_width = self ._GetTermWidth ()
374
394
375
395
# Variables set by the completion generator. They should always exist,
376
396
# because we can't get "matches" without calling that function.
@@ -450,7 +470,7 @@ def PrintRequired(self, msg, *args):
450
470
#log('PrintOptional %r', msg, file=DEBUG_F)
451
471
452
472
# Truncate to terminal width
453
- max_len = self ._GetTerminalWidth () - 2
473
+ max_len = self ._GetTermWidth () - 2
454
474
if len (msg ) > max_len :
455
475
msg = msg [:max_len - 5 ] + ' ... '
456
476
@@ -471,7 +491,7 @@ def PrintOptional(self, msg, *args):
471
491
472
492
def ShowPromptOnRight (self , rendered ):
473
493
# type: (str) -> None
474
- n = self ._GetTerminalWidth () - 2 - len (rendered )
494
+ n = self ._GetTermWidth () - 2 - len (rendered )
475
495
spaces = ' ' * n
476
496
477
497
# We avoid drawing problems if we print it on its own line:
@@ -512,18 +532,6 @@ def EraseLines(self):
512
532
self .f .write ('\x1b [%dA' % n )
513
533
self .f .flush () # Without this, output will look messed up
514
534
515
- def _GetTerminalWidth (self ):
516
- # type: () -> int
517
- if self .signal_safe .PollSigWinch (): # is our value dirty?
518
- try :
519
- self .term_width = libc .get_terminal_width ()
520
- except (IOError , OSError ):
521
- # This shouldn't raise IOError because we did it at startup! Under
522
- # rare circumstances stdin can change, e.g. if you do exec <&
523
- # input.txt. So we have a fallback.
524
- self .term_width = 80
525
- return self .term_width
526
-
527
535
528
536
def ExecutePrintCandidates (display , sub , matches , max_len ):
529
537
# type: (_IDisplay, str, List[str], int) -> None
@@ -548,7 +556,8 @@ def InitReadline(
548
556
549
557
readline .parse_and_bind ('tab: complete' )
550
558
551
- readline .parse_and_bind ('set horizontal-scroll-mode on' )
559
+ for cmd in display .ReadlineInitCommands ():
560
+ readline .parse_and_bind (cmd )
552
561
553
562
# How does this map to C?
554
563
# https://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC45
0 commit comments