reline 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 34664ed4926dba843c8d0b5c161a8783384afbe37ef601a6ef7130e6acfe614f
4
+ data.tar.gz: c006411fd71550186facf348787d964f51f8e7ad03d87c7718ab2689a973b2c0
5
+ SHA512:
6
+ metadata.gz: f8307b181652977ca88bc9858533a41cdb9305b27c5fdd1584b6c9c7931e96fbf4c86c73d84b0d09cb4d27a7061adbf5e5f7c327f65864986eea2fd75c55937f
7
+ data.tar.gz: 47453f12ee20b6438751008216abfb04bf8ff06e9e1b5ab9bcb5e73319bc39aeecb1748d2ad54f5293385a04fba877f3fe6fa219d1e36afacb41851106ebf22c
data/BSDL ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
data/COPYING ADDED
@@ -0,0 +1,56 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the
3
+ 2-clause BSDL (see the file BSDL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -0,0 +1,7 @@
1
+ # Reline
2
+
3
+ Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and Editline by pure Ruby implementation.
4
+
5
+ ## License
6
+
7
+ The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
@@ -0,0 +1,196 @@
1
+ require 'io/console'
2
+ require 'reline/version'
3
+ require 'reline/config'
4
+ require 'reline/key_actor'
5
+ require 'reline/key_stroke'
6
+ require 'reline/line_editor'
7
+
8
+ module Reline
9
+ extend self
10
+ FILENAME_COMPLETION_PROC = nil
11
+ USERNAME_COMPLETION_PROC = nil
12
+ HISTORY = Array.new
13
+
14
+ if RUBY_PLATFORM =~ /mswin|mingw/
15
+ require 'Win32API'
16
+ IS_WINDOWS = true
17
+ else
18
+ IS_WINDOWS = false
19
+ end
20
+
21
+ CursorPos = Struct.new(:x, :y)
22
+
23
+ class << self
24
+ attr_accessor :basic_quote_characters
25
+ attr_accessor :completer_quote_characters
26
+ attr_accessor :completer_word_break_characters
27
+ attr_reader :completion_append_character
28
+ attr_accessor :completion_case_fold
29
+ attr_accessor :filename_quote_characters
30
+ attr_writer :input
31
+ attr_writer :output
32
+ end
33
+
34
+ @@ambiguous_width = nil
35
+ @@config = nil
36
+
37
+ @basic_quote_characters = '"\''
38
+ @completer_quote_characters
39
+ @completer_word_break_characters = @basic_word_break_characters.dup
40
+ @completion_append_character
41
+ def self.completion_append_character=(val)
42
+ if val.nil?
43
+ @completion_append_character = nil
44
+ elsif val.size == 1
45
+ @completion_append_character = val
46
+ elsif val.size > 1
47
+ @completion_append_character = val[0]
48
+ else
49
+ @completion_append_character = val
50
+ end
51
+ end
52
+ @completion_case_fold
53
+ @filename_quote_characters
54
+
55
+ @@basic_word_break_characters = " \t\n`><=;|&{("
56
+ def self.basic_word_break_characters
57
+ @@basic_word_break_characters
58
+ end
59
+ def self.basic_word_break_characters=(v)
60
+ @@basic_word_break_characters = v
61
+ end
62
+
63
+ @@completion_proc = nil
64
+ def self.completion_proc
65
+ @@completion_proc
66
+ end
67
+ def self.completion_proc=(p)
68
+ @@completion_proc = p
69
+ end
70
+
71
+ @@dig_perfect_match_proc = nil
72
+ def self.dig_perfect_match_proc
73
+ @@dig_perfect_match_proc
74
+ end
75
+ def self.dig_perfect_match_proc=(p)
76
+ @@dig_perfect_match_proc = p
77
+ end
78
+
79
+ if IS_WINDOWS
80
+ require 'reline/windows'
81
+ else
82
+ require 'reline/ansi'
83
+ end
84
+
85
+ def retrieve_completion_block(line, byte_pointer)
86
+ break_regexp = /[#{Regexp.escape(@@basic_word_break_characters)}]/
87
+ before_pointer = line.byteslice(0, byte_pointer)
88
+ break_point = before_pointer.rindex(break_regexp)
89
+ if break_point
90
+ preposing = before_pointer[0..(break_point)]
91
+ block = before_pointer[(break_point + 1)..-1]
92
+ else
93
+ preposing = ''
94
+ block = before_pointer
95
+ end
96
+ postposing = line.byteslice(byte_pointer, line.bytesize)
97
+ [preposing, block, postposing]
98
+ end
99
+
100
+ def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
101
+ if block_given?
102
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
103
+ else
104
+ inner_readline(prompt, add_hist, true)
105
+ end
106
+
107
+ if add_hist and @line_editor.whole_buffer and @line_editor.whole_buffer.chomp.size > 0
108
+ Reline::HISTORY << @line_editor.whole_buffer
109
+ end
110
+
111
+ @line_editor.whole_buffer
112
+ end
113
+
114
+ def readline(prompt = '', add_hist = false)
115
+ inner_readline(prompt, add_hist, false)
116
+
117
+ if add_hist and @line_editor.line and @line_editor.line.chomp.size > 0
118
+ Reline::HISTORY << @line_editor.line.chomp
119
+ end
120
+
121
+ @line_editor.line
122
+ end
123
+
124
+ def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
125
+ if @@config.nil?
126
+ @@config = Reline::Config.new
127
+ @@config.read
128
+ end
129
+ otio = prep
130
+
131
+ may_req_ambiguous_char_width
132
+ @line_editor = Reline::LineEditor.new(@@config, prompt)
133
+ if multiline
134
+ @line_editor.multiline_on
135
+ if block_given?
136
+ @line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
137
+ end
138
+ end
139
+ @line_editor.completion_proc = @@completion_proc
140
+ @line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
141
+ @line_editor.retrieve_completion_block = method(:retrieve_completion_block)
142
+ @line_editor.rerender
143
+
144
+ if IS_WINDOWS
145
+ config = {
146
+ key_mapping: {
147
+ [224, 72] => :ed_prev_history, # ↑
148
+ [224, 80] => :ed_next_history, # ↓
149
+ [224, 77] => :ed_next_char, # →
150
+ [224, 75] => :ed_prev_char # ←
151
+ }
152
+ }
153
+ else
154
+ config = {
155
+ key_mapping: {
156
+ [27, 91, 65] => :ed_prev_history, # ↑
157
+ [27, 91, 66] => :ed_next_history, # ↓
158
+ [27, 91, 67] => :ed_next_char, # →
159
+ [27, 91, 68] => :ed_prev_char # ←
160
+ }
161
+ }
162
+ end
163
+
164
+ key_stroke = Reline::KeyStroke.new(config)
165
+ begin
166
+ while c = getc
167
+ key_stroke.input_to!(c)&.then { |inputs|
168
+ inputs.each { |c|
169
+ @line_editor.input_key(c)
170
+ @line_editor.rerender
171
+ }
172
+ }
173
+ break if @line_editor.finished?
174
+ end
175
+ Reline.move_cursor_column(0)
176
+ rescue StandardError => e
177
+ deprep(otio)
178
+ raise e
179
+ end
180
+
181
+ deprep(otio)
182
+ end
183
+
184
+ def may_req_ambiguous_char_width
185
+ return if @@ambiguous_width
186
+ Reline.move_cursor_column(0)
187
+ print "\u{25bd}"
188
+ @@ambiguous_width = Reline.cursor_pos.x
189
+ Reline.move_cursor_column(0)
190
+ Reline.erase_after_cursor
191
+ end
192
+
193
+ def self.ambiguous_width
194
+ @@ambiguous_width
195
+ end
196
+ end
@@ -0,0 +1,87 @@
1
+ module Reline
2
+ def getc
3
+ c = nil
4
+ until c
5
+ return nil if @line_editor.finished?
6
+ result = select([$stdin], [], [], 0.1)
7
+ next if result.nil?
8
+ c = $stdin.read(1)
9
+ end
10
+ c.ord
11
+ end
12
+
13
+ def self.get_screen_size
14
+ $stdin.winsize
15
+ end
16
+
17
+ def self.set_screen_size(rows, columns)
18
+ $stdin.winsize = [rows, columns]
19
+ self
20
+ end
21
+
22
+ def self.cursor_pos
23
+ res = ''
24
+ $stdin.raw do |stdin|
25
+ $stdout << "\e[6n"
26
+ $stdout.flush
27
+ while (c = stdin.getc) != 'R'
28
+ res << c if c
29
+ end
30
+ end
31
+ m = res.match(/(?<row>\d+);(?<column>\d+)/)
32
+ CursorPos.new(m[:column].to_i - 1, m[:row].to_i - 1)
33
+ end
34
+
35
+ def self.move_cursor_column(x)
36
+ print "\e[#{x + 1}G"
37
+ end
38
+
39
+ def self.move_cursor_up(x)
40
+ if x > 0
41
+ print "\e[#{x}A" if x > 0
42
+ elsif x < 0
43
+ move_cursor_down(-x)
44
+ end
45
+ end
46
+
47
+ def self.move_cursor_down(x)
48
+ if x > 0
49
+ print "\e[#{x}B" if x > 0
50
+ elsif x < 0
51
+ move_cursor_up(-x)
52
+ end
53
+ end
54
+
55
+ def self.erase_after_cursor
56
+ print "\e[K"
57
+ end
58
+
59
+ def self.scroll_down(x)
60
+ return if x.zero?
61
+ print "\e[#{x}S"
62
+ end
63
+
64
+ def self.clear_screen
65
+ print "\e[2J"
66
+ print "\e[1;1H"
67
+ end
68
+
69
+ def prep
70
+ int_handle = Signal.trap('INT', 'IGNORE')
71
+ otio = `stty -g`.chomp
72
+ setting = ' -echo -icrnl cbreak'
73
+ if (`stty -a`.scan(/-parenb\b/).first == '-parenb')
74
+ setting << ' pass8'
75
+ end
76
+ setting << ' -ixoff'
77
+ `stty #{setting}`
78
+ Signal.trap('INT', int_handle)
79
+ otio
80
+ end
81
+
82
+ def deprep(otio)
83
+ int_handle = Signal.trap('INT', 'IGNORE')
84
+ `stty #{otio}`
85
+ Signal.trap('INT', int_handle)
86
+ end
87
+ end
@@ -0,0 +1,235 @@
1
+ require 'pathname'
2
+
3
+ class Reline::Config
4
+ DEFAULT_PATH = Pathname.new(Dir.home).join('.inputrc')
5
+
6
+ def initialize
7
+ @skip_section = nil
8
+ @if_stack = []
9
+ @editing_mode_label = :emacs
10
+ @keymap_label = :emacs
11
+ @key_actors = {}
12
+ @key_actors[:emacs] = Reline::KeyActor::Emacs.new
13
+ @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
14
+ @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
15
+ end
16
+
17
+ def reset
18
+ if editing_mode_is?(:vi_command)
19
+ @editing_mode_label = :vi_insert
20
+ end
21
+ end
22
+
23
+ def editing_mode
24
+ @key_actors[@editing_mode_label]
25
+ end
26
+
27
+ def editing_mode=(val)
28
+ @editing_mode_label = val
29
+ end
30
+
31
+ def editing_mode_is?(*val)
32
+ (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
33
+ end
34
+
35
+ def keymap
36
+ @key_actors[@keymap_label]
37
+ end
38
+
39
+ def read(file = DEFAULT_PATH)
40
+ begin
41
+ if file.respond_to?(:readlines)
42
+ lines = file.readlines
43
+ else
44
+ File.open(file, 'rt') do |f|
45
+ lines = f.readlines
46
+ end
47
+ end
48
+ rescue Errno::ENOENT
49
+ $stderr.puts "no such file #{file}"
50
+ return nil
51
+ end
52
+
53
+ read_lines(lines)
54
+ self
55
+ end
56
+
57
+ def read_lines(lines)
58
+ lines.each do |line|
59
+ line = line.chomp.gsub(/^\s*/, '')
60
+ if line[0, 1] == '$'
61
+ handle_directive(line[1..-1])
62
+ next
63
+ end
64
+
65
+ next if @skip_section
66
+
67
+ if line.match(/^set +([^ ]+) +([^ ]+)/i)
68
+ var, value = $1.downcase, $2.downcase
69
+ bind_variable(var, value)
70
+ next
71
+ end
72
+
73
+ if line =~ /\s*(.*)\s*:\s*(.*)\s*$/
74
+ key, func_name = $1, $2
75
+ bind_key(key, func_name)
76
+ end
77
+ end
78
+ end
79
+
80
+ def handle_directive(directive)
81
+ directive, args = directive.split(' ')
82
+ case directive
83
+ when 'if'
84
+ condition = false
85
+ case args # TODO: variables
86
+ when 'mode'
87
+ when 'term'
88
+ when 'version'
89
+ else # application name
90
+ condition = true if args == 'Ruby'
91
+ end
92
+ unless @skip_section.nil?
93
+ @if_stack << @skip_section
94
+ end
95
+ @skip_section = !condition
96
+ when 'else'
97
+ @skip_section = !@skip_section
98
+ when 'endif'
99
+ @skip_section = nil
100
+ unless @if_stack.empty?
101
+ @skip_section = @if_stack.pop
102
+ end
103
+ when 'include'
104
+ read(args)
105
+ end
106
+ end
107
+
108
+ def bind_variable(name, value)
109
+ case name
110
+ when %w{
111
+ bind-tty-special-chars
112
+ blink-matching-paren
113
+ byte-oriented
114
+ completion-ignore-case
115
+ convert-meta
116
+ disable-completion
117
+ enable-keypad
118
+ expand-tilde
119
+ history-preserve-point
120
+ horizontal-scroll-mode
121
+ input-meta
122
+ mark-directories
123
+ mark-modified-lines
124
+ mark-symlinked-directories
125
+ match-hidden-files
126
+ meta-flag
127
+ output-meta
128
+ page-completions
129
+ prefer-visible-bell
130
+ print-completions-horizontally
131
+ show-all-if-ambiguous
132
+ show-all-if-unmodified
133
+ visible-stats
134
+ } then
135
+ variable_name = :"@#{name.tr(?-, ?_)}"
136
+ instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
137
+ when 'bell-style'
138
+ @bell_style =
139
+ case value
140
+ when 'none', 'off'
141
+ :none
142
+ when 'audible', 'on'
143
+ :audible
144
+ when 'visible'
145
+ :visible
146
+ else
147
+ :audible
148
+ end
149
+ when 'comment-begin'
150
+ @comment_begin = value.dup
151
+ when 'completion-query-items'
152
+ @completion_query_items = value.to_i
153
+ when 'isearch-terminators'
154
+ @isearch_terminators = instance_eval(value)
155
+ when 'editing-mode'
156
+ case value
157
+ when 'emacs'
158
+ @editing_mode_label = :emacs
159
+ @keymap_label = :emacs
160
+ when 'vi'
161
+ @editing_mode_label = :vi_insert
162
+ @keymap_label = :vi_insert
163
+ end
164
+ when 'keymap'
165
+ case value
166
+ when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx'
167
+ @keymap_label = :emacs
168
+ when 'vi', 'vi-move', 'vi-command'
169
+ @keymap_label = :vi_command
170
+ when 'vi-insert'
171
+ @keymap_label = :vi_insert
172
+ end
173
+ end
174
+ end
175
+
176
+ def bind_key(key, func_name)
177
+ if key =~ /"(.*)"/
178
+ keyseq = parse_keyseq($1).force_encoding('ASCII-8BIT')
179
+ else
180
+ keyseq = nil
181
+ end
182
+ if func_name =~ /"(.*)"/
183
+ func = parse_keyseq($1).force_encoding('ASCII-8BIT')
184
+ else
185
+ func = func_name.to_sym # It must be macro.
186
+ end
187
+ [keyseq, func]
188
+ end
189
+
190
+ def key_notation_to_char(notation)
191
+ case notation
192
+ when /\\C-([A-Za-z_])/
193
+ (1 + $1.downcase.ord - ?a.ord).chr('ASCII-8BIT')
194
+ when /\\M-([0-9A-Za-z_])/
195
+ modified_key = $1
196
+ code =
197
+ case $1
198
+ when /[0-9]/
199
+ ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
200
+ when /[A-Z]/
201
+ ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
202
+ when /[a-z]/
203
+ ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
204
+ end
205
+ code.chr('ASCII-8BIT')
206
+ when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/
207
+ # 129 M-^A
208
+ when /\\(\d{1,3})/ then $1.to_i(8).chr # octal
209
+ when /\\x(\h{1,2})/ then $1.to_i(16).chr # hexadecimal
210
+ when "\\e" then ?\e
211
+ when "\\\\" then ?\\
212
+ when "\\\"" then ?"
213
+ when "\\'" then ?'
214
+ when "\\a" then ?\a
215
+ when "\\b" then ?\b
216
+ when "\\d" then ?\d
217
+ when "\\f" then ?\f
218
+ when "\\n" then ?\n
219
+ when "\\r" then ?\r
220
+ when "\\t" then ?\t
221
+ when "\\v" then ?\v
222
+ else notation
223
+ end
224
+ end
225
+
226
+ def parse_keyseq(str)
227
+ # TODO: Control- and Meta-
228
+ ret = String.new(encoding: 'ASCII-8BIT')
229
+ while str =~ /(\\C-[A-Za-z_]|\\M-[0-9A-Za-z_]|\\C-M-[A-Za-z_]|\\M-C-[A-Za-z_]|\\e|\\\\|\\"|\\'|\\a|\\b|\\d|\\f|\\n|\\r|\\t|\\v|\\\d{1,3}|\\x\h{1,2}|.)/
230
+ ret << key_notation_to_char($&)
231
+ str = $'
232
+ end
233
+ ret
234
+ end
235
+ end