yap-rawline 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|