{% skip_file if flag?(:win32) %} # FIXME: We skip all these specs because the file descriptor is blocking, making # the spec to hang out, and we cannot change it. # (`blocking=` is not implemented on window) require "./spec_helper" module Reply describe Reader do it "reads char" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "a" reader.read_next(from: pipe_out).should eq "♥💎" end SpecHelper.send(pipe_in, 'a') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, '♥') SpecHelper.send(pipe_in, '💎') SpecHelper.send(pipe_in, '\n') end it "reads string" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "Hello" reader.read_next(from: pipe_out).should eq "class Foo\n def foo\n 42\n end\nend" end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, <<-END) class Foo def foo 42 end end END SpecHelper.send(pipe_in, '\n') end it "uses directional arrows" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, <<-END) class Foo def foo 42 end end END SpecHelper.send(pipe_in, "\e[A") # up SpecHelper.send(pipe_in, "\e[C") # right SpecHelper.send(pipe_in, "\e[B") # down SpecHelper.send(pipe_in, "\e[D") # left reader.editor.verify(x: 2, y: 4) SpecHelper.send(pipe_in, '\0') end it "uses ctrl-n & ctrl-p" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) reader.read_next(from: pipe_out) reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "x = 42") SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, <<-END) puts "Hello", "World" END SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, '\u0010') # ctrl-p (up) reader.editor.verify(%(puts "Hello",\n "World")) SpecHelper.send(pipe_in, '\u0010') # ctrl-p (up) SpecHelper.send(pipe_in, '\u0010') # ctrl-p (up) reader.editor.verify("x = 42") SpecHelper.send(pipe_in, '\u000e') # ctrl-n (down) reader.editor.verify(%(puts "Hello",\n "World")) SpecHelper.send(pipe_in, '\0') end it "uses ctrl-f & ctrl-b" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "x=42") reader.editor.verify(x: 4, y: 0) SpecHelper.send(pipe_in, '\u0006') # ctrl-f (right) reader.editor.verify(x: 4, y: 0) SpecHelper.send(pipe_in, '\u0002') # ctrl-b (left) reader.editor.verify(x: 3, y: 0) SpecHelper.send(pipe_in, '\u0002') # ctrl-b (left) SpecHelper.send(pipe_in, '\u0002') # ctrl-b (left) SpecHelper.send(pipe_in, '\u0002') # ctrl-b (left) reader.editor.verify(x: 0, y: 0) SpecHelper.send(pipe_in, '\u0002') # ctrl-b (left) reader.editor.verify(x: 0, y: 0) SpecHelper.send(pipe_in, '\u0006') # ctrl-f (right) reader.editor.verify(x: 1, y: 0) SpecHelper.send(pipe_in, '\0') end it "uses back" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "Hey" reader.read_next(from: pipe_out).should eq "ab" reader.read_next(from: pipe_out).should eq "" end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, '\u{7f}') # back SpecHelper.send(pipe_in, '\u{7f}') SpecHelper.send(pipe_in, '\u{7f}') SpecHelper.send(pipe_in, 'y') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, "a\nb") SpecHelper.send(pipe_in, "\e[D") # left SpecHelper.send(pipe_in, '\u{7f}') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, "") SpecHelper.send(pipe_in, '\u{7f}') SpecHelper.send(pipe_in, '\n') end it "deletes" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "Hey" reader.read_next(from: pipe_out).should eq "ab" reader.read_next(from: pipe_out).should eq "" end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, "\e[D") # left SpecHelper.send(pipe_in, "\e[D") SpecHelper.send(pipe_in, "\e[D") SpecHelper.send(pipe_in, "\e[3~") # delete SpecHelper.send(pipe_in, "\e[3~") SpecHelper.send(pipe_in, "\e[3~") SpecHelper.send(pipe_in, 'y') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, "a\nb") SpecHelper.send(pipe_in, "\e[D") SpecHelper.send(pipe_in, "\e[D") SpecHelper.send(pipe_in, "\e[3~") SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, "") SpecHelper.send(pipe_in, "\e[3~") SpecHelper.send(pipe_in, '\n') end it "deletes or eof" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe channel = Channel(Symbol).new spawn do reader.read_next(from: pipe_out).should be_nil channel.send(:finished) end SpecHelper.send(pipe_in, "a\nb") SpecHelper.send(pipe_in, '\u0001') # ctrl-a (move cursor to begin) reader.editor.verify("a\nb") SpecHelper.send(pipe_in, '\u0004') # ctrl-d (delete or eof) reader.editor.verify("\nb") SpecHelper.send(pipe_in, '\u0004') # ctrl-d (delete or eof) reader.editor.verify("b") SpecHelper.send(pipe_in, '\u0004') # ctrl-d (delete or eof) reader.editor.verify("") SpecHelper.send(pipe_in, '\u0004') # ctrl-d (delete or eof) channel.receive.should eq :finished end it "uses tabulation" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "42.") reader.auto_completion.verify(open: false) SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world hey)) reader.editor.verify("42.") SpecHelper.send(pipe_in, 'w') reader.auto_completion.verify(open: true, entries: %w(world), name_filter: "w") reader.editor.verify("42.w") SpecHelper.send(pipe_in, '\u{7f}') # back reader.auto_completion.verify(open: true, entries: %w(hello world hey)) reader.editor.verify("42.") SpecHelper.send(pipe_in, 'h') reader.auto_completion.verify(open: true, entries: %w(hello hey), name_filter: "h") reader.editor.verify("42.h") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello hey), name_filter: "h", selection_pos: 0) reader.editor.verify("42.hello") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello hey), name_filter: "h", selection_pos: 1) reader.editor.verify("42.hey") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello hey), name_filter: "h", selection_pos: 0) reader.editor.verify("42.hello") SpecHelper.send(pipe_in, "\e\t") # shift_tab reader.auto_completion.verify(open: true, entries: %w(hello hey), name_filter: "h", selection_pos: 1) reader.editor.verify("42.hey") SpecHelper.send(pipe_in, '\u{7f}') # back SpecHelper.send(pipe_in, 'l') SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello), name_filter: "hel", selection_pos: 0) reader.editor.verify("42.hello") SpecHelper.send(pipe_in, ' ') reader.auto_completion.verify(open: false, cleared: true) reader.editor.verify("42.hello ") SpecHelper.send(pipe_in, '\0') end it "roll over auto completion entries with equal" do reader = SpecHelper.reader(SpecReaderWithEqual) pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world= hey)) reader.editor.verify("") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world= hey), selection_pos: 0) reader.editor.verify("hello") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world= hey), selection_pos: 1) reader.editor.verify("world=") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world= hey), selection_pos: 2) reader.editor.verify("hey") SpecHelper.send(pipe_in, '\0') end it "retriggers auto-completion when current word ends with ':'" do reader = SpecHelper.reader(SpecReaderWithAutoCompletionRetrigger) pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "fo") SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(foo foobar), name_filter: "fo") reader.editor.verify("foo") SpecHelper.send(pipe_in, ':') SpecHelper.send(pipe_in, ':') reader.auto_completion.verify(open: true, entries: %w(foo::foo foo::foobar foo::bar), name_filter: "foo::") reader.editor.verify("foo::") SpecHelper.send(pipe_in, 'b') SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(foo::bar), name_filter: "foo::b", selection_pos: 0) reader.editor.verify("foo::bar") SpecHelper.send(pipe_in, ':') SpecHelper.send(pipe_in, ':') reader.auto_completion.verify(open: true, entries: %w(foo::bar::foo foo::bar::foobar foo::bar::bar), name_filter: "foo::bar::") reader.editor.verify("foo::bar::") SpecHelper.send(pipe_in, '\0') end it "uses escape" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "42.") reader.auto_completion.verify(open: false) SpecHelper.send(pipe_in, '\t') reader.auto_completion.verify(open: true, entries: %w(hello world hey)) SpecHelper.send(pipe_in, '\e') # escape reader.auto_completion.verify(open: false) end it "uses alt-enter" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "Hello\nWorld" end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, "\e\r") # alt-enter SpecHelper.send(pipe_in, "World") reader.editor.verify("Hello\nWorld") SpecHelper.send(pipe_in, "\n") end it "uses ctrl-c" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should be_nil end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, '\u{3}') # ctrl-c reader.editor.verify("") SpecHelper.send(pipe_in, '\0') end it "uses ctrl-d & ctrl-x" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should be_nil reader.read_next(from: pipe_out).should be_nil end SpecHelper.send(pipe_in, "Hello") SpecHelper.send(pipe_in, '\u{4}') # ctrl-d SpecHelper.send(pipe_in, "World") SpecHelper.send(pipe_in, '\u{24}') # ctrl-x end it "uses ctrl-u & ctrl-k" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, <<-END) Lorem ipsum dolor sit amet. END SpecHelper.send(pipe_in, '\u0010') # ctrl-p (up) reader.editor.verify(x: 5, y: 1) SpecHelper.send(pipe_in, '\u000b') # ctrl-k (delete after) reader.editor.verify(<<-END, x: 5, y: 1) Lorem ipsum dolor amet. END SpecHelper.send(pipe_in, '\u000b') # ctrl-k (delete after) reader.editor.verify(<<-END, x: 5, y: 1) Lorem ipsum doloramet. END SpecHelper.send(pipe_in, '\u0015') # ctrl-u (delete before) reader.editor.verify(<<-END, x: 0, y: 1) Lorem ipsum amet. END SpecHelper.send(pipe_in, '\u000b') # ctrl-k (delete after) reader.editor.verify(<<-END, x: 0, y: 1) Lorem ipsum END SpecHelper.send(pipe_in, '\u0015') # ctrl-u (delete before) SpecHelper.send(pipe_in, '\u0015') # ctrl-u (delete before) reader.editor.verify("", x: 0, y: 0) SpecHelper.send(pipe_in, '\u000b') # ctrl-k (delete after) SpecHelper.send(pipe_in, '\u0015') # ctrl-u (delete before) reader.editor.verify("", x: 0, y: 0) end it "moves word forward" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, <<-END) lorem ipsum +"dolor", sit: amet() END SpecHelper.send(pipe_in, '\u0001') # ctrl-a (move cursor to begin) reader.editor.verify(x: 0, y: 0) SpecHelper.send(pipe_in, "\ef") # Alt-f (move_word_forward) reader.editor.verify(x: 5, y: 0) SpecHelper.send(pipe_in, "\ef") # Alt-f (move_word_forward) reader.editor.verify(x: 13, y: 0) SpecHelper.send(pipe_in, "\e[1;5C") # Ctrl-right (move_word_forward) reader.editor.verify(x: 7, y: 1) SpecHelper.send(pipe_in, "\e[1;5C") # Ctrl-right (move_word_forward) reader.editor.verify(x: 13, y: 1) SpecHelper.send(pipe_in, "\e[1;5C") # Ctrl-right (move_word_forward) reader.editor.verify(x: 14, y: 1) SpecHelper.send(pipe_in, "\ef") # Alt-f (move_word_forward) reader.editor.verify(x: 4, y: 2) SpecHelper.send(pipe_in, "\ef") # Alt-f (move_word_forward) reader.editor.verify(x: 6, y: 2) SpecHelper.send(pipe_in, "\ef") # Alt-f (move_word_forward) reader.editor.verify(x: 6, y: 2) SpecHelper.send(pipe_in, "\0") end it "moves word backward" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, <<-END) lorem ipsum +"dolor", sit: amet() END reader.editor.verify(x: 6, y: 2) SpecHelper.send(pipe_in, "\eb") # Alt-b (move_word_backward) reader.editor.verify(x: 0, y: 2) SpecHelper.send(pipe_in, "\eb") # Alt-b (move_word_backward) reader.editor.verify(x: 10, y: 1) SpecHelper.send(pipe_in, "\e[1;5D") # Ctrl-left (move_word_backward) reader.editor.verify(x: 2, y: 1) SpecHelper.send(pipe_in, "\e[1;5D") # Ctrl-left (move_word_backward) reader.editor.verify(x: 0, y: 1) SpecHelper.send(pipe_in, "\e[1;5D") # Ctrl-left (move_word_backward) reader.editor.verify(x: 8, y: 0) SpecHelper.send(pipe_in, "\eb") # Alt-b (move_word_backward) reader.editor.verify(x: 0, y: 0) SpecHelper.send(pipe_in, "\eb") # Alt-b (move_word_backward) reader.editor.verify(x: 0, y: 0) SpecHelper.send(pipe_in, "\0") end it "uses delete word and word back" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, <<-END) lorem ipsum +"dolor", sit: amet() END SpecHelper.send(pipe_in, "\e[A") # up reader.editor.verify(x: 6, y: 1) SpecHelper.send(pipe_in, '\b') # Ctrl-backspace (delete_word) reader.editor.verify(<<-END, x: 2, y: 1) lorem ipsum +"r", sit: amet() END SpecHelper.send(pipe_in, '\b') # Ctrl-backspace (delete_word) reader.editor.verify(<<-END, x: 0, y: 1) lorem ipsum r", sit: amet() END SpecHelper.send(pipe_in, '\b') # Ctrl-backspace (delete_word) reader.editor.verify(<<-END, x: 8, y: 0) lorem r", sit: amet() END SpecHelper.send(pipe_in, "\ed") # Alt-d (word_back) reader.editor.verify(<<-END, x: 8, y: 0) lorem ", sit: amet() END SpecHelper.send(pipe_in, "\ed") # Alt-d (word_back) SpecHelper.send(pipe_in, "\e[3;5~") # Ctrl-delete (word_back) SpecHelper.send(pipe_in, "\e[3;5~") # Ctrl-delete (word_back) reader.editor.verify(<<-END, x: 8, y: 0) lorem () END SpecHelper.send(pipe_in, '\b') # Ctrl-backspace (delete_word) SpecHelper.send(pipe_in, "\e\u007f") # Alt-backspace (delete_word) reader.editor.verify(<<-END, x: 0, y: 0) () END SpecHelper.send(pipe_in, "\ed") # Alt-d (word_back) SpecHelper.send(pipe_in, "\ed") # Alt-d (word_back) reader.editor.verify("", x: 0, y: 0) SpecHelper.send(pipe_in, "\0") end it "sets history to last after empty entry" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out).should eq "a" reader.read_next(from: pipe_out).should eq "b" reader.read_next(from: pipe_out).should eq "" reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, 'a') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, 'b') SpecHelper.send(pipe_in, '\n') SpecHelper.send(pipe_in, "\e[A") # up reader.editor.verify("b") SpecHelper.send(pipe_in, "\e[A") # up reader.editor.verify("a") SpecHelper.send(pipe_in, "\u{7f}") # back SpecHelper.send(pipe_in, '\n') reader.editor.verify("") SpecHelper.send(pipe_in, "\e[A") # up reader.editor.verify("b") SpecHelper.send(pipe_in, "\e[A") # up reader.editor.verify("a") SpecHelper.send(pipe_in, '\0') end it "searches on ctrl-r" do reader = SpecHelper.reader(type: SpecReaderWithSearch) pipe_out, pipe_in = IO.pipe SEARCH_ENTRIES.each { |e| reader.history << e } spawn do reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, '\u0012') # Ctrl-r (search) reader.search.verify("", open: true, failed: true) SpecHelper.send(pipe_in, 'p') reader.search.verify("p", open: true, failed: false) reader.editor.verify("pp! i") reader.history.index.should eq 3 SpecHelper.send(pipe_in, "ut") reader.search.verify("put", open: true, failed: false) reader.editor.verify(<<-END) while i < 10 puts i i += 1 end END reader.history.index.should eq 2 SpecHelper.send(pipe_in, "ss") reader.search.verify("putss", open: true, failed: true) reader.editor.verify("") reader.history.index.should eq 5 SpecHelper.send(pipe_in, '\u{7f}') # back reader.search.verify("puts", open: true, failed: false) reader.editor.verify(<<-END) while i < 10 puts i i += 1 end END reader.history.index.should eq 2 SpecHelper.send(pipe_in, '\e') # back reader.search.verify("", open: false, failed: false) reader.editor.verify(<<-END) while i < 10 puts i i += 1 end END reader.history.index.should eq 2 end it "resets" do reader = SpecHelper.reader pipe_out, pipe_in = IO.pipe spawn do reader.read_next(from: pipe_out) reader.read_next(from: pipe_out) end SpecHelper.send(pipe_in, "Hello\nWorld") SpecHelper.send(pipe_in, '\n') reader.line_number.should eq 3 reader.reset reader.line_number.should eq 1 SpecHelper.send(pipe_in, '\0') end end end