rawline 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +13 -0
- data/LICENSE +11 -0
- data/README +43 -0
- data/examples/key_tester.rb +19 -0
- data/examples/rawline_shell.rb +26 -0
- data/lib/rawline/editor.rb +647 -0
- data/lib/rawline/history_buffer.rb +130 -0
- data/lib/rawline/line.rb +158 -0
- data/lib/rawline/terminal/vt220_terminal.rb +66 -0
- data/lib/rawline/terminal/windows_terminal.rb +66 -0
- data/lib/rawline/terminal.rb +87 -0
- data/lib/rawline.rb +32 -0
- data/test/test_all.rb +9 -0
- data/test/test_editor.rb +169 -0
- data/test/test_history_buffer.rb +113 -0
- data/test/test_line.rb +60 -0
- metadata +76 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# history_buffer.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
|
+
#
|
13
|
+
module RawLine
|
14
|
+
|
15
|
+
#
|
16
|
+
# The HistoryBuffer class is used to hold the editor and line histories, as well
|
17
|
+
# as word completion matches.
|
18
|
+
#
|
19
|
+
class HistoryBuffer < Array
|
20
|
+
|
21
|
+
attr_reader :position, :size
|
22
|
+
attr_accessor :duplicates, :exclude, :cycle
|
23
|
+
|
24
|
+
undef <<
|
25
|
+
|
26
|
+
#
|
27
|
+
# Create an instance of RawLine::HistoryBuffer.
|
28
|
+
# This method takes an optional block used to override the
|
29
|
+
# following instance attributes:
|
30
|
+
# * <tt>@duplicates</tt> - whether or not duplicate items will be stored in the
|
31
|
+
# buffer.
|
32
|
+
# * <tt>@exclude</tt> - a Proc object defining exclusion rules to prevent items
|
33
|
+
# from being added to the buffer.
|
34
|
+
# * <tt>@cycle</tt> - Whether or not the buffer is cyclic.
|
35
|
+
#
|
36
|
+
def initialize(size)
|
37
|
+
@duplicates = true
|
38
|
+
@exclude = lambda { nil }
|
39
|
+
@cycle = false
|
40
|
+
yield self if block_given?
|
41
|
+
@size = size
|
42
|
+
@position = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Resize the buffer, resetting <tt>@position</tt> to nil.
|
47
|
+
#
|
48
|
+
def resize(new_size)
|
49
|
+
if new_size < @size
|
50
|
+
@size-new_size.times { pop }
|
51
|
+
end
|
52
|
+
@size = new_size
|
53
|
+
@position = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Clear the content of the buffer and reset <tt>@position</tt> to nil.
|
58
|
+
#
|
59
|
+
def empty
|
60
|
+
@position = nil
|
61
|
+
clear
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Retrieve the element at <tt>@position</tt>.
|
66
|
+
#
|
67
|
+
def get
|
68
|
+
return nil unless length > 0
|
69
|
+
@position = length-1 unless @position
|
70
|
+
at @position
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Return true if <tt>@position</tt> is at the end of the buffer.
|
75
|
+
#
|
76
|
+
def end?
|
77
|
+
@position == length-1
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Return true if <tt>@position</tt> is at the start of the buffer.
|
82
|
+
#
|
83
|
+
def start?
|
84
|
+
@position == 0
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Decrement <tt>@position</tt>.
|
89
|
+
#
|
90
|
+
def back
|
91
|
+
return nil unless length > 0
|
92
|
+
case @position
|
93
|
+
when nil: @position = length-1
|
94
|
+
when 0: @position = length-1 if @cycle
|
95
|
+
else @position -= 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Increment <tt>@position</tt>.
|
101
|
+
#
|
102
|
+
def forward
|
103
|
+
return nil unless length > 0
|
104
|
+
case @position
|
105
|
+
when nil: @position = length-1
|
106
|
+
when length-1: @position = 0 if @cycle
|
107
|
+
else @position += 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Add a new item to the buffer.
|
113
|
+
#
|
114
|
+
def <<(item)
|
115
|
+
delete(item) unless @duplicates
|
116
|
+
unless @exclude.call(item)
|
117
|
+
# Remove the oldest element if size is exceeded
|
118
|
+
if @size <= length
|
119
|
+
reverse!.pop
|
120
|
+
reverse!
|
121
|
+
end
|
122
|
+
# Add the new item and reset the position
|
123
|
+
push(item)
|
124
|
+
@position = nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
data/lib/rawline/line.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# line.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
|
+
# The Line class is used to represent the current line being processed and edited
|
16
|
+
# by RawLine::Editor. It keeps track of the characters typed, the cursor position,
|
17
|
+
# the current word and maintains an internal history to allow undos and redos.
|
18
|
+
#
|
19
|
+
class Line
|
20
|
+
|
21
|
+
attr_accessor :text, :position, :history, :prompt, :history_size, :word_separator
|
22
|
+
attr_reader :offset
|
23
|
+
|
24
|
+
include HighLine::SystemExtensions
|
25
|
+
|
26
|
+
#
|
27
|
+
# Create an instance of RawLine::Line.
|
28
|
+
# This method takes an optional block used to override the
|
29
|
+
# following instance attributes:
|
30
|
+
# * <tt>@text</tt> - the line text.
|
31
|
+
# * <tt>@history_size</tt> - the size of the line history buffer.
|
32
|
+
# * <tt>@position</tt> - the current cursor position within the line.
|
33
|
+
# * <tt>@prompt</tt> - a prompt to prepend to the line text.
|
34
|
+
#
|
35
|
+
def initialize(history_size)
|
36
|
+
@text = ""
|
37
|
+
@history_size = history_size
|
38
|
+
@position = 0
|
39
|
+
@prompt = ""
|
40
|
+
@word_separator = ' '
|
41
|
+
yield self if block_given?
|
42
|
+
@words = []
|
43
|
+
@history = RawLine::HistoryBuffer.new(@history_size)
|
44
|
+
@history << "" # Add empty line for complete undo...
|
45
|
+
@offset = @prompt.length
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Return the maximum line length. By default, it corresponds to the terminal's
|
50
|
+
# width minus the length of the line prompt.
|
51
|
+
#
|
52
|
+
def max_length
|
53
|
+
terminal_size[0]-@offset
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Return information about the current word, as a Hash composed by the following
|
58
|
+
# elements:
|
59
|
+
# * <tt>:start</tt>: The position in the line corresponding to the word start
|
60
|
+
# * <tt>:end</tt>: The position in the line corresponding to the word end
|
61
|
+
# * <tt>:text</tt>: The word text.
|
62
|
+
def word
|
63
|
+
last = @text.index(@word_separator, @position)
|
64
|
+
first = @text.rindex(@word_separator, @position)
|
65
|
+
# Trim word separators and handle EOL and BOL
|
66
|
+
if first: first +=1
|
67
|
+
else first = bol
|
68
|
+
end
|
69
|
+
if last: last -=1
|
70
|
+
else last = eol+1 unless last
|
71
|
+
end
|
72
|
+
# Swap if overlapping
|
73
|
+
last, first = first, last if last < first
|
74
|
+
text = @text[first..last]
|
75
|
+
# Repeat the search if within word separator
|
76
|
+
if text.match @word_separator then
|
77
|
+
last = first
|
78
|
+
first = @text.rindex(@word_separator, first)
|
79
|
+
if first then first+=1
|
80
|
+
else first = bol
|
81
|
+
end
|
82
|
+
text = @text[first..last]
|
83
|
+
end
|
84
|
+
{:start => first, :end => last, :text => text}
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Return the position corresponding to the beginning of the line.
|
89
|
+
#
|
90
|
+
def bol
|
91
|
+
0
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Return true if the cursor is at the beginning of the line.
|
96
|
+
#
|
97
|
+
def bol?
|
98
|
+
@position<=bol
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Return the position corresponding to the end of the line.
|
103
|
+
#
|
104
|
+
def eol
|
105
|
+
@text.length-1
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Return true if the cursor is at the end of the line.
|
110
|
+
#
|
111
|
+
def eol?
|
112
|
+
@position>=eol
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Decrement the line position by <tt>offset</tt>
|
117
|
+
#
|
118
|
+
def left(offset=1)
|
119
|
+
@position = (@position-offset <= 0) ? 0 : @position-offset
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Increment the line position by <tt>offset</tt>
|
124
|
+
#
|
125
|
+
def right(offset=1)
|
126
|
+
@position = (@position+offset >= max_length) ? max_length : @position+offset
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Add a character (expressed as a character code) to the line text.
|
131
|
+
#
|
132
|
+
def <<(char)
|
133
|
+
@text << char.chr
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# Access the line text at <tt>@index</tt>
|
138
|
+
#
|
139
|
+
def [](index)
|
140
|
+
@text[index]
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Modify the character(s) in the line text at <tt>@index</tt>
|
145
|
+
#
|
146
|
+
def []=(index, chars)
|
147
|
+
@text[index] = chars
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Return the length of the line text.
|
152
|
+
#
|
153
|
+
def length
|
154
|
+
@text.length
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
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]
|
23
|
+
@keys.merge!(
|
24
|
+
{
|
25
|
+
:up_arrow => [?\e, ?[, ?A],
|
26
|
+
:down_arrow => [?\e, ?[, ?B],
|
27
|
+
:right_arrow => [?\e, ?[, ?C],
|
28
|
+
:left_arrow => [?\e, ?[, ?D],
|
29
|
+
:insert => [?\e, ?[, ?2, ?~],
|
30
|
+
:delete => [?\e, ?[, ?3, ?~],
|
31
|
+
:backspace => [?\C-?],
|
32
|
+
:enter => [?\n],
|
33
|
+
|
34
|
+
:ctrl_alt_a => [?\e, ?\C-a],
|
35
|
+
:ctrl_alt_b => [?\e, ?\C-b],
|
36
|
+
:ctrl_alt_c => [?\e, ?\C-c],
|
37
|
+
:ctrl_alt_d => [?\e, ?\C-d],
|
38
|
+
:ctrl_alt_e => [?\e, ?\C-e],
|
39
|
+
:ctrl_alt_f => [?\e, ?\C-f],
|
40
|
+
:ctrl_alt_g => [?\e, ?\C-g],
|
41
|
+
:ctrl_alt_h => [?\e, ?\C-h],
|
42
|
+
:ctrl_alt_i => [?\e, ?\C-i],
|
43
|
+
:ctrl_alt_j => [?\e, ?\C-j],
|
44
|
+
:ctrl_alt_k => [?\e, ?\C-k],
|
45
|
+
:ctrl_alt_l => [?\e, ?\C-l],
|
46
|
+
:ctrl_alt_m => [?\e, ?\C-m],
|
47
|
+
:ctrl_alt_n => [?\e, ?\C-n],
|
48
|
+
:ctrl_alt_o => [?\e, ?\C-o],
|
49
|
+
:ctrl_alt_p => [?\e, ?\C-p],
|
50
|
+
:ctrl_alt_q => [?\e, ?\C-q],
|
51
|
+
:ctrl_alt_r => [?\e, ?\C-r],
|
52
|
+
:ctrl_alt_s => [?\e, ?\C-s],
|
53
|
+
:ctrl_alt_t => [?\e, ?\C-t],
|
54
|
+
:ctrl_alt_u => [?\e, ?\C-u],
|
55
|
+
:ctrl_alt_v => [?\e, ?\C-v],
|
56
|
+
:ctrl_alt_w => [?\e, ?\C-w],
|
57
|
+
:ctrl_alt_x => [?\e, ?\C-x],
|
58
|
+
:ctrl_alt_y => [?\e, ?\C-y],
|
59
|
+
:ctrl_alt_z => [?\e, ?\C-z]
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
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
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# 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
|
+
#
|
13
|
+
module RawLine
|
14
|
+
|
15
|
+
#
|
16
|
+
# The Terminal class defines character codes and code sequences which can be
|
17
|
+
# bound to actions by editors.
|
18
|
+
# An OS-dependent subclass of RawLine::Terminal is automatically instantiated by
|
19
|
+
# RawLine::Editor.
|
20
|
+
#
|
21
|
+
class Terminal
|
22
|
+
|
23
|
+
include HighLine::SystemExtensions
|
24
|
+
|
25
|
+
attr_accessor :escape_codes
|
26
|
+
attr_reader :keys, :escape_sequences
|
27
|
+
|
28
|
+
#
|
29
|
+
# Create an instance of RawLine::Terminal.
|
30
|
+
#
|
31
|
+
def initialize
|
32
|
+
@keys =
|
33
|
+
{
|
34
|
+
:tab => [?\t],
|
35
|
+
:return => [?\r],
|
36
|
+
:newline => [?\n],
|
37
|
+
:escape => [?\e],
|
38
|
+
|
39
|
+
:ctrl_a => [?\C-a],
|
40
|
+
:ctrl_b => [?\C-b],
|
41
|
+
:ctrl_c => [?\C-c],
|
42
|
+
:ctrl_d => [?\C-d],
|
43
|
+
:ctrl_e => [?\C-e],
|
44
|
+
:ctrl_f => [?\C-f],
|
45
|
+
:ctrl_g => [?\C-g],
|
46
|
+
:ctrl_h => [?\C-h],
|
47
|
+
:ctrl_i => [?\C-i],
|
48
|
+
:ctrl_j => [?\C-j],
|
49
|
+
:ctrl_k => [?\C-k],
|
50
|
+
:ctrl_l => [?\C-l],
|
51
|
+
:ctrl_m => [?\C-m],
|
52
|
+
:ctrl_n => [?\C-n],
|
53
|
+
:ctrl_o => [?\C-o],
|
54
|
+
:ctrl_p => [?\C-p],
|
55
|
+
:ctrl_q => [?\C-q],
|
56
|
+
:ctrl_r => [?\C-r],
|
57
|
+
:ctrl_s => [?\C-s],
|
58
|
+
:ctrl_t => [?\C-t],
|
59
|
+
:ctrl_u => [?\C-u],
|
60
|
+
:ctrl_v => [?\C-v],
|
61
|
+
:ctrl_w => [?\C-w],
|
62
|
+
:ctrl_x => [?\C-x],
|
63
|
+
:ctrl_y => [?\C-y],
|
64
|
+
:ctrl_z => [?\C-z]
|
65
|
+
}
|
66
|
+
@escape_codes = []
|
67
|
+
@escape_sequences = []
|
68
|
+
update
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Update the terminal escape sequences. This method is called automatically
|
73
|
+
# by RawLine::Editor#bind().
|
74
|
+
#
|
75
|
+
def update
|
76
|
+
@keys.each_value do |k|
|
77
|
+
l = k.length
|
78
|
+
if l > 1 then
|
79
|
+
@escape_sequences << k unless @escape_sequences.include? k
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
end
|
data/lib/rawline.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# RawLine.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
|
+
HOME = File.dirname(File.expand_path(__FILE__))
|
14
|
+
class BindingException < Exception; end
|
15
|
+
begin
|
16
|
+
WIN32CONSOLE = require "win32console" if PLATFORM.match(/win32/i)
|
17
|
+
ANSI = true
|
18
|
+
rescue
|
19
|
+
ANSI = false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require "rubygems"
|
24
|
+
require "highline"
|
25
|
+
require "#{RawLine::HOME}/RawLine/terminal"
|
26
|
+
require "#{RawLine::HOME}/RawLine/terminal/windows_terminal"
|
27
|
+
require "#{RawLine::HOME}/RawLine/terminal/vt220_terminal"
|
28
|
+
require "#{RawLine::HOME}/RawLine/history_buffer"
|
29
|
+
require "#{RawLine::HOME}/RawLine/line"
|
30
|
+
require "#{RawLine::HOME}/RawLine/editor"
|
31
|
+
|
32
|
+
|
data/test/test_all.rb
ADDED
data/test/test_editor.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
module RawLine
|
4
|
+
TEST_HOME = File.dirname(File.expand_path(__FILE__))+'/..' unless const_defined?(:TEST_HOME)
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'highline/system_extensions'
|
8
|
+
|
9
|
+
module HighLine::SystemExtensions
|
10
|
+
# Override Windows' character reading so it's not tied to STDIN.
|
11
|
+
def get_character( input = STDIN )
|
12
|
+
input.getc
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'stringio'
|
17
|
+
require "#{RawLine::TEST_HOME}/lib/RawLine"
|
18
|
+
|
19
|
+
describe RawLine::Editor do
|
20
|
+
|
21
|
+
before :each do
|
22
|
+
@output = StringIO.new
|
23
|
+
@input = StringIO.new
|
24
|
+
@editor = RawLine::Editor.new(@input, @output)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "reads raw characters from @input" do
|
28
|
+
@input << "test #1"
|
29
|
+
@input.rewind
|
30
|
+
@editor.read
|
31
|
+
@editor.line.text.should == "test #1"
|
32
|
+
@output.string.should == "test #1"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can bind keys to code blocks" do
|
36
|
+
@editor.bind(:ctrl_w) { @editor.write "test #2a" }
|
37
|
+
@editor.bind(?\C-q) { "test #2b" }
|
38
|
+
@editor.bind(21) { "test #2c" }
|
39
|
+
@editor.bind([22]) { "test #2d" }
|
40
|
+
@editor.terminal.escape_codes = [] # remove any existing escape codes
|
41
|
+
lambda {@editor.bind({:test => [?\e, ?t, ?e, ?s, ?t]}) { "test #2e" }}.should raise_error(RawLine::BindingException)
|
42
|
+
@editor.terminal.escape_codes << ?\e
|
43
|
+
lambda {@editor.bind({:test => "\etest"}) { "test #2e" }}.should_not raise_error(RawLine::BindingException)
|
44
|
+
lambda {@editor.bind("\etest2") { "test #2f" }}.should_not raise_error(RawLine::BindingException)
|
45
|
+
@input << ?\C-w.chr
|
46
|
+
@input.rewind
|
47
|
+
@editor.read
|
48
|
+
@editor.line.text.should == "test #2a"
|
49
|
+
@editor.char = [?\C-q]
|
50
|
+
@editor.press_key.should == "test #2b"
|
51
|
+
@editor.char = [?\C-u]
|
52
|
+
@editor.press_key.should == "test #2c"
|
53
|
+
@editor.char = [?\C-v]
|
54
|
+
@editor.press_key.should == "test #2d"
|
55
|
+
@editor.char = [?\e, ?t, ?e, ?s, ?t]
|
56
|
+
@editor.press_key.should == "test #2e"
|
57
|
+
@editor.char = [?\e, ?t, ?e, ?s, ?t, ?2]
|
58
|
+
@editor.press_key.should == "test #2f"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "keeps track of the cursor position" do
|
62
|
+
@input << "test #4"
|
63
|
+
@input.rewind
|
64
|
+
@editor.read
|
65
|
+
@editor.line.position.should == 7
|
66
|
+
3.times { @editor.move_left }
|
67
|
+
@editor.line.position.should == 4
|
68
|
+
2.times { @editor.move_right }
|
69
|
+
@editor.line.position.should == 6
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can delete characters" do
|
73
|
+
@input << "test #5"
|
74
|
+
@input.rewind
|
75
|
+
@editor.read
|
76
|
+
3.times { @editor.move_left }
|
77
|
+
4.times { @editor.delete_left_character }
|
78
|
+
3.times { @editor.delete_character }
|
79
|
+
@editor.line.text.should == ""
|
80
|
+
@editor.line.position.should == 0
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can clear the whole line" do
|
84
|
+
@input << "test #5"
|
85
|
+
@input.rewind
|
86
|
+
@editor.read
|
87
|
+
@editor.clear_line
|
88
|
+
@editor.line.text.should == ""
|
89
|
+
@editor.line.position.should == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it "supports undo and redo" do
|
93
|
+
@input << "test #6"
|
94
|
+
@input.rewind
|
95
|
+
@editor.read
|
96
|
+
3.times { @editor.delete_left_character }
|
97
|
+
2.times { @editor.undo }
|
98
|
+
@editor.line.text.should == "test #"
|
99
|
+
2.times { @editor.redo }
|
100
|
+
@editor.line.text.should == "test"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "supports history" do
|
104
|
+
@input << "test #7a"
|
105
|
+
@input.rewind
|
106
|
+
@editor.read
|
107
|
+
@editor.newline
|
108
|
+
@input << "test #7b"
|
109
|
+
@input.pos = 8
|
110
|
+
@editor.read
|
111
|
+
@editor.newline
|
112
|
+
@input << "test #7c"
|
113
|
+
@input.pos = 16
|
114
|
+
@editor.read
|
115
|
+
@editor.newline
|
116
|
+
@input << "test #7d"
|
117
|
+
@input.pos = 24
|
118
|
+
@editor.read
|
119
|
+
@editor.newline
|
120
|
+
@editor.history_back
|
121
|
+
@editor.line.text.should == "test #7c"
|
122
|
+
10.times { @editor.history_back }
|
123
|
+
@editor.line.text.should == "test #7a"
|
124
|
+
2.times { @editor.history_forward }
|
125
|
+
@editor.line.text.should == "test #7c"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "can overwrite lines" do
|
129
|
+
@input << "test #8a"
|
130
|
+
@input.rewind
|
131
|
+
@editor.read
|
132
|
+
@editor.overwrite_line("test #8b", 2)
|
133
|
+
@editor.line.text.should == "test #8b"
|
134
|
+
@editor.line.position.should == 2
|
135
|
+
end
|
136
|
+
|
137
|
+
it "can complete words" do
|
138
|
+
@editor.completion_append_string = "\t"
|
139
|
+
@editor.bind(:tab) { @editor.complete }
|
140
|
+
@editor.completion_proc = lambda do |word|
|
141
|
+
if word
|
142
|
+
['select', 'update', 'delete', 'debug', 'destroy'].find_all { |e| e.match(/^#{Regexp.escape(word)}/) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
@input << "test #9 de" << ?\t.chr << ?\t.chr
|
146
|
+
@input.rewind
|
147
|
+
@editor.read
|
148
|
+
@editor.line.text.should == "test #9 delete\t"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "supports INSERT and REPLACE modes" do
|
152
|
+
@input << "test 0"
|
153
|
+
@editor.terminal.keys[:left_arrow].each { |k| @input << k.chr }
|
154
|
+
@input << "#1"
|
155
|
+
@input.rewind
|
156
|
+
@editor.read
|
157
|
+
@editor.line.text.should == "test #10"
|
158
|
+
@editor.toggle_mode
|
159
|
+
@input << "test 0"
|
160
|
+
@editor.terminal.keys[:left_arrow].each { |k| @input << k.chr }
|
161
|
+
@input << "#1"
|
162
|
+
@input.pos = 10
|
163
|
+
@editor.read
|
164
|
+
@editor.line.text.should == "test #1"
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
end
|
169
|
+
|