reline 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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