Skip to content

Commit 410a6c8

Browse files
authored
Update REPLy 13f7eba083f138dd063c68b859c8e315f44fb523 (#15328)
1 parent dbdf548 commit 410a6c8

File tree

18 files changed

+404
-37
lines changed

18 files changed

+404
-37
lines changed

lib/.shards.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ shards:
66
version: 0.5.0
77
reply:
88
git: https://github.com/i3oris/reply.git
9-
version: 0.3.1+git.commit.db423dae3dd34c6ba5e36174653a0c109117a167
9+
version: 0.3.1+git.commit.13f7eba083f138dd063c68b859c8e315f44fb523

lib/reply/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ It includes the following features:
1212
* Hook for Auto formatting
1313
* Hook for Auto indentation
1414
* Hook for Auto completion (Experimental)
15+
* History Reverse i-search
1516
* Work on Windows 10
1617

1718
It doesn't support yet:
18-
* History reverse i-search
1919
* Customizable hotkeys
2020
* Unicode characters
2121

@@ -53,7 +53,7 @@ end
5353
require "reply"
5454
5555
class MyReader < Reply::Reader
56-
def prompt(io : IO, line_number : Int32, color? : Bool) : Nil
56+
def prompt(io : IO, line_number : Int32, color : Bool) : Nil
5757
# Display a custom prompt
5858
end
5959

lib/reply/examples/crystal_repl.cr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ CONTINUE_ERROR = [
4646
WORD_DELIMITERS = {{" \n\t+-*/,;@&%<>^\\[](){}|.~".chars}}
4747

4848
class CrystalReader < Reply::Reader
49-
def prompt(io : IO, line_number : Int32, color? : Bool) : Nil
50-
io << "crystal".colorize.blue.toggle(color?)
49+
def prompt(io : IO, line_number : Int32, color : Bool) : Nil
50+
io << "crystal".colorize.blue.toggle(color)
5151
io << ':'
5252
io << sprintf("%03d", line_number)
5353
io << "> "

lib/reply/spec/expression_editor_spec.cr

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ module Reply
391391
end
392392

393393
it "is aligned when prompt size change" do
394-
editor = ExpressionEditor.new do |line_number, _color?|
394+
editor = ExpressionEditor.new do |line_number, _color|
395395
"*" * line_number + ">" # A prompt that increase its size at each line
396396
end
397397
editor.output = IO::Memory.new
@@ -428,6 +428,39 @@ module Reply
428428
"****>5"
429429
end
430430

431+
it "Don't mess up the terminal when the prompt is empty" do
432+
editor = ExpressionEditor.new { "" }
433+
editor.output = IO::Memory.new
434+
editor.color = false
435+
editor.height = 5
436+
editor.width = 15
437+
438+
editor.update { editor << "Hello,\nWorld" }
439+
editor.verify_output "\e[1G\e[J" \
440+
"Hello,\n" \
441+
"World"
442+
443+
editor.output = IO::Memory.new
444+
editor.update { editor << '\n' }
445+
editor.verify_output "\e[1A\e[1G\e[J" \
446+
"Hello,\n" \
447+
"World\n"
448+
449+
editor.output = IO::Memory.new
450+
editor.update { editor << "1+1" }
451+
editor.verify_output "\e[2A\e[1G\e[J" \
452+
"Hello,\n" \
453+
"World\n" \
454+
"1+1"
455+
456+
editor.output = IO::Memory.new
457+
editor.update { editor << '\n' }
458+
editor.verify_output "\e[2A\e[1G\e[J" \
459+
"Hello,\n" \
460+
"World\n" \
461+
"1+1\n"
462+
end
463+
431464
# TODO:
432465
# header
433466
end

lib/reply/spec/reader_spec.cr

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,60 @@ module Reply
622622
SpecHelper.send(pipe_in, '\0')
623623
end
624624

625+
it "searches on ctrl-r" do
626+
reader = SpecHelper.reader(type: SpecReaderWithSearch)
627+
pipe_out, pipe_in = IO.pipe
628+
629+
SEARCH_ENTRIES.each { |e| reader.history << e }
630+
631+
spawn do
632+
reader.read_next(from: pipe_out)
633+
end
634+
635+
SpecHelper.send(pipe_in, '\u0012') # Ctrl-r (search)
636+
reader.search.verify("", open: true, failed: true)
637+
638+
SpecHelper.send(pipe_in, 'p')
639+
reader.search.verify("p", open: true, failed: false)
640+
reader.editor.verify("pp! i")
641+
reader.history.index.should eq 3
642+
643+
SpecHelper.send(pipe_in, "ut")
644+
reader.search.verify("put", open: true, failed: false)
645+
reader.editor.verify(<<-END)
646+
while i < 10
647+
puts i
648+
i += 1
649+
end
650+
END
651+
reader.history.index.should eq 2
652+
653+
SpecHelper.send(pipe_in, "ss")
654+
reader.search.verify("putss", open: true, failed: true)
655+
reader.editor.verify("")
656+
reader.history.index.should eq 5
657+
658+
SpecHelper.send(pipe_in, '\u{7f}') # back
659+
reader.search.verify("puts", open: true, failed: false)
660+
reader.editor.verify(<<-END)
661+
while i < 10
662+
puts i
663+
i += 1
664+
end
665+
END
666+
reader.history.index.should eq 2
667+
668+
SpecHelper.send(pipe_in, '\e') # back
669+
reader.search.verify("", open: false, failed: false)
670+
reader.editor.verify(<<-END)
671+
while i < 10
672+
puts i
673+
i += 1
674+
end
675+
END
676+
reader.history.index.should eq 2
677+
end
678+
625679
it "resets" do
626680
reader = SpecHelper.reader
627681
pipe_out, pipe_in = IO.pipe

lib/reply/spec/search_spec.cr

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
module Reply
2+
SEARCH_ENTRIES = [
3+
[%(puts "Hello World")],
4+
[%(i = 0)],
5+
[
6+
%(while i < 10),
7+
%( puts i),
8+
%( i += 1),
9+
%(end),
10+
],
11+
[%(pp! i)],
12+
[%("Bye")],
13+
]
14+
15+
describe Search do
16+
it "displays footer" do
17+
search = SpecHelper.search
18+
search.verify_footer("search: _", height: 1)
19+
20+
search.query = "foo"
21+
search.verify_footer("search: foo_", height: 1)
22+
23+
search.failed = true
24+
search.verify_footer("search: #{"foo".colorize.bold.red}_", height: 1)
25+
26+
search.failed = false
27+
search.query = "foobar"
28+
search.verify_footer("search: foobar_", height: 1)
29+
30+
search.close
31+
search.verify_footer("", height: 0)
32+
end
33+
34+
it "opens and closes" do
35+
search = SpecHelper.search
36+
search.query = "foo"
37+
search.failed = true
38+
search.verify(query: "foo", open: true, failed: true)
39+
40+
search.close
41+
search.verify(query: "", open: false, failed: false)
42+
43+
search.query = "bar"
44+
search.failed = true
45+
46+
search.open
47+
search.verify(query: "bar", open: true, failed: false)
48+
end
49+
50+
it "searches" do
51+
search = SpecHelper.search
52+
history = SpecHelper.history(SEARCH_ENTRIES)
53+
54+
search.search(history).should be_nil
55+
search.verify("", failed: true)
56+
history.verify(SEARCH_ENTRIES, index: 5)
57+
58+
search.query = "p"
59+
search.search(history).should eq Search::SearchResult.new(3, [%(pp! i)], x: 0, y: 0)
60+
history.verify(SEARCH_ENTRIES, index: 3)
61+
62+
search.query = "put"
63+
search.search(history).should eq Search::SearchResult.new(2, SEARCH_ENTRIES[2], x: 2, y: 1)
64+
history.verify(SEARCH_ENTRIES, index: 2)
65+
66+
search.query = "i"
67+
search.search(history).should eq Search::SearchResult.new(1, ["i = 0"], x: 0, y: 0)
68+
history.verify(SEARCH_ENTRIES, index: 1)
69+
70+
search.open
71+
search.search(history).should eq Search::SearchResult.new(3, ["pp! i"], x: 4, y: 0)
72+
history.verify(SEARCH_ENTRIES, index: 3)
73+
74+
search.open
75+
search.search(history).should eq Search::SearchResult.new(2, SEARCH_ENTRIES[2], x: 2, y: 0)
76+
history.verify(SEARCH_ENTRIES, index: 2)
77+
78+
search.query = "baz"
79+
search.search(history).should be_nil
80+
search.verify("baz", failed: true)
81+
history.verify(SEARCH_ENTRIES, index: 5)
82+
end
83+
end
84+
end

lib/reply/spec/spec_helper.cr

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module Reply
1515
height_got = nil
1616

1717
display_got = String.build do |io|
18-
height_got = self.display_entries(io, color?: false, width: with_width, max_height: max_height, min_height: min_height)
18+
height_got = self.display_entries(io, color: false, width: with_width, max_height: max_height, min_height: min_height)
1919
end
2020
display_got.should eq display
2121
height_got.should eq height
@@ -54,6 +54,22 @@ module Reply
5454
end
5555
end
5656

57+
class Search
58+
setter failed
59+
60+
def verify(query, open = true, failed = false)
61+
@query.should eq query
62+
@open.should eq open
63+
@failed.should eq failed
64+
end
65+
66+
def verify_footer(footer, height)
67+
String.build do |io|
68+
footer(io, true).should eq height
69+
end.should eq footer
70+
end
71+
end
72+
5773
struct CharReader
5874
def verify_read(to_read, expect : CharReader::Sequence)
5975
verify_read(to_read, [expect])
@@ -81,6 +97,14 @@ module Reply
8197
getter auto_completion
8298
end
8399

100+
class SpecReaderWithSearch < Reader
101+
def disable_search?
102+
false
103+
end
104+
105+
getter search
106+
end
107+
84108
class SpecReaderWithEqual < Reader
85109
def initialize
86110
super
@@ -124,7 +148,7 @@ module Reply
124148
end
125149

126150
def self.expression_editor
127-
editor = ExpressionEditor.new do |line_number, _color?|
151+
editor = ExpressionEditor.new do |line_number, _color|
128152
# Prompt size = 5
129153
"p:#{sprintf("%02d", line_number)}>"
130154
end
@@ -141,6 +165,10 @@ module Reply
141165
history
142166
end
143167

168+
def self.search
169+
Search.new.tap &.open
170+
end
171+
144172
def self.char_reader(buffer_size = 64)
145173
CharReader.new(buffer_size)
146174
end

lib/reply/src/auto_completion.cr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ module Reply
5656
# If closed, do nothing.
5757
#
5858
# Returns the actual displayed height.
59-
def display_entries(io, color? = true, width = Term::Size.width, max_height = 10, min_height = 0) : Int32 # ameba:disable Metrics/CyclomaticComplexity
59+
def display_entries(io, color = true, width = Term::Size.width, max_height = 10, min_height = 0) : Int32 # ameba:disable Metrics/CyclomaticComplexity
6060
if cleared?
6161
min_height.times { io.puts }
6262
return min_height
@@ -68,7 +68,7 @@ module Reply
6868
height = 0
6969

7070
# Print title:
71-
if color?
71+
if color
7272
@display_title.call(io, @title)
7373
else
7474
io << @title << ":"
@@ -116,23 +116,23 @@ module Reply
116116

117117
if r + c*nb_rows == @selection_pos
118118
# Colorize selection:
119-
if color?
119+
if color
120120
@display_selected_entry.call(io, entry_str)
121121
else
122122
io << ">" + entry_str[...-1] # if no color, remove last spaces to let place to '*'.
123123
end
124124
else
125125
# Display entry_str, with @name_filter prefix in bright:
126126
unless entry.empty?
127-
if color?
127+
if color
128128
io << @display_entry.call(io, @name_filter, entry_str.lchop(@name_filter))
129129
else
130130
io << entry_str
131131
end
132132
end
133133
end
134134
end
135-
io << Term::Cursor.clear_line_after if color?
135+
io << Term::Cursor.clear_line_after if color
136136
io.puts
137137
end
138138

lib/reply/src/char_reader.cr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Reply
1919
CTRL_K
2020
CTRL_N
2121
CTRL_P
22+
CTRL_R
2223
CTRL_U
2324
CTRL_X
2425
CTRL_UP
@@ -44,8 +45,8 @@ module Reply
4445
end
4546

4647
def read_char(from io : IO = STDIN)
47-
nb_read = raw(io, &.read(@slice_buffer))
48-
parse_escape_sequence(@slice_buffer[0...nb_read])
48+
nb_read = raw(io, &.read(@slice_buffer))
49+
parse_escape_sequence(@slice_buffer[0...nb_read])
4950
end
5051

5152
private def parse_escape_sequence(chars : Bytes) : Char | Sequence | String?
@@ -141,6 +142,8 @@ module Reply
141142
Sequence::CTRL_N
142143
when ctrl('p')
143144
Sequence::CTRL_P
145+
when ctrl('r')
146+
Sequence::CTRL_R
144147
when ctrl('u')
145148
Sequence::CTRL_U
146149
when ctrl('x')

0 commit comments

Comments
 (0)