inline 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +11 -0
- data/README +43 -0
- data/lib/inline.rb +26 -0
- data/lib/inline/editor.rb +594 -0
- data/lib/inline/history_buffer.rb +130 -0
- data/lib/inline/line.rb +158 -0
- data/lib/inline/terminal.rb +87 -0
- data/lib/inline/terminal/vt220_terminal.rb +66 -0
- data/lib/inline/terminal/windows_terminal.rb +66 -0
- data/test/test_all.rb +9 -0
- metadata +70 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright (c) 2008, Fabio Cevasco
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
7
|
+
Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
8
|
+
|
9
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
10
|
+
|
11
|
+
|
data/README
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
= InLine
|
2
|
+
|
3
|
+
InLine was created to provide a 100% Ruby alternative to the ReadLine library, providing some of its most popular features such as:
|
4
|
+
|
5
|
+
* Basic line editing operations
|
6
|
+
* Word completion
|
7
|
+
* History Management
|
8
|
+
* Custom key/key sequences bindings
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
The simplest method to install InLine is to install the gem:
|
13
|
+
|
14
|
+
gem install -r inline
|
15
|
+
|
16
|
+
== Usage
|
17
|
+
|
18
|
+
Editor initialization:
|
19
|
+
|
20
|
+
require 'inline'
|
21
|
+
editor = InLine::Editor.new
|
22
|
+
|
23
|
+
Key binding:
|
24
|
+
|
25
|
+
editor.bind(:ctrl_z) { editor.undo }
|
26
|
+
editor.bind(:up_arrow) { editor.history_back }
|
27
|
+
editor.bind(:ctrl_x) { puts "Exiting..."; exit }
|
28
|
+
|
29
|
+
Setup word completion
|
30
|
+
|
31
|
+
editor.completion_proc = lambda do |word|
|
32
|
+
if word
|
33
|
+
['select', 'update', 'delete', 'debug', 'destroy'].find_all { |e| e.match(/^#{Regexp.escape(word)}/) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
editor.completion_append_string = " "
|
37
|
+
|
38
|
+
Read input:
|
39
|
+
|
40
|
+
editor.read("=> ")
|
41
|
+
|
42
|
+
|
43
|
+
|
data/lib/inline.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# inline.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 InLine
|
13
|
+
HOME = File.dirname(File.expand_path(__FILE__))
|
14
|
+
class BindingException < Exception; end
|
15
|
+
end
|
16
|
+
|
17
|
+
require "rubygems"
|
18
|
+
require "highline"
|
19
|
+
require "#{InLine::HOME}/inline/terminal"
|
20
|
+
require "#{InLine::HOME}/inline/terminal/windows_terminal"
|
21
|
+
require "#{InLine::HOME}/inline/terminal/vt220_terminal"
|
22
|
+
require "#{InLine::HOME}/inline/history_buffer"
|
23
|
+
require "#{InLine::HOME}/inline/line"
|
24
|
+
require "#{InLine::HOME}/inline/editor"
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,594 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# editor.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 InLine
|
13
|
+
|
14
|
+
#
|
15
|
+
# The Editor class defines methods to:
|
16
|
+
#
|
17
|
+
# * Read characters from STDIN or any type of input
|
18
|
+
# * Write characters to STDOUT or any type of output
|
19
|
+
# * Bind keys to specific actions
|
20
|
+
# * Perform line-related operations like moving, navigating through history, etc.
|
21
|
+
#
|
22
|
+
# Note that the following default key bindings are provided:
|
23
|
+
#
|
24
|
+
# * TAB: word completion defined via completion_proc
|
25
|
+
# * LEFT/RIGHT ARROWS: cursor movement (left/right)
|
26
|
+
# * UP/DOWN ARROWS: history navigation
|
27
|
+
# * DEL: Delete character under cursor
|
28
|
+
# * BACKSPACE: Delete character before cursor
|
29
|
+
# * INSERT: Toggle insert/replace mode (default: insert)
|
30
|
+
# * CTRL+K: Clear the whole line
|
31
|
+
# * CTRL+Z: undo (unless already registered by the OS)
|
32
|
+
# * CTRL+Y: redo (unless already registered by the OS)
|
33
|
+
#
|
34
|
+
class Editor
|
35
|
+
|
36
|
+
include HighLine::SystemExtensions
|
37
|
+
|
38
|
+
attr_accessor :char, :history_size, :line_history_size, :terminal, :keys, :word_separator, :mode, :completion_proc, :line, :history, :completion_append_string
|
39
|
+
|
40
|
+
#
|
41
|
+
# Create an instance of InLine::Editor which can be used
|
42
|
+
# to read from input and perform line-editing operations.
|
43
|
+
# This method takes an optional block used to override the
|
44
|
+
# following instance attributes:
|
45
|
+
# * <tt>@history_size</tt> - the size of the editor history buffer (30).
|
46
|
+
# * <tt>@line_history_size</tt> - the size of the editor line history
|
47
|
+
# buffer (50).
|
48
|
+
# * <tt>@keys</tt> - the keys (arrays of character codes)
|
49
|
+
# bound to specific actions.
|
50
|
+
# * <tt>@word_separator</tt> - a string used as word separator (' ').
|
51
|
+
# * <tt>@mode</tt> - The editor's character insertion mode (:insert).
|
52
|
+
# * <tt>@completion_proc</tt> - a Proc object used to perform word completion.
|
53
|
+
# * <tt>@completion_append_string</tt> - a string to append to completed words.
|
54
|
+
# * <tt>@terminal</tt> - an InLine::Terminal containing character key codes.
|
55
|
+
#
|
56
|
+
def initialize(input=STDIN, output=STDOUT)
|
57
|
+
@input = input
|
58
|
+
@output = output
|
59
|
+
case PLATFORM
|
60
|
+
when /win32/i then
|
61
|
+
@terminal = WindowsTerminal.new
|
62
|
+
else
|
63
|
+
@terminal = VT220Terminal.new
|
64
|
+
end
|
65
|
+
@history_size = 30
|
66
|
+
@line_history_size = 50
|
67
|
+
@keys = {}
|
68
|
+
@word_separator = ' '
|
69
|
+
@mode = :insert
|
70
|
+
@completion_proc = []
|
71
|
+
@completion_append_string = ' '
|
72
|
+
@completion_matches = HistoryBuffer.new(0) { |h| h.duplicates = false; h.cycle = true }
|
73
|
+
set_default_keys
|
74
|
+
yield self if block_given?
|
75
|
+
@history = HistoryBuffer.new(@history_size) do |h|
|
76
|
+
h.duplicates = false;
|
77
|
+
h.exclude = lambda { |item| item.strip == "" }
|
78
|
+
end
|
79
|
+
@char = nil
|
80
|
+
@newline = true
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Read characters from <tt>@input</tt> until the user presses ENTER
|
85
|
+
# (use it in the same way as you'd use IO#gets)
|
86
|
+
# An optional prompt can be specified to be printed at the beginning of the line.
|
87
|
+
#
|
88
|
+
def read(prompt="")
|
89
|
+
@newline = true
|
90
|
+
@line = Line.new(@line_history_size) do |l|
|
91
|
+
l.prompt = prompt
|
92
|
+
l.word_separator = @word_separator
|
93
|
+
end
|
94
|
+
add_to_line_history
|
95
|
+
loop do
|
96
|
+
print prompt if @newline
|
97
|
+
@newline = false
|
98
|
+
read_character
|
99
|
+
process_character
|
100
|
+
break if @char == @terminal.keys[:enter] || !@char
|
101
|
+
end
|
102
|
+
puts
|
103
|
+
"#{@line.text}\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Read and parse a character from <tt>@input</tt>.
|
108
|
+
# This method is called automatically by <tt>read</tt>
|
109
|
+
#
|
110
|
+
def read_character
|
111
|
+
c = get_character(@input)
|
112
|
+
@char = parse_key_code(c) || c
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Parse a key or key sequence into the corresponding codes.
|
117
|
+
# This method is called automatically by <tt>read_character</tt>
|
118
|
+
#
|
119
|
+
def parse_key_code(code)
|
120
|
+
if @terminal.escape_codes.include? code then
|
121
|
+
sequence = [code]
|
122
|
+
seqs = []
|
123
|
+
loop do
|
124
|
+
c = get_character(@input)
|
125
|
+
sequence << c
|
126
|
+
seqs = @terminal.escape_sequences.select { |e| e[0..sequence.length-1] == sequence }
|
127
|
+
break if seqs.empty?
|
128
|
+
return sequence if [sequence] == seqs
|
129
|
+
end
|
130
|
+
else
|
131
|
+
return (@terminal.keys.has_value? [code]) ? [code] : nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Write a string to <tt>@output</tt> starting from the cursor position.
|
137
|
+
# Characters at the right of the cursor are shifted to the right if
|
138
|
+
# <tt>@mode == :insert</tt>, deleted otherwise.
|
139
|
+
#
|
140
|
+
def write(string)
|
141
|
+
string.each_byte { |c| print_character c, true }
|
142
|
+
add_to_line_history
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Process a character. If the key corresponding to the inputted character
|
147
|
+
# is bound to an action, call <tt>press_key</tt>, otherwise call <tt>default_action</tt>.
|
148
|
+
# This method is called automatically by <tt>read</tt>
|
149
|
+
#
|
150
|
+
def process_character
|
151
|
+
case @char.class.to_s
|
152
|
+
when 'Fixnum' then
|
153
|
+
default_action
|
154
|
+
when 'Array'
|
155
|
+
press_key if key_bound?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Bind a key to an action specified via <tt>block</tt>.
|
161
|
+
# <tt>key</tt> can be:
|
162
|
+
#
|
163
|
+
# * A Symbol identifying a character or character sequence defined for the current terminal
|
164
|
+
# * A Fixnum identifying a character defined for the current terminal
|
165
|
+
# * An Array identifying a character or character sequence defined for the current terminal
|
166
|
+
# * A String identifying a character or character sequence defined for the current terminal
|
167
|
+
# * An Hash identifying a character or character sequence, even if it is not defined for the current terminal
|
168
|
+
#
|
169
|
+
# If <tt>key</tt> is a has, then:
|
170
|
+
#
|
171
|
+
# * It must contain only one key/value pair
|
172
|
+
# * The key identifies the name of the character or character sequence
|
173
|
+
# * The value identifies the code(s) corresponding to the character or character sequence
|
174
|
+
# * The value can be a Fixnum, a String or an Array.
|
175
|
+
#
|
176
|
+
def bind(key, &block)
|
177
|
+
case key.class.to_s
|
178
|
+
when 'Symbol' then
|
179
|
+
raise BindingException, "Unknown key or key sequence '#{key.to_s}' (#{key.class.to_s})" unless @terminal.keys[key]
|
180
|
+
@keys[@terminal.keys[key]] = block
|
181
|
+
when 'Array' then
|
182
|
+
raise BindingException, "Unknown key or key sequence '#{key.join(", ")}' (#{key.class.to_s})" unless @terminal.keys.has_value? key
|
183
|
+
@keys[key] = block
|
184
|
+
when 'Fixnum' then
|
185
|
+
raise BindingException, "Unknown key or key sequence '#{key.to_s}' (#{key.class.to_s})" unless @terminal.keys.has_value? [key]
|
186
|
+
@keys[[key]] = block
|
187
|
+
when 'String' then
|
188
|
+
raise BindingException, "Unknown key or key sequence '#{key.to_s}' (#{key.class.to_s})" unless @terminal.keys.has_value? key_array
|
189
|
+
key_array = []
|
190
|
+
key.each_byte { |b| key_array << b }
|
191
|
+
@keys[key_array] = block
|
192
|
+
when 'Hash' then
|
193
|
+
raise BindingException, "Cannot bind more than one key or key sequence at once" unless key.values.length == 1
|
194
|
+
key.each_pair do |j,k|
|
195
|
+
raise BindingException, "'#{k[0].chr}' is not a legal escape code for '#{@terminal.class.to_s}'." unless k.length > 1 && @terminal.escape_codes.include?(k[0])
|
196
|
+
code = []
|
197
|
+
case k.class.to_s
|
198
|
+
when 'Fixnum' then
|
199
|
+
code = [k]
|
200
|
+
when 'String' then
|
201
|
+
k.each_byte { |b| code << b }
|
202
|
+
when 'Array' then
|
203
|
+
code = k
|
204
|
+
else
|
205
|
+
raise BindingException, "Unable to bind '#{k.to_s}' (#{k.class.to_s})"
|
206
|
+
end
|
207
|
+
@terminal.keys[j] = code
|
208
|
+
@keys[code] = block
|
209
|
+
end
|
210
|
+
else
|
211
|
+
raise BindingException, "Unable to bind '#{key.to_s}' (#{key.class.to_s})"
|
212
|
+
end
|
213
|
+
@terminal.update
|
214
|
+
end
|
215
|
+
|
216
|
+
#
|
217
|
+
# Return true if the last character read via <tt>read</tt> is bound to an action.
|
218
|
+
#
|
219
|
+
def key_bound?
|
220
|
+
@keys[@char] ? true : false
|
221
|
+
end
|
222
|
+
|
223
|
+
#
|
224
|
+
# Call the action bound to the last character read via <tt>read</tt>.
|
225
|
+
# This method is called automatically by <tt>process_character</tt>.
|
226
|
+
#
|
227
|
+
def press_key
|
228
|
+
@keys[@char].call
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# Execute the default action for the last character read via <tt>read</tt>.
|
233
|
+
# By default it prints the character to the screen via <tt>print_character</tt>.
|
234
|
+
# This method is called automatically by <tt>process_character</tt>.
|
235
|
+
#
|
236
|
+
def default_action
|
237
|
+
print_character
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
# Write a character to <tt>@output</tt> at cursor position,
|
242
|
+
# shifting characters as appropriate.
|
243
|
+
# If <tt>no_line_history</tt> is set to <tt>true</tt>, the updated
|
244
|
+
# won't be saved in the history of the current line.
|
245
|
+
#
|
246
|
+
def print_character(char=@char, no_line_history = false)
|
247
|
+
unless @line.length >= @line.max_length-2
|
248
|
+
case
|
249
|
+
when @line.position < @line.length then
|
250
|
+
chars = select_characters_from_cursor if @mode == :insert
|
251
|
+
@output.putc char
|
252
|
+
@line.text[@line.position] = (@mode == :insert) ? "#{char.chr}#{@line.text[@line.position].chr}" : "#{char.chr}"
|
253
|
+
@line.right
|
254
|
+
if @mode == :insert then
|
255
|
+
raw_print chars
|
256
|
+
chars.length.times { putc ?\b } # move cursor back
|
257
|
+
end
|
258
|
+
else
|
259
|
+
@output.putc char
|
260
|
+
@line.right
|
261
|
+
@line << char
|
262
|
+
end
|
263
|
+
add_to_line_history unless no_line_history
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# Complete the current word according to what returned by
|
269
|
+
# <tt>@completion_proc</tt>. Characters can be appended to the
|
270
|
+
# completed word via <tt>@completion_append_character</tt> and word
|
271
|
+
# separators can be defined via <tt>@word_separator</tt>.
|
272
|
+
#
|
273
|
+
# This action is bound to the tab key by default, so the first
|
274
|
+
# match is displayed the first time the user presses tab, and all
|
275
|
+
# the possible messages will be displayed (cyclically) when tab is
|
276
|
+
# pressed again.
|
277
|
+
#
|
278
|
+
def complete
|
279
|
+
completion_char = @char
|
280
|
+
@completion_matches.empty
|
281
|
+
word_start = @line.word[:start]
|
282
|
+
sub_word = @line.text[@line.word[:start]..@line.position-1] || ""
|
283
|
+
matches = @completion_proc.call(sub_word)
|
284
|
+
matches = (matches.is_a?(Array)) ? matches.sort.reverse : []
|
285
|
+
complete_word = lambda do |match|
|
286
|
+
unless @line.word[:text].length == 0
|
287
|
+
# If not in a word, print the match, otherwise continue existing word
|
288
|
+
move_to_position(@line.word[:end]+@completion_append_string.length+1)
|
289
|
+
end
|
290
|
+
(@line.position-word_start).times { delete_left_character(true) }
|
291
|
+
write match+@completion_append_string
|
292
|
+
end
|
293
|
+
unless matches.empty? then
|
294
|
+
@completion_matches.resize(matches.length)
|
295
|
+
matches.each { |w| @completion_matches << w }
|
296
|
+
# Get first match
|
297
|
+
@completion_matches.back
|
298
|
+
match = @completion_matches.get
|
299
|
+
complete_word.call(match)
|
300
|
+
read_character
|
301
|
+
while @char == completion_char do
|
302
|
+
move_to_position(word_start)
|
303
|
+
@completion_matches.back
|
304
|
+
match = @completion_matches.get
|
305
|
+
complete_word.call(match)
|
306
|
+
read_character
|
307
|
+
end
|
308
|
+
process_character
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
#
|
313
|
+
# Adds <tt>@line.text</tt> to the editor history. This action is
|
314
|
+
# bound to the enter key by default.
|
315
|
+
#
|
316
|
+
def newline
|
317
|
+
add_to_history
|
318
|
+
end
|
319
|
+
|
320
|
+
#
|
321
|
+
# Move the cursor left (if possible) by printing a
|
322
|
+
# backspace, updating <tt>@line.position</tt> accordingly.
|
323
|
+
# This action is bound to the left arrow key by default.
|
324
|
+
#
|
325
|
+
def move_left
|
326
|
+
unless @line.bol?:
|
327
|
+
@output.putc ?\b
|
328
|
+
@line.left
|
329
|
+
return true
|
330
|
+
end
|
331
|
+
false
|
332
|
+
end
|
333
|
+
|
334
|
+
#
|
335
|
+
# Move the cursor right (if possible) by re-printing the
|
336
|
+
# character at the right of the cursor, if any, and updating
|
337
|
+
# <tt>@line.position</tt> accordingly.
|
338
|
+
# This action is bound to the right arrow key by default.
|
339
|
+
#
|
340
|
+
def move_right
|
341
|
+
unless @line.position > @line.eol:
|
342
|
+
@line.right
|
343
|
+
@output.putc @line.text[@line.position-1]
|
344
|
+
return true
|
345
|
+
end
|
346
|
+
false
|
347
|
+
end
|
348
|
+
|
349
|
+
#
|
350
|
+
# Print debug information about the current line. Note that after
|
351
|
+
# the message is displayed, the line text and position will be restored.
|
352
|
+
#
|
353
|
+
def debug_line
|
354
|
+
pos = @line.position
|
355
|
+
text = @line.text
|
356
|
+
word = @line.word
|
357
|
+
@output.puts
|
358
|
+
@output.puts "Text: [#{text}]"
|
359
|
+
@output.puts "Length: #{@line.length}"
|
360
|
+
@output.puts "Position: #{pos}"
|
361
|
+
@output.puts "Character at Position: [#{text[pos].chr}] (#{text[pos]})" unless pos >= @line.length
|
362
|
+
@output.puts "Current Word: [#{word[:text]}] (#{word[:start]} -- #{word[:end]})"
|
363
|
+
clear_line
|
364
|
+
raw_print text
|
365
|
+
overwrite_line(text, pos)
|
366
|
+
end
|
367
|
+
|
368
|
+
#
|
369
|
+
# Print the content of the editor history. Note that after
|
370
|
+
# the message is displayed, the line text and position will be restored.
|
371
|
+
#
|
372
|
+
def show_history
|
373
|
+
pos = @line.position
|
374
|
+
text = @line.text
|
375
|
+
@output.puts
|
376
|
+
@output.puts "History:"
|
377
|
+
@history.each {|l| puts "- [#{l}]"}
|
378
|
+
overwrite_line(text, pos)
|
379
|
+
end
|
380
|
+
|
381
|
+
#
|
382
|
+
# Clear the editor history.
|
383
|
+
#
|
384
|
+
def clear_history
|
385
|
+
@history.empty
|
386
|
+
end
|
387
|
+
|
388
|
+
#
|
389
|
+
# Delete the character at the left of the cursor.
|
390
|
+
# If <tt>no_line_hisytory</tt> is set to true, the deletion won't be
|
391
|
+
# recorded in the line history.
|
392
|
+
# This action is bound to the backspace key by default.
|
393
|
+
#
|
394
|
+
def delete_left_character(no_line_history=false)
|
395
|
+
if move_left then
|
396
|
+
delete_character(no_line_history)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
#
|
401
|
+
# Delete the character under the cursor.
|
402
|
+
# If <tt>no_line_hisytory</tt> is set to true, the deletion won't be
|
403
|
+
# recorded in the line history.
|
404
|
+
# This action is bound to the delete key by default.
|
405
|
+
#
|
406
|
+
def delete_character(no_line_history=false)
|
407
|
+
unless @line.position > @line.eol
|
408
|
+
# save characters to shift
|
409
|
+
chars = (@line.eol?) ? ' ' : select_characters_from_cursor(1)
|
410
|
+
# remove character from console and shift characters
|
411
|
+
raw_print chars
|
412
|
+
putc ?\s
|
413
|
+
(chars.length+1).times { putc ?\b }
|
414
|
+
#remove character from line
|
415
|
+
@line[@line.position] = ''
|
416
|
+
add_to_line_history unless no_line_history
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
#
|
421
|
+
# Clear the current line, i.e.
|
422
|
+
# <tt>@line.text</tt> and <tt>@line.position</tt>.
|
423
|
+
# This action is bound to ctrl+k by default.
|
424
|
+
#
|
425
|
+
def clear_line
|
426
|
+
@output.putc ?\r
|
427
|
+
raw_print @line.prompt
|
428
|
+
@line.length.times { putc ?\s }
|
429
|
+
@line.length.times { putc ?\b }
|
430
|
+
add_to_line_history
|
431
|
+
@line.text = ""
|
432
|
+
@line.position = 0
|
433
|
+
end
|
434
|
+
|
435
|
+
#
|
436
|
+
# Undo the last modification to the current line (<tt>@line.text</tt>).
|
437
|
+
# This action is bound to ctrl+z by default.
|
438
|
+
#
|
439
|
+
def undo
|
440
|
+
generic_history_back(@line.history) if @line.history.position == nil
|
441
|
+
generic_history_back(@line.history)
|
442
|
+
end
|
443
|
+
|
444
|
+
#
|
445
|
+
# Redo a previously-undone modification to the
|
446
|
+
# current line (<tt>@line.text</tt>).
|
447
|
+
# This action is bound to ctrl+y by default.
|
448
|
+
#
|
449
|
+
def redo
|
450
|
+
generic_history_forward(@line.history)
|
451
|
+
end
|
452
|
+
|
453
|
+
#
|
454
|
+
# Load the previous entry of the editor in place of the
|
455
|
+
# current line (<tt>@line.text</tt>).
|
456
|
+
# This action is bound to the up arrow key by default.
|
457
|
+
#
|
458
|
+
def history_back
|
459
|
+
unless @history.position
|
460
|
+
current_line = @line.text.dup
|
461
|
+
# Temporarily override exclusion rules
|
462
|
+
exclude = @history.exclude.dup
|
463
|
+
@history.exclude = lambda { nil }
|
464
|
+
# Add current line
|
465
|
+
@history << current_line
|
466
|
+
@history.exclude = exclude
|
467
|
+
@history.back
|
468
|
+
end
|
469
|
+
generic_history_back(@history)
|
470
|
+
add_to_line_history
|
471
|
+
end
|
472
|
+
|
473
|
+
#
|
474
|
+
# Load the next entry of the editor history in place of the
|
475
|
+
# current line (<tt>@line.text</tt>).
|
476
|
+
# This action is bound to down arrow key by default.
|
477
|
+
#
|
478
|
+
def history_forward
|
479
|
+
generic_history_forward(@history)
|
480
|
+
add_to_line_history
|
481
|
+
end
|
482
|
+
|
483
|
+
#
|
484
|
+
# Add the current line (<tt>@line.text</tt>) to the
|
485
|
+
# line history, to allow undo/redo
|
486
|
+
# operations.
|
487
|
+
#
|
488
|
+
def add_to_line_history
|
489
|
+
@line.history << @line.text.dup unless @line.text == ""
|
490
|
+
end
|
491
|
+
|
492
|
+
#
|
493
|
+
# Add the current line (<tt>@line.text</tt>) to the editor history.
|
494
|
+
#
|
495
|
+
def add_to_history
|
496
|
+
@history << @line.text.dup unless @line.text == ""
|
497
|
+
end
|
498
|
+
|
499
|
+
#
|
500
|
+
# Toggle the editor <tt>@mode</tt> to :replace or :insert (default).
|
501
|
+
#
|
502
|
+
def toggle_mode
|
503
|
+
case @mode
|
504
|
+
when :insert then @mode = :replace
|
505
|
+
when :replace then @mode = :insert
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
#
|
510
|
+
# Overwrite the current line (<tt>@line.text</tt>)
|
511
|
+
# with <tt>new_line</tt>, and optionally reset the cursor position to
|
512
|
+
# <tt>position</tt>.
|
513
|
+
#
|
514
|
+
def overwrite_line(new_line, position=nil)
|
515
|
+
pos = position || new_line.length
|
516
|
+
text = @line.text
|
517
|
+
putc ?\r
|
518
|
+
raw_print @line.prompt
|
519
|
+
raw_print new_line
|
520
|
+
n = text.length-new_line.length+1
|
521
|
+
if n > 0
|
522
|
+
n.times { putc ?\s }
|
523
|
+
n.times { putc ?\b }
|
524
|
+
end
|
525
|
+
@line.position = new_line.length
|
526
|
+
move_to_position(pos)
|
527
|
+
@line.text = new_line
|
528
|
+
end
|
529
|
+
|
530
|
+
#
|
531
|
+
# Move the cursor to <tt>pos</tt>.
|
532
|
+
#
|
533
|
+
def move_to_position(pos)
|
534
|
+
n = pos-@line.position
|
535
|
+
case
|
536
|
+
when n > 0 then
|
537
|
+
n.times { move_right }
|
538
|
+
when n < 0 then
|
539
|
+
n.abs.times {move_left}
|
540
|
+
when n == 0 then
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
private
|
545
|
+
|
546
|
+
def select_characters_from_cursor(offset=0)
|
547
|
+
select_characters(:right, @line.length-@line.position, offset)
|
548
|
+
end
|
549
|
+
|
550
|
+
def raw_print(string)
|
551
|
+
string.each_byte { |c| @output.putc c }
|
552
|
+
end
|
553
|
+
|
554
|
+
def generic_history_back(history)
|
555
|
+
unless history.empty?
|
556
|
+
history.back
|
557
|
+
line = history.get
|
558
|
+
overwrite_line(line)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
def generic_history_forward(history)
|
563
|
+
if history.forward then
|
564
|
+
overwrite_line(history.get)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def select_characters(direction, n, offset=0)
|
569
|
+
if direction == :right then
|
570
|
+
@line.text[@line.position+offset..@line.position+offset+n]
|
571
|
+
elsif direction == :left then
|
572
|
+
@line.text[@line.position-offset-n..@line.position-offset]
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def set_default_keys
|
577
|
+
bind(:return) { newline }
|
578
|
+
bind(:tab) { complete }
|
579
|
+
bind(:backspace) { delete_left_character }
|
580
|
+
bind(:ctrl_k) { clear_line }
|
581
|
+
bind(:ctrl_z) { undo }
|
582
|
+
bind(:ctrl_y) { self.redo }
|
583
|
+
bind(:left_arrow) { move_left }
|
584
|
+
bind(:right_arrow) { move_right }
|
585
|
+
bind(:up_arrow) { history_back }
|
586
|
+
bind(:down_arrow) { history_forward }
|
587
|
+
bind(:delete) { delete_character }
|
588
|
+
bind(:insert) { toggle_mode }
|
589
|
+
end
|
590
|
+
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
|
@@ -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 InLine
|
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 InLine::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/inline/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 InLine
|
13
|
+
|
14
|
+
#
|
15
|
+
# The Line class is used to represent the current line being processed and edited
|
16
|
+
# by InLine::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 InLine::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 = InLine::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,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 InLine
|
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 InLine::Terminal is automatically instantiated by
|
19
|
+
# InLine::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 InLine::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 InLine::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
|
@@ -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 InLine
|
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 InLine
|
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, 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/test/test_all.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: inline
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2008-03-15 00:00:00 +01:00
|
8
|
+
summary: A library for definign custom key bindings and perform line editing operations
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: h3rald@h3rald.com
|
12
|
+
homepage: http://rubyforge.org/projects/inline
|
13
|
+
rubyforge_project: inline
|
14
|
+
description: InLine can be used to define custom key bindings, perform common line editing operations, manage command history and define custom command completion rules.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Fabio Cevasco
|
31
|
+
files:
|
32
|
+
- lib/inline
|
33
|
+
- lib/inline/editor.rb
|
34
|
+
- lib/inline/history_buffer.rb
|
35
|
+
- lib/inline/line.rb
|
36
|
+
- lib/inline/terminal
|
37
|
+
- lib/inline/terminal/vt220_terminal.rb
|
38
|
+
- lib/inline/terminal/windows_terminal.rb
|
39
|
+
- lib/inline/terminal.rb
|
40
|
+
- lib/inline.rb
|
41
|
+
- README
|
42
|
+
- LICENSE
|
43
|
+
- CHANGELOG
|
44
|
+
test_files:
|
45
|
+
- test/test_all.rb
|
46
|
+
rdoc_options:
|
47
|
+
- --main
|
48
|
+
- README
|
49
|
+
- --exclude
|
50
|
+
- test
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
- LICENSE
|
54
|
+
- CHANGELOG
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
dependencies:
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: highline
|
64
|
+
version_requirement:
|
65
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.4.0
|
70
|
+
version:
|