yap-rawline 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +42 -0
- data/LICENSE +12 -0
- data/README.rdoc +60 -0
- data/examples/key_tester.rb +21 -0
- data/examples/rawline_irb.rb +28 -0
- data/examples/rawline_rush.rb +27 -0
- data/examples/rawline_shell.rb +189 -0
- data/examples/readline_emulation.rb +17 -0
- data/lib/rawline.rb +60 -0
- data/lib/rawline/completer.rb +71 -0
- data/lib/rawline/editor.rb +938 -0
- data/lib/rawline/event_loop.rb +78 -0
- data/lib/rawline/event_registry.rb +17 -0
- data/lib/rawline/history_buffer.rb +177 -0
- data/lib/rawline/keycode_parser.rb +49 -0
- data/lib/rawline/line.rb +169 -0
- data/lib/rawline/prompt.rb +12 -0
- data/lib/rawline/terminal.rb +161 -0
- data/lib/rawline/terminal/vt220_terminal.rb +66 -0
- data/lib/rawline/terminal/windows_terminal.rb +66 -0
- data/spec/editor_spec.rb +260 -0
- data/spec/history_buffer_spec.rb +106 -0
- data/spec/keycode_parser_spec.rb +96 -0
- data/spec/line_spec.rb +58 -0
- data/spec/prompt_spec.rb +8 -0
- data/spec/spec_helper.rb +96 -0
- metadata +119 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
#!usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'terminfo'
|
4
|
+
require 'io/console'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
#
|
8
|
+
# terminal.rb
|
9
|
+
#
|
10
|
+
# Created by Fabio Cevasco on 2008-03-01.
|
11
|
+
# Copyright (c) 2008 Fabio Cevasco. All rights reserved.
|
12
|
+
#
|
13
|
+
# This is Free Software. See LICENSE for details.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module RawLine
|
18
|
+
|
19
|
+
#
|
20
|
+
# The Terminal class defines character codes and code sequences which can be
|
21
|
+
# bound to actions by editors.
|
22
|
+
# An OS-dependent subclass of RawLine::Terminal is automatically instantiated by
|
23
|
+
# RawLine::Editor.
|
24
|
+
#
|
25
|
+
class Terminal
|
26
|
+
|
27
|
+
include HighLine::SystemExtensions
|
28
|
+
|
29
|
+
attr_accessor :escape_codes
|
30
|
+
attr_reader :keys, :escape_sequences
|
31
|
+
|
32
|
+
#
|
33
|
+
# Create an instance of RawLine::Terminal.
|
34
|
+
#
|
35
|
+
def initialize
|
36
|
+
@keys =
|
37
|
+
{
|
38
|
+
:tab => [?\t.ord],
|
39
|
+
:return => [?\r.ord],
|
40
|
+
:newline => [?\n.ord],
|
41
|
+
:escape => [?\e.ord],
|
42
|
+
|
43
|
+
:ctrl_a => [?\C-a.ord],
|
44
|
+
:ctrl_b => [?\C-b.ord],
|
45
|
+
:ctrl_c => [?\C-c.ord],
|
46
|
+
:ctrl_d => [?\C-d.ord],
|
47
|
+
:ctrl_e => [?\C-e.ord],
|
48
|
+
:ctrl_f => [?\C-f.ord],
|
49
|
+
:ctrl_g => [?\C-g.ord],
|
50
|
+
:ctrl_h => [?\C-h.ord],
|
51
|
+
:ctrl_i => [?\C-i.ord],
|
52
|
+
:ctrl_j => [?\C-j.ord],
|
53
|
+
:ctrl_k => [?\C-k.ord],
|
54
|
+
:ctrl_l => [?\C-l.ord],
|
55
|
+
:ctrl_m => [?\C-m.ord],
|
56
|
+
:ctrl_n => [?\C-n.ord],
|
57
|
+
:ctrl_o => [?\C-o.ord],
|
58
|
+
:ctrl_p => [?\C-p.ord],
|
59
|
+
:ctrl_q => [?\C-q.ord],
|
60
|
+
:ctrl_r => [?\C-r.ord],
|
61
|
+
:ctrl_s => [?\C-s.ord],
|
62
|
+
:ctrl_t => [?\C-t.ord],
|
63
|
+
:ctrl_u => [?\C-u.ord],
|
64
|
+
:ctrl_v => [?\C-v.ord],
|
65
|
+
:ctrl_w => [?\C-w.ord],
|
66
|
+
:ctrl_x => [?\C-x.ord],
|
67
|
+
:ctrl_y => [?\C-y.ord],
|
68
|
+
:ctrl_z => [?\C-z.ord]
|
69
|
+
}
|
70
|
+
@escape_codes = []
|
71
|
+
@escape_sequences = []
|
72
|
+
update
|
73
|
+
end
|
74
|
+
|
75
|
+
CursorPosition = Struct.new(:column, :row)
|
76
|
+
|
77
|
+
def cursor_position
|
78
|
+
res = ''
|
79
|
+
$stdin.raw do |stdin|
|
80
|
+
$stdout << "\e[6n"
|
81
|
+
$stdout.flush
|
82
|
+
while (c = stdin.getc) != 'R'
|
83
|
+
res << c if c
|
84
|
+
end
|
85
|
+
end
|
86
|
+
m = res.match /(?<row>\d+);(?<column>\d+)/
|
87
|
+
CursorPosition.new(Integer(m[:column]), Integer(m[:row]))
|
88
|
+
end
|
89
|
+
|
90
|
+
def clear_to_beginning_of_line
|
91
|
+
term_info.control "el1"
|
92
|
+
end
|
93
|
+
|
94
|
+
def clear_screen
|
95
|
+
term_info.control "clear"
|
96
|
+
end
|
97
|
+
|
98
|
+
def clear_screen_down
|
99
|
+
term_info.control "ed"
|
100
|
+
end
|
101
|
+
|
102
|
+
def move_to_beginning_of_row
|
103
|
+
move_to_column 0
|
104
|
+
end
|
105
|
+
|
106
|
+
def move_left
|
107
|
+
move_left_n_characters 1
|
108
|
+
end
|
109
|
+
|
110
|
+
def move_left_n_characters(n)
|
111
|
+
n.times { term_info.control "cub1" }
|
112
|
+
end
|
113
|
+
|
114
|
+
def move_right_n_characters(n)
|
115
|
+
n.times { term_info.control "cuf1" }
|
116
|
+
end
|
117
|
+
|
118
|
+
def move_to_column_and_row(column, row)
|
119
|
+
term_info.control "cup", column, row
|
120
|
+
end
|
121
|
+
|
122
|
+
def move_to_column(n)
|
123
|
+
term_info.control "hpa", n
|
124
|
+
end
|
125
|
+
|
126
|
+
def move_up_n_rows(n)
|
127
|
+
n.times { term_info.control "cuu1" }
|
128
|
+
end
|
129
|
+
|
130
|
+
def move_down_n_rows(n)
|
131
|
+
n.times { term_info.control "cud1" }
|
132
|
+
end
|
133
|
+
|
134
|
+
def preserve_cursor(&blk)
|
135
|
+
term_info.control "sc" # store cursor position
|
136
|
+
blk.call
|
137
|
+
ensure
|
138
|
+
term_info.control "rc" # restore cursor position
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Update the terminal escape sequences. This method is called automatically
|
143
|
+
# by RawLine::Editor#bind().
|
144
|
+
#
|
145
|
+
def update
|
146
|
+
@keys.each_value do |k|
|
147
|
+
l = k.length
|
148
|
+
if l > 1 then
|
149
|
+
@escape_sequences << k unless @escape_sequences.include? k
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def term_info
|
155
|
+
@term_info ||= TermInfo.new(ENV["TERM"], $stdout)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# vt220_terminal.rb
|
5
|
+
#
|
6
|
+
# Created by Fabio Cevasco on 2008-03-01.
|
7
|
+
# Copyright (c) 2008 Fabio Cevasco. All rights reserved.
|
8
|
+
#
|
9
|
+
# This is Free Software. See LICENSE for details.
|
10
|
+
#
|
11
|
+
|
12
|
+
module RawLine
|
13
|
+
|
14
|
+
#
|
15
|
+
# This class is used to define all the most common character codes and
|
16
|
+
# escape sequences used on *nix systems.
|
17
|
+
#
|
18
|
+
class VT220Terminal < Terminal
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
@escape_codes = [?\e.ord]
|
23
|
+
@keys.merge!(
|
24
|
+
{
|
25
|
+
:up_arrow => [?\e.ord, ?[.ord, ?A.ord],
|
26
|
+
:down_arrow => [?\e.ord, ?[.ord, ?B.ord],
|
27
|
+
:right_arrow => [?\e.ord, ?[.ord, ?C.ord],
|
28
|
+
:left_arrow => [?\e.ord, ?[.ord, ?D.ord],
|
29
|
+
:insert => [?\e.ord, ?[, ?2.ord, ?~.ord],
|
30
|
+
:delete => [?\e.ord, ?[, ?3.ord, ?~.ord],
|
31
|
+
:backspace => [?\C-?.ord],
|
32
|
+
:enter => (HighLine::SystemExtensions::CHARACTER_MODE == 'termios' ? [?\n.ord] : [?\r.ord]),
|
33
|
+
|
34
|
+
:ctrl_alt_a => [?\e.ord, ?\C-a.ord],
|
35
|
+
:ctrl_alt_b => [?\e.ord, ?\C-b.ord],
|
36
|
+
:ctrl_alt_c => [?\e.ord, ?\C-c.ord],
|
37
|
+
:ctrl_alt_d => [?\e.ord, ?\C-d.ord],
|
38
|
+
:ctrl_alt_e => [?\e.ord, ?\C-e.ord],
|
39
|
+
:ctrl_alt_f => [?\e.ord, ?\C-f.ord],
|
40
|
+
:ctrl_alt_g => [?\e.ord, ?\C-g.ord],
|
41
|
+
:ctrl_alt_h => [?\e.ord, ?\C-h.ord],
|
42
|
+
:ctrl_alt_i => [?\e.ord, ?\C-i.ord],
|
43
|
+
:ctrl_alt_j => [?\e.ord, ?\C-j.ord],
|
44
|
+
:ctrl_alt_k => [?\e.ord, ?\C-k.ord],
|
45
|
+
:ctrl_alt_l => [?\e.ord, ?\C-l.ord],
|
46
|
+
:ctrl_alt_m => [?\e.ord, ?\C-m.ord],
|
47
|
+
:ctrl_alt_n => [?\e.ord, ?\C-n.ord],
|
48
|
+
:ctrl_alt_o => [?\e.ord, ?\C-o.ord],
|
49
|
+
:ctrl_alt_p => [?\e.ord, ?\C-p.ord],
|
50
|
+
:ctrl_alt_q => [?\e.ord, ?\C-q.ord],
|
51
|
+
:ctrl_alt_r => [?\e.ord, ?\C-r.ord],
|
52
|
+
:ctrl_alt_s => [?\e.ord, ?\C-s.ord],
|
53
|
+
:ctrl_alt_t => [?\e.ord, ?\C-t.ord],
|
54
|
+
:ctrl_alt_u => [?\e.ord, ?\C-u.ord],
|
55
|
+
:ctrl_alt_v => [?\e.ord, ?\C-v.ord],
|
56
|
+
:ctrl_alt_w => [?\e.ord, ?\C-w.ord],
|
57
|
+
:ctrl_alt_x => [?\e.ord, ?\C-x.ord],
|
58
|
+
:ctrl_alt_y => [?\e.ord, ?\C-y.ord],
|
59
|
+
:ctrl_alt_z => [?\e.ord, ?\C-z.ord]
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# windows_terminal.rb
|
5
|
+
#
|
6
|
+
# Created by Fabio Cevasco on 2008-03-01.
|
7
|
+
# Copyright (c) 2008 Fabio Cevasco. All rights reserved.
|
8
|
+
#
|
9
|
+
# This is Free Software. See LICENSE for details.
|
10
|
+
#
|
11
|
+
|
12
|
+
module RawLine
|
13
|
+
|
14
|
+
#
|
15
|
+
# This class is used to define all the most common character codes and
|
16
|
+
# escape sequences used on Windows systems.
|
17
|
+
#
|
18
|
+
class WindowsTerminal < Terminal
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
@escape_codes = [0, 27, 224]
|
23
|
+
@keys.merge!(
|
24
|
+
{
|
25
|
+
:left_arrow => [224, 75],
|
26
|
+
:right_arrow => [224, 77],
|
27
|
+
:up_arrow => [224, 72],
|
28
|
+
:down_arrow => [224, 80],
|
29
|
+
:insert => [224, 82],
|
30
|
+
:delete => [224, 83],
|
31
|
+
:backspace => [8],
|
32
|
+
:enter => [13],
|
33
|
+
|
34
|
+
:ctrl_alt_a => [0, 30],
|
35
|
+
:ctrl_alt_b => [0, 48],
|
36
|
+
:ctrl_alt_c => [0, 46],
|
37
|
+
:ctrl_alt_d => [0, 32],
|
38
|
+
:ctrl_alt_e => [0, 63],
|
39
|
+
:ctrl_alt_f => [0, 33],
|
40
|
+
:ctrl_alt_g => [0, 34],
|
41
|
+
:ctrl_alt_h => [0, 35],
|
42
|
+
:ctrl_alt_i => [0, 23],
|
43
|
+
:ctrl_alt_j => [0, 36],
|
44
|
+
:ctrl_alt_k => [0, 37],
|
45
|
+
:ctrl_alt_l => [0, 26],
|
46
|
+
:ctrl_alt_m => [0, 32],
|
47
|
+
:ctrl_alt_n => [0, 31],
|
48
|
+
:ctrl_alt_o => [0, 24],
|
49
|
+
:ctrl_alt_p => [0, 25],
|
50
|
+
:ctrl_alt_q => [0, 16],
|
51
|
+
:ctrl_alt_r => [0, 19],
|
52
|
+
:ctrl_alt_s => [0, 31],
|
53
|
+
:ctrl_alt_t => [0, 20],
|
54
|
+
:ctrl_alt_u => [0, 22],
|
55
|
+
:ctrl_alt_v => [0, 47],
|
56
|
+
:ctrl_alt_w => [0, 17],
|
57
|
+
:ctrl_alt_x => [0, 45],
|
58
|
+
:ctrl_alt_y => [0, 21],
|
59
|
+
:ctrl_alt_z => [0, 44]
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
data/spec/editor_spec.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'highline/system_extensions'
|
4
|
+
|
5
|
+
module HighLine::SystemExtensions
|
6
|
+
# Override Windows' character reading so it's not tied to STDIN.
|
7
|
+
def get_character( input = STDIN )
|
8
|
+
(RUBY_VERSION.gsub(/1\./, '').to_f >= 8.7) ? input.getbyte : input.getc
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'stringio'
|
13
|
+
require_relative "../lib/rawline.rb"
|
14
|
+
|
15
|
+
describe RawLine::Editor do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
@output = StringIO.new
|
19
|
+
@input = StringIO.new
|
20
|
+
@editor = RawLine::Editor.new(@input, @output)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "reads raw characters from @input" do
|
24
|
+
@input << "test #1"
|
25
|
+
@input.rewind
|
26
|
+
@editor.read
|
27
|
+
expect(@editor.line.text).to eq("test #1")
|
28
|
+
expect(@output.string).to eq("test #1\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can bind keys to code blocks" do
|
32
|
+
@editor.bind(:ctrl_w) { @editor.write "test #2a" }
|
33
|
+
@editor.bind(?\C-q) { "test #2b" }
|
34
|
+
@editor.bind(21) { "test #2c" }
|
35
|
+
@editor.bind([22]) { "test #2d" }
|
36
|
+
@editor.terminal.escape_codes = [] # remove any existing escape codes
|
37
|
+
expect {@editor.bind({:test => [?\e.ord, ?t.ord, ?e.ord, ?s.ord, ?t.ord]}) { "test #2e" }}.to raise_error(RawLine::BindingException)
|
38
|
+
@editor.terminal.escape_codes << ?\e.ord
|
39
|
+
expect {@editor.bind({:test => "\etest"}) { "test #2e" }}.to_not raise_error
|
40
|
+
expect {@editor.bind("\etest2") { "test #2f" }}.to_not raise_error
|
41
|
+
@input << ?\C-w.chr
|
42
|
+
@input.rewind
|
43
|
+
@editor.read
|
44
|
+
expect(@editor.line.text).to eq("test #2a")
|
45
|
+
@editor.char = [?\C-q.ord]
|
46
|
+
expect(@editor.press_key).to eq("test #2b")
|
47
|
+
@editor.char = [?\C-u.ord]
|
48
|
+
expect(@editor.press_key).to eq("test #2c")
|
49
|
+
@editor.char = [?\C-v.ord]
|
50
|
+
expect(@editor.press_key).to eq("test #2d")
|
51
|
+
@editor.char = [?\e.ord, ?t.ord, ?e.ord, ?s.ord, ?t.ord]
|
52
|
+
expect(@editor.press_key).to eq("test #2e")
|
53
|
+
@editor.char = [?\e.ord, ?t.ord, ?e.ord, ?s.ord, ?t.ord, ?2.ord]
|
54
|
+
expect(@editor.press_key).to eq("test #2f")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "keeps track of the cursor position" do
|
58
|
+
@input << "test #4"
|
59
|
+
@input.rewind
|
60
|
+
@editor.read
|
61
|
+
expect(@editor.line.position).to eq(7)
|
62
|
+
3.times { @editor.move_left }
|
63
|
+
expect(@editor.line.position).to eq(4)
|
64
|
+
2.times { @editor.move_right }
|
65
|
+
expect(@editor.line.position).to eq(6)
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "keeping track of the cursor position across terminal lines (e.g. multi-line editing)" do
|
69
|
+
let(:terminal_width){ 3 }
|
70
|
+
let(:terminal_height){ 7 }
|
71
|
+
let(:output){ @output.rewind ; @output.read }
|
72
|
+
let(:arrow_key_left_ansi){ "\e[D" }
|
73
|
+
let(:arrow_key_right_ansi){ "\e[C" }
|
74
|
+
|
75
|
+
before do
|
76
|
+
allow(@editor).to receive(:terminal_size).and_return [terminal_width, terminal_height]
|
77
|
+
end
|
78
|
+
|
79
|
+
context "and the cursor position is at the first character of the second line" do
|
80
|
+
before do
|
81
|
+
@input << "123"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "is at the first character of a second line" do
|
85
|
+
@input.rewind
|
86
|
+
@editor.read
|
87
|
+
expect(@editor.line.position).to eq(3)
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "moving left from the first position of the second line" do
|
91
|
+
before do
|
92
|
+
@input << arrow_key_left_ansi
|
93
|
+
@input.rewind
|
94
|
+
@editor.read
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sends the escape sequences moving the cursor to the end of the previous line" do
|
98
|
+
expected_ansi_sequence = "\e[A\e[#{terminal_width}C"
|
99
|
+
expect(output).to eq("123#{expected_ansi_sequence}\n")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "correctly sets the line's position" do
|
103
|
+
expect(@editor.line.position).to eq(2)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "moving right from the first position of the second line" do
|
108
|
+
before do
|
109
|
+
@input << arrow_key_right_ansi
|
110
|
+
@input.rewind
|
111
|
+
@editor.read
|
112
|
+
end
|
113
|
+
|
114
|
+
it "doesn't send any escape sequences" do
|
115
|
+
expected_ansi_sequence = "\e[A\e[#{terminal_width}C"
|
116
|
+
expect(output).to_not include("\e")
|
117
|
+
end
|
118
|
+
|
119
|
+
it "doesn't move the cursor when it's at the end of the input" do
|
120
|
+
expect(@editor.line.position).to eq(3)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "moving left to the previous line then right to the next line" do
|
125
|
+
before do
|
126
|
+
# this is the one that moves us to the previous line
|
127
|
+
@input << arrow_key_left_ansi
|
128
|
+
|
129
|
+
# these are for fun to show that we don't generate unnecessary
|
130
|
+
# escape sequences
|
131
|
+
@input << arrow_key_left_ansi
|
132
|
+
@input << arrow_key_left_ansi
|
133
|
+
@input << arrow_key_left_ansi
|
134
|
+
|
135
|
+
# now let's move right again
|
136
|
+
@input << arrow_key_right_ansi
|
137
|
+
@input << arrow_key_right_ansi
|
138
|
+
@input << arrow_key_right_ansi
|
139
|
+
|
140
|
+
# this is the one that puts us on the next line
|
141
|
+
@input << arrow_key_right_ansi
|
142
|
+
|
143
|
+
@input.rewind
|
144
|
+
@editor.read
|
145
|
+
end
|
146
|
+
|
147
|
+
it "sends the the escape sequence for moving to the previous line just once" do
|
148
|
+
expected_ansi_sequence = "\e[A\e[#{terminal_width}C"
|
149
|
+
expect(output.scan(expected_ansi_sequence).flatten.length).to eq(1)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "sends the the escape sequence for moving to the next line just once" do
|
153
|
+
expected_ansi_sequence = "\e[B\e[#{terminal_width}D"
|
154
|
+
expect(output.scan(expected_ansi_sequence).flatten.length).to eq(1)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "correctly sets the line's position" do
|
158
|
+
expect(@editor.line.position).to eq(3)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it "can delete characters" do
|
165
|
+
@input << "test #5"
|
166
|
+
@input.rewind
|
167
|
+
@editor.read
|
168
|
+
3.times { @editor.move_left }
|
169
|
+
4.times { @editor.delete_left_character }
|
170
|
+
3.times { @editor.delete_character }
|
171
|
+
expect(@editor.line.text).to eq("")
|
172
|
+
expect(@editor.line.position).to eq(0)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "can clear the whole line" do
|
176
|
+
@input << "test #5"
|
177
|
+
@input.rewind
|
178
|
+
@editor.read
|
179
|
+
@editor.clear_line
|
180
|
+
expect(@editor.line.text).to eq("")
|
181
|
+
expect(@editor.line.position).to eq(0)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "supports undo and redo" do
|
185
|
+
@input << "test #6"
|
186
|
+
@input.rewind
|
187
|
+
@editor.read
|
188
|
+
3.times { @editor.delete_left_character }
|
189
|
+
2.times { @editor.undo }
|
190
|
+
expect(@editor.line.text).to eq("test #")
|
191
|
+
2.times { @editor.redo }
|
192
|
+
expect(@editor.line.text).to eq("test")
|
193
|
+
end
|
194
|
+
|
195
|
+
it "supports history" do
|
196
|
+
@input << "test #7a"
|
197
|
+
@input.rewind
|
198
|
+
@editor.read "", true
|
199
|
+
@editor.newline
|
200
|
+
@input << "test #7b"
|
201
|
+
@input.pos = 8
|
202
|
+
@editor.read "", true
|
203
|
+
@editor.newline
|
204
|
+
@input << "test #7c"
|
205
|
+
@input.pos = 16
|
206
|
+
@editor.read "", true
|
207
|
+
@editor.newline
|
208
|
+
@input << "test #7d"
|
209
|
+
@input.pos = 24
|
210
|
+
@editor.read "", true
|
211
|
+
@editor.newline
|
212
|
+
@editor.history_back
|
213
|
+
expect(@editor.line.text).to eq("test #7c")
|
214
|
+
10.times { @editor.history_back }
|
215
|
+
expect(@editor.line.text).to eq("test #7a")
|
216
|
+
2.times { @editor.history_forward }
|
217
|
+
expect(@editor.line.text).to eq("test #7c")
|
218
|
+
end
|
219
|
+
|
220
|
+
it "can overwrite lines" do
|
221
|
+
@input << "test #8a"
|
222
|
+
@input.rewind
|
223
|
+
@editor.read
|
224
|
+
@editor.overwrite_line("test #8b", 2)
|
225
|
+
expect(@editor.line.text).to eq("test #8b")
|
226
|
+
expect(@editor.line.position).to eq(2)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "can complete words" do
|
230
|
+
@editor.completion_append_string = "\t"
|
231
|
+
@editor.bind(:tab) { @editor.complete }
|
232
|
+
@editor.completion_proc = lambda do |word|
|
233
|
+
if word then
|
234
|
+
['select', 'update', 'delete', 'debug', 'destroy'].find_all { |e| e.match(/^#{Regexp.escape(word)}/) }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
@input << "test #9 de" << ?\t.chr << ?\t.chr
|
238
|
+
@input.rewind
|
239
|
+
@editor.read
|
240
|
+
expect(@editor.line.text).to eq("test #9 delete\t")
|
241
|
+
end
|
242
|
+
|
243
|
+
it "supports INSERT and REPLACE modes" do
|
244
|
+
@input << "test 0"
|
245
|
+
@editor.terminal.keys[:left_arrow].each { |k| @input << k.chr }
|
246
|
+
@input << "#1"
|
247
|
+
@input.rewind
|
248
|
+
@editor.read
|
249
|
+
expect(@editor.line.text).to eq("test #10")
|
250
|
+
@editor.toggle_mode
|
251
|
+
@input << "test 0"
|
252
|
+
@editor.terminal.keys[:left_arrow].each { |k| @input << k.chr }
|
253
|
+
@input << "#1"
|
254
|
+
@input.rewind
|
255
|
+
@editor.read
|
256
|
+
expect(@editor.line.text).to eq("test #1test #1")
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
end
|