readline-ffi 0.0.2
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/.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
|