rawline 0.2.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.
- 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
|
+
|