Skip to content

Commit cde6807

Browse files
committed
Add rprompt support for right-side prompt display
This adds support for displaying a right-aligned prompt (rprompt) similar to zsh's RPROMPT feature. The rprompt is displayed at the right edge of the terminal and automatically hides when the input line gets too long. Usage: Reline.rprompt = "[%H:%M]" Reline.readline("> ") The rprompt is rendered as part of Reline's normal render cycle, so it persists during line editing unlike workarounds using pre_input_hook.
1 parent a416186 commit cde6807

File tree

5 files changed

+69
-0
lines changed

5 files changed

+69
-0
lines changed

lib/reline.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Core
5050
output_modifier_proc
5151
prompt_proc
5252
auto_indent_proc
53+
rprompt
5354
pre_input_hook
5455
dig_perfect_match_proc
5556
).each(&method(:attr_reader))
@@ -158,6 +159,10 @@ def dig_perfect_match_proc=(p)
158159
@dig_perfect_match_proc = p
159160
end
160161

162+
def rprompt=(val)
163+
@rprompt = val&.encode(encoding)
164+
end
165+
161166
DialogProc = Struct.new(:dialog_proc, :context)
162167
def add_dialog_proc(name_sym, p, context = nil)
163168
raise ArgumentError unless name_sym.instance_of?(Symbol)
@@ -323,6 +328,7 @@ def readline(prompt = '', add_hist = false)
323328
line_editor.prompt_proc = prompt_proc
324329
line_editor.auto_indent_proc = auto_indent_proc
325330
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
331+
line_editor.rprompt = rprompt
326332

327333
pre_input_hook&.call
328334

lib/reline/line_editor.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Reline::LineEditor
1313
attr_accessor :prompt_proc
1414
attr_accessor :auto_indent_proc
1515
attr_accessor :dig_perfect_match_proc
16+
attr_accessor :rprompt
1617

1718
VI_MOTIONS = %i{
1819
ed_prev_char
@@ -476,6 +477,20 @@ def render
476477
prompt_width = Reline::Unicode.calculate_width(prompt, true)
477478
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
478479
end
480+
481+
# Add rprompt to the first visible line if set and there's room
482+
if @rprompt && !@rprompt.empty? && new_lines[0]
483+
rprompt_width = Reline::Unicode.calculate_width(@rprompt, true)
484+
right_col = screen_width - rprompt_width
485+
first_line = new_lines[0]
486+
# Calculate the end of the current content (prompt + input)
487+
content_end = first_line.sum { |_, width, _| width }
488+
# Only show rprompt if there's at least 1 char gap between content and rprompt
489+
if right_col > content_end
490+
first_line << [right_col, rprompt_width, @rprompt]
491+
end
492+
end
493+
479494
if @menu_info
480495
@menu_info.lines(screen_width).each do |item|
481496
new_lines << [[0, Reline::Unicode.calculate_width(item), item]]

test/reline/test_reline.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def setup
1919
Reline.auto_indent_proc = nil
2020
Reline.pre_input_hook = nil
2121
Reline.dig_perfect_match_proc = nil
22+
Reline.rprompt = nil
2223
end
2324

2425
def teardown
@@ -232,6 +233,21 @@ def test_pre_input_hook
232233
assert_equal(l, Reline.pre_input_hook)
233234
end
234235

236+
def test_rprompt
237+
assert_equal(nil, Reline.rprompt)
238+
239+
Reline.rprompt = "[Time]"
240+
assert_equal("[Time]", Reline.rprompt)
241+
assert_equal(get_reline_encoding, Reline.rprompt.encoding)
242+
243+
Reline.rprompt = "[Time]".encode(Encoding::ASCII)
244+
assert_equal("[Time]", Reline.rprompt)
245+
assert_equal(get_reline_encoding, Reline.rprompt.encoding)
246+
247+
Reline.rprompt = nil
248+
assert_equal(nil, Reline.rprompt)
249+
end
250+
235251
def test_dig_perfect_match_proc
236252
assert_equal(nil, Reline.dig_perfect_match_proc)
237253

test/reline/yamatanooroti/multiline_repl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ opt.on('--autocomplete-width-long') {
212212
}.select{ |c| c.start_with?(target) }
213213
}
214214
}
215+
opt.on('--rprompt VAL') { |v|
216+
Reline.rprompt = v
217+
}
215218
opt.parse!(ARGV)
216219

217220
begin

test/reline/yamatanooroti/test_rendering.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,35 @@ def test_prompt
183183
close
184184
end
185185

186+
def test_rprompt
187+
start_terminal(5, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --rprompt [RPROMPT]}, startup_message: 'Multiline REPL.')
188+
assert_screen(<<~EOC)
189+
Multiline REPL.
190+
prompt> [RPROMPT]
191+
EOC
192+
close
193+
end
194+
195+
def test_rprompt_with_input
196+
start_terminal(5, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --rprompt [RPROMPT]}, startup_message: 'Multiline REPL.')
197+
write("hello")
198+
assert_screen(<<~EOC)
199+
Multiline REPL.
200+
prompt> hello [RPROMPT]
201+
EOC
202+
close
203+
end
204+
205+
def test_rprompt_hides_when_input_reaches_rprompt
206+
start_terminal(5, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --rprompt [RPROMPT]}, startup_message: 'Multiline REPL.')
207+
write("a" * 30)
208+
assert_screen(<<~EOC)
209+
Multiline REPL.
210+
prompt> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
211+
EOC
212+
close
213+
end
214+
186215
def test_mode_string_emacs
187216
write_inputrc <<~LINES
188217
set show-mode-in-prompt on

0 commit comments

Comments
 (0)