readline-ffi 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.rdoc +9 -0
- data/Rakefile +37 -0
- data/ext/.gitignore +0 -0
- data/lib/ffi/readline.rb +71 -0
- data/lib/readline-ffi/version.rb +3 -0
- data/lib/readline.rb +300 -0
- data/readline-ffi.gemspec +25 -0
- data/spec/default.mspec +3 -0
- data/spec/library/readline/basic_quote_characters_spec.rb +20 -0
- data/spec/library/readline/basic_word_break_characters_spec.rb +20 -0
- data/spec/library/readline/completer_quote_characters_spec.rb +20 -0
- data/spec/library/readline/completer_word_break_characters_spec.rb +20 -0
- data/spec/library/readline/completion_append_character_spec.rb +20 -0
- data/spec/library/readline/completion_case_fold_spec.rb +22 -0
- data/spec/library/readline/completion_proc_spec.rb +26 -0
- data/spec/library/readline/constants_spec.rb +22 -0
- data/spec/library/readline/emacs_editing_mode_spec.rb +13 -0
- data/spec/library/readline/filename_quote_characters_spec.rb +20 -0
- data/spec/library/readline/history/append_spec.rb +32 -0
- data/spec/library/readline/history/delete_at_spec.rb +49 -0
- data/spec/library/readline/history/each_spec.rb +33 -0
- data/spec/library/readline/history/element_reference_spec.rb +44 -0
- data/spec/library/readline/history/element_set_spec.rb +39 -0
- data/spec/library/readline/history/empty_spec.rb +17 -0
- data/spec/library/readline/history/history_spec.rb +13 -0
- data/spec/library/readline/history/length_spec.rb +13 -0
- data/spec/library/readline/history/pop_spec.rb +34 -0
- data/spec/library/readline/history/push_spec.rb +30 -0
- data/spec/library/readline/history/shared/size.rb +14 -0
- data/spec/library/readline/history/shift_spec.rb +34 -0
- data/spec/library/readline/history/size_spec.rb +13 -0
- data/spec/library/readline/history/to_s_spec.rb +13 -0
- data/spec/library/readline/readline_spec.rb +38 -0
- data/spec/library/readline/vi_editing_mode_spec.rb +13 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +53 -0
- data/test/test_readline.rb +69 -0
- metadata +106 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009 Koichiro Ohba. All rights reserved.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'rake/clean'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'fileutils'
|
9
|
+
include FileUtils
|
10
|
+
|
11
|
+
CLEAN.include ["ext/*.{log,c,so,obj,pdb,lib,def,exp,manifest,orig}", "ext/Makefile", "*.gem"]
|
12
|
+
|
13
|
+
name="readline-ffi"
|
14
|
+
version="0.0.1"
|
15
|
+
|
16
|
+
desc "Do everything, baby!"
|
17
|
+
task :default => [:package]
|
18
|
+
|
19
|
+
task :package => [:clean,:compile,:makegem]
|
20
|
+
|
21
|
+
task :ffi_generate do
|
22
|
+
require 'ffi'
|
23
|
+
require 'ffi/tools/generator'
|
24
|
+
require 'ffi/tools/struct_generator'
|
25
|
+
|
26
|
+
ffi_files = ["lib/readline.rb.ffi"]
|
27
|
+
ffi_options = "-Ilib"
|
28
|
+
ffi_files.each do |ffi_file|
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "mspec"
|
35
|
+
task :spec do
|
36
|
+
sh "mspec -t j spec"
|
37
|
+
end
|
data/ext/.gitignore
ADDED
File without changes
|
data/lib/ffi/readline.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module FFI::Readline
|
4
|
+
extend FFI::Library
|
5
|
+
CONVENTION = FFI::Platform.windows? ? :stdcall : :default
|
6
|
+
|
7
|
+
paths = %w[
|
8
|
+
/usr/local/lib/libreadline.dylib
|
9
|
+
/opt/local/lib/libreadline.dylib
|
10
|
+
/usr/lib/libreadline.dylib
|
11
|
+
/usr/lib/libreadline.so
|
12
|
+
/lib/libreadline.so
|
13
|
+
/lib/libreadline.so.5
|
14
|
+
]
|
15
|
+
paths << File.expand_path(File.dirname(__FILE__) + "/../../ext/readline.dll")
|
16
|
+
paths.find do |path|
|
17
|
+
if File.exist?(path)
|
18
|
+
begin
|
19
|
+
ffi_lib path
|
20
|
+
true
|
21
|
+
rescue LoadError
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
ffi_convention(CONVENTION)
|
27
|
+
|
28
|
+
attach_function :readline, [:string], :string
|
29
|
+
attach_function :filename_completion_function, :rl_filename_completion_function, [:pointer, :int], :pointer
|
30
|
+
attach_function :username_completion_function, :rl_username_completion_function, [:string, :int], :string
|
31
|
+
callback :compentry_function, [:pointer, :int], :string
|
32
|
+
attach_function :completion_matches, :rl_completion_matches, [:string, :compentry_function], :pointer
|
33
|
+
attach_function :refresh_line, :rl_refresh_line, [:int, :int], :int
|
34
|
+
|
35
|
+
attach_variable :library_version, :rl_library_version, :pointer
|
36
|
+
attach_variable :readline_name, :rl_readline_name, :pointer
|
37
|
+
attach_variable :editing_mode, :rl_editing_mode, :int
|
38
|
+
attach_variable :insert_mode, :rl_insert_mode, :int
|
39
|
+
attach_variable :completion_entry_function, :rl_completion_entry_function, :pointer
|
40
|
+
attach_variable :completion_append_character, :rl_completion_append_character, :int
|
41
|
+
attach_variable :completer_quote_characters, :rl_completer_quote_characters, :pointer
|
42
|
+
attach_variable :filename_quote_characters, :rl_filename_quote_characters, :pointer
|
43
|
+
attach_variable :basic_quote_characters, :rl_basic_quote_characters, :pointer
|
44
|
+
attach_variable :basic_word_break_characters, :rl_basic_word_break_characters, :pointer
|
45
|
+
attach_variable :completer_word_break_characters, :rl_completer_word_break_characters, :pointer
|
46
|
+
callback :completion_function, [:string, :int, :int], :pointer
|
47
|
+
attach_variable :attempted_completion_function, :rl_attempted_completion_function, :completion_function
|
48
|
+
attach_variable :outstream, :rl_outstream, :pointer
|
49
|
+
attach_variable :instream, :rl_instream, :pointer
|
50
|
+
|
51
|
+
# History
|
52
|
+
class HistoryEntry < FFI::Struct
|
53
|
+
layout :line, :string, 0,
|
54
|
+
:timstamp, :string, 4,
|
55
|
+
:data, :pointer, 8
|
56
|
+
end
|
57
|
+
|
58
|
+
attach_function :using_history, [], :void
|
59
|
+
attach_function :add_history, [:string], :void
|
60
|
+
# attach_function :add_history_time, [:string], :void
|
61
|
+
attach_function :clear_history, [], :void
|
62
|
+
attach_function :remove_history, [:int], :pointer
|
63
|
+
attach_function :history_get, [:int], :pointer
|
64
|
+
attach_function :replace_history_entry, [:int, :string, :pointer], :pointer
|
65
|
+
|
66
|
+
attach_variable :history_base, :int
|
67
|
+
attach_variable :history_length, :int
|
68
|
+
attach_variable :history_max_entries, :int
|
69
|
+
end
|
70
|
+
|
71
|
+
|
data/lib/readline.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
#
|
2
|
+
# Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
3
|
+
#
|
4
|
+
# The contents of this file are subject to the Common Public
|
5
|
+
# License Version 1.0 (the "License"); you may not use this file
|
6
|
+
# except in compliance with the License. You may obtain a copy of
|
7
|
+
# the License at http://www.eclipse.org/legal/cpl-v10.html
|
8
|
+
#
|
9
|
+
# Software distributed under the License is distributed on an "AS
|
10
|
+
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
11
|
+
# implied. See the License for the specific language governing
|
12
|
+
# rights and limitations under the License.
|
13
|
+
#
|
14
|
+
# Copyright (C) 2009 Koichiro Ohba <koichiro@meadowy.org>
|
15
|
+
#
|
16
|
+
# Alternatively, the contents of this file may be used under the terms of
|
17
|
+
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
18
|
+
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
19
|
+
# in which case the provisions of the GPL or the LGPL are applicable instead
|
20
|
+
# of those above. If you wish to allow use of your version of this file only
|
21
|
+
# under the terms of either the GPL or the LGPL, and not to allow others to
|
22
|
+
# use your version of this file under the terms of the CPL, indicate your
|
23
|
+
# decision by deleting the provisions above and replace them with the notice
|
24
|
+
# and other provisions required by the GPL or the LGPL. If you do not delete
|
25
|
+
# the provisions above, a recipient may use your version of this file under
|
26
|
+
# the terms of any one of the CPL, the GPL or the LGPL.
|
27
|
+
|
28
|
+
require 'ffi/readline'
|
29
|
+
require 'readeline-ffi/version'
|
30
|
+
|
31
|
+
module Readline
|
32
|
+
|
33
|
+
# filename_completion_proc_call
|
34
|
+
FILENAME_COMPLETION_PROC = Proc.new do |text|
|
35
|
+
# p = FFI::Readline.completion_matches(text) do |word, state|
|
36
|
+
# puts word
|
37
|
+
# puts word_p.read_string
|
38
|
+
# puts state
|
39
|
+
# p = FFI::Readline.filename_completion_function(word_p, state)
|
40
|
+
# nil
|
41
|
+
# p = ::FFI::MemoryPointer.new :pointer
|
42
|
+
# p.put_string("hoge")
|
43
|
+
# p
|
44
|
+
# nil
|
45
|
+
# end
|
46
|
+
# return nil if p.null?
|
47
|
+
|
48
|
+
r = Dir.glob(text + "*")
|
49
|
+
p r
|
50
|
+
r
|
51
|
+
end
|
52
|
+
# username_completion_proc_call
|
53
|
+
USERNAME_COMPLETION_PROC = Proc.new do |text|
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Readline initialize
|
58
|
+
private
|
59
|
+
FFI::Readline.readline_name = ::FFI::MemoryPointer.from_string("Ruby")
|
60
|
+
@completion_proc = nil
|
61
|
+
@completion_case_fold = nil
|
62
|
+
|
63
|
+
def self.library_version
|
64
|
+
p = FFI::Readline.library_version
|
65
|
+
p.null? ? nil : p.read_string
|
66
|
+
end
|
67
|
+
|
68
|
+
ATTEMPTED_COMPLETION_PROC = lambda do |text, start, last|
|
69
|
+
proc = @completion_proc
|
70
|
+
return nil unless proc
|
71
|
+
|
72
|
+
candidates = proc.call(text)
|
73
|
+
return nil if candidates.empty?
|
74
|
+
|
75
|
+
result_size = candidates.length + 1
|
76
|
+
result = ::FFI::MemoryPointer.new :pointer, result_size
|
77
|
+
candidates.each_with_index do |word, i|
|
78
|
+
result[i].put_pointer(0, ::FFI::MemoryPointer.from_string(word))
|
79
|
+
end
|
80
|
+
result[result_size - 1].put_pointer(0, nil)
|
81
|
+
|
82
|
+
result
|
83
|
+
end
|
84
|
+
FFI::Readline.attempted_completion_function = ATTEMPTED_COMPLETION_PROC
|
85
|
+
|
86
|
+
public
|
87
|
+
VERSION = "#{library_version} (FFI wrapper #{ReadelineFFI::VERSION})"
|
88
|
+
|
89
|
+
def readline( prompt = "", history = nil)
|
90
|
+
raise TypeError unless STDIN.kind_of? IO
|
91
|
+
raise TypeError unless STDOUT.kind_of? IO
|
92
|
+
# TODO: STDOUT,STDIN connected in rl_outstream, rl_instream
|
93
|
+
# FFI::Readline.instream
|
94
|
+
# FFI::Readline.outstream
|
95
|
+
|
96
|
+
r = FFI::Readline.readline(prompt)
|
97
|
+
if history
|
98
|
+
FFI::Readline.add_history(r)
|
99
|
+
end
|
100
|
+
r.taint
|
101
|
+
end
|
102
|
+
module_function :readline
|
103
|
+
|
104
|
+
class History
|
105
|
+
include Enumerable
|
106
|
+
|
107
|
+
private
|
108
|
+
def initialize
|
109
|
+
FFI::Readline.using_history
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_history(index)
|
113
|
+
p = FFI::Readline.remove_history(index)
|
114
|
+
return nil if p.null?
|
115
|
+
entry = FFI::Readline::HistoryEntry.new(p)
|
116
|
+
String.new(entry[:line])
|
117
|
+
end
|
118
|
+
|
119
|
+
public
|
120
|
+
def to_s; "HISTORY" end
|
121
|
+
|
122
|
+
def [](index)
|
123
|
+
index += FFI::Readline.history_length if index < 0
|
124
|
+
p = FFI::Readline.history_get(FFI::Readline.history_base + index)
|
125
|
+
raise IndexError, "invalid index" if p.null?
|
126
|
+
entry = FFI::Readline::HistoryEntry.new(p)
|
127
|
+
entry[:line].taint
|
128
|
+
end
|
129
|
+
|
130
|
+
def []=(index, str)
|
131
|
+
index += FFI::Readline.hisrory_length if index < 0
|
132
|
+
p = FFI::Readline.replace_history_entry(index, str, nil)
|
133
|
+
raise IndexError, "invalid index" if p.null?
|
134
|
+
str
|
135
|
+
end
|
136
|
+
|
137
|
+
def <<(str)
|
138
|
+
raise TypeError unless str.respond_to?(:to_str)
|
139
|
+
|
140
|
+
FFI::Readline.add_history(str.to_str)
|
141
|
+
HISTORY
|
142
|
+
end
|
143
|
+
|
144
|
+
def push(*args)
|
145
|
+
for s in args
|
146
|
+
raise TypeError unless s.respond_to?(:to_str)
|
147
|
+
FFI::Readline.add_history(s.to_str)
|
148
|
+
end
|
149
|
+
HISTORY
|
150
|
+
end
|
151
|
+
|
152
|
+
def pop
|
153
|
+
l = FFI::Readline.history_length
|
154
|
+
return nil if l <= 0
|
155
|
+
r = remove_history(l - 1)
|
156
|
+
r.taint
|
157
|
+
end
|
158
|
+
|
159
|
+
def shift
|
160
|
+
return nil unless FFI::Readline.history_length > 0
|
161
|
+
r = remove_history(0)
|
162
|
+
r.taint
|
163
|
+
end
|
164
|
+
|
165
|
+
def each(&block)
|
166
|
+
i = 0
|
167
|
+
while i < FFI::Readline.history_length
|
168
|
+
p = FFI::Readline.history_get(FFI::Readline.history_base + i)
|
169
|
+
break if p.null?
|
170
|
+
entry = FFI::Readline::HistoryEntry.new(p)
|
171
|
+
block.call(String.new(entry[:line]).taint)
|
172
|
+
i += 1
|
173
|
+
end
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
def length
|
178
|
+
FFI::Readline.history_length
|
179
|
+
end
|
180
|
+
alias :size :length
|
181
|
+
|
182
|
+
def empty?
|
183
|
+
FFI::Readline.history_length == 0 ? true : false
|
184
|
+
end
|
185
|
+
|
186
|
+
def delete_at(index)
|
187
|
+
l = FFI::Readline.history_length
|
188
|
+
index += l if index < 0
|
189
|
+
raise IndexError, "invalid index" if (index < 0) or (index > l - 1)
|
190
|
+
r = remove_history(index)
|
191
|
+
r.taint
|
192
|
+
end
|
193
|
+
end
|
194
|
+
HISTORY = History.new
|
195
|
+
|
196
|
+
def self.basic_word_break_characters=(str)
|
197
|
+
return nil unless str
|
198
|
+
FFI::Readline.basic_word_break_characters = ::FFI::MemoryPointer.from_string(str)
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.basic_word_break_characters
|
202
|
+
p = FFI::Readline.basic_word_break_characters
|
203
|
+
p.null? ? nil : p.read_string
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.completion_append_character=(str)
|
207
|
+
if str
|
208
|
+
FFI::Readline.completion_append_character = str[0]
|
209
|
+
else
|
210
|
+
FFI::Readline.completion_append_character = 0
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.completion_append_character
|
215
|
+
ch = FFI::Readline.completion_append_character
|
216
|
+
return nil if ch == 0
|
217
|
+
ch.chr
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.completer_word_break_characters=(str)
|
221
|
+
return nil unless str
|
222
|
+
FFI::Readline.completer_word_break_characters = ::FFI::MemoryPointer.from_string(str)
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.completer_word_break_characters
|
226
|
+
p = FFI::Readline.completer_word_break_characters
|
227
|
+
p.null? ? nil : p.read_string
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.basic_quote_characters=(str)
|
231
|
+
return nil unless str
|
232
|
+
FFI::Readline.basic_quote_characters = ::FFI::MemoryPointer.from_string(str)
|
233
|
+
HISTORY
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.basic_quote_characters
|
237
|
+
p = FFI::Readline.basic_quote_characters
|
238
|
+
p.null? ? nil : p.read_string
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.completer_quote_characters=(str)
|
242
|
+
return nil unless str
|
243
|
+
FFI::Readline.completer_quote_characters = ::FFI::MemoryPointer.from_string(str)
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.completer_quote_characters
|
247
|
+
p = FFI::Readline.completer_quote_characters
|
248
|
+
p.null? ? nil : p.read_string
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.filename_quote_characters=(str)
|
252
|
+
return nil unless str
|
253
|
+
FFI::Readline.completer_quote_characters = ::FFI::MemoryPointer.from_string(str)
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.filename_quote_characters
|
257
|
+
p = FFI::Readline.filename_quote_characters
|
258
|
+
p.null? ? nil : p.read_string
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.completion_proc=(proc)
|
262
|
+
raise ArgumentError, "argument must respond to `call'" unless proc.respond_to? :call
|
263
|
+
@completion_proc = proc
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.completion_proc
|
267
|
+
@completion_proc
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.completion_case_fold=(bool)
|
271
|
+
@completion_case_fold = bool
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.completion_case_fold
|
275
|
+
@completion_case_fold
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.vi_editing_mode
|
279
|
+
FFI::Readline.editing_mode = 0
|
280
|
+
nil
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.emacs_editing_mode
|
284
|
+
FFI::Readline.editing_mode = 1
|
285
|
+
nil
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.refresh_line()
|
289
|
+
FFI::Readline.refresh_line(0, 0)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
if __FILE__ == $0
|
294
|
+
r = Readline.readline("hoge> ")
|
295
|
+
puts ">> " + r
|
296
|
+
puts FFI::Readline.editing_mode
|
297
|
+
puts Readline.emacs_editing_mode
|
298
|
+
puts Readline.completer_quote_characters
|
299
|
+
puts Readline.basic_word_break_characters
|
300
|
+
end
|