katakata_irb 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d3d6ff924cd8fa8f05b097253525517a988c377d59e63ed1e5c6e5867cfde88
4
- data.tar.gz: af6afd4a833e2b9bfd08a05877db80e45faadcac845be5148b53e3266d5539d8
3
+ metadata.gz: fb7e44af7218463a13dd4e072748d5880924a3b5678f8c30200f3e5bfde391cd
4
+ data.tar.gz: c0a6c33286b4a8197ad594f16baa87892448ea487e2faa153cac4d8f48d4208f
5
5
  SHA512:
6
- metadata.gz: 35b76a766b99913226c5888b00efcc1c88c35ecb1bbb38338cf69a47772153caecb204bf5cad0a4f7affb475a179b0bd084ecd673c793260165731eef88a258b
7
- data.tar.gz: e2dc520f4d5fe10a95795d5cbcdb784289e674c415a8eddea2c06883262f02847a5fd5b4fb7dc83d219e8a2add95e5fb5ed7c3d7000b4c83ea539825d50c5f20
6
+ metadata.gz: c79ea66192ad0c3f671a8cbaa9cf2d497b6b662482615a6f497e9a86ad6f797cc4d7b8d228bd44afd4c0968736a1ea83630311d2056b3bd4ae4befc1d2c94609
7
+ data.tar.gz: d3cde5bdd7a966ecf6a06e82bdd9f276664e513999902e37e3abd1a0870452e4eb8265c43949ce8c7f110e6786980a79e4b07abaf0dd7880c37dee39a27865c3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- katakata_irb (0.1.2)
4
+ katakata_irb (0.1.3)
5
5
  rbs
6
6
 
7
7
  GEM
@@ -3,7 +3,7 @@ module KatakataIrb::RelinePatch
3
3
  module RelinePatchIseqLoader; end
4
4
  def self.require_patched_reline
5
5
  # Apply patches of unmerged pull-request to reline
6
- patches = %w[wholelines escapeseq indent fullwidth raw scrollbar]
6
+ patches = %w[wholelines escapeseq indent raw scrollbar]
7
7
  patched = {}
8
8
  require 'reline/version.rb' # result of $LOAD_PATH.resolve_feature_path will change after this require
9
9
  patches.each do |patch_name|
@@ -1,18 +1,8 @@
1
1
  diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
2
- index e3985a3..8d785db 100644
2
+ index 8153aab..50a063a 100644
3
3
  --- a/lib/reline/line_editor.rb
4
4
  +++ b/lib/reline/line_editor.rb
5
- @@ -680,6 +680,9 @@ class Reline::LineEditor
6
- end
7
- height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
8
- height = dialog.contents.size if dialog.contents.size < height
9
- + if dialog.scroll_top >= dialog.contents.size - height
10
- + dialog.scroll_top = dialog.contents.size - height
11
- + end
12
- if dialog.contents.size > height
13
- if dialog.pointer
14
- if dialog.pointer < 0
15
- @@ -690,17 +693,20 @@ class Reline::LineEditor
5
+ @@ -703,17 +703,17 @@ class Reline::LineEditor
16
6
  dialog.scroll_top = dialog.pointer
17
7
  end
18
8
  pointer = dialog.pointer - dialog.scroll_top
@@ -23,9 +13,7 @@ index e3985a3..8d785db 100644
23
13
  end
24
14
  - if dialog.contents and dialog.scroll_top >= dialog.contents.size
25
15
  - dialog.scroll_top = dialog.contents.size - height
26
- + if dialog_render_info.contents and dialog.scroll_top > dialog_render_info.contents.size - height
27
- + dialog.scroll_top = dialog_render_info.contents.size - height
28
- end
16
+ - end
29
17
  if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
30
18
  bar_max_height = height * 2
31
19
  moving_distance = (dialog_render_info.contents.size - height) * 2
@@ -35,7 +23,10 @@ index e3985a3..8d785db 100644
35
23
  dialog.scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i
36
24
  else
37
25
  dialog.scrollbar_pos = nil
38
- @@ -745,4 +751,4 @@ class Reline::LineEditor
26
+ @@ -755,7 +755,7 @@ class Reline::LineEditor
27
+ str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
28
+ str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
29
+ @output.write "\e[#{bg_color}m\e[#{fg_color}m#{str}"
39
30
  - if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
40
31
  + if dialog.scrollbar_pos
41
32
  @output.write "\e[37m"
@@ -84,16 +84,17 @@ module KatakataIrb::RubyLexPatch
84
84
 
85
85
  if first_token && first_token.state != Ripper::EXPR_DOT
86
86
  tokens_without_last_line = tokens[0..index]
87
- if check_termination(tokens_without_last_line)
87
+ if check_termination(tokens_without_last_line.map(&:tok).join, context: context)
88
88
  return last_line_tokens.map(&:tok).join
89
89
  end
90
90
  end
91
91
  false
92
92
  end
93
93
 
94
- def check_termination(tokens)
94
+ def check_termination(code, context: nil)
95
+ tokens = KatakataIrb::RubyLexPatch.complete_tokens(code, context: context)
95
96
  opens = KatakataIrb::TRex.parse(tokens)
96
- opens.empty? && !process_continue(tokens)
97
+ opens.empty? && !process_continue(tokens) && !check_code_block(code, tokens)
97
98
  end
98
99
 
99
100
  def set_input(io, p = nil, context: nil, &block)
@@ -101,8 +102,7 @@ module KatakataIrb::RubyLexPatch
101
102
  if @io.respond_to?(:check_termination)
102
103
  @io.check_termination do |code|
103
104
  if Reline::IOGate.in_pasting?
104
- lex = RubyLex.new
105
- rest = lex.check_termination_in_prev_line(code, context: context)
105
+ rest = check_termination_in_prev_line(code, context: context)
106
106
  if rest
107
107
  Reline.delete_text
108
108
  rest.bytes.reverse_each do |c|
@@ -113,8 +113,14 @@ module KatakataIrb::RubyLexPatch
113
113
  false
114
114
  end
115
115
  else
116
- tokens = KatakataIrb::RubyLexPatch.complete_tokens(code, context: context)
117
- check_termination(tokens)
116
+ # Accept any single-line input for symbol aliases or commands that transform args
117
+ command = code.split(/\s/, 2).first
118
+ if context.symbol_alias?(command) || context.transform_args?(command)
119
+ next true
120
+ end
121
+
122
+ code.gsub!(/\s*\z/, '').concat("\n")
123
+ check_termination(code, context: context)
118
124
  end
119
125
  end
120
126
  end
@@ -130,9 +136,7 @@ module KatakataIrb::RubyLexPatch
130
136
  line_tokens.each do |token, _s|
131
137
  tokens_until_line << token if token != tokens_until_line.last
132
138
  end
133
- unless (c = process_continue(tokens_until_line)).nil?
134
- continue = c
135
- end
139
+ continue = process_continue(tokens_until_line)
136
140
  prompt next_opens, continue, line_num_offset
137
141
  end
138
142
  end
@@ -152,7 +156,7 @@ module KatakataIrb::RubyLexPatch
152
156
  @io.auto_indent do |lines, line_index, byte_pointer, is_newline|
153
157
  if is_newline
154
158
  tokens = KatakataIrb::RubyLexPatch.complete_tokens(lines[0..line_index].join("\n"), context: context)
155
- process_indent_level tokens
159
+ process_indent_level(tokens)
156
160
  else
157
161
  code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
158
162
  last_line = lines[line_index]&.byteslice(0, byte_pointer)
@@ -170,19 +174,20 @@ module KatakataIrb::RubyLexPatch
170
174
  @prompt.call(ltype, nesting_level, opens.any? || continue, @line_no + line_num_offset)
171
175
  end
172
176
 
173
- def store_prompt_to_irb(...)
174
- prompt(...) # TODO: do not use this. change the api. example: @input.call(prompt)
177
+ # TODO: do not use this. change the api. example: @input.call(prompt)
178
+ def store_prompt_to_irb(opens, continue, line_num_offset)
179
+ prompt(opens, continue, line_num_offset)
175
180
  end
176
181
 
177
182
  def readmultiline(context)
178
183
  if @io.respond_to? :check_termination
179
- store_prompt_to_irb [], false, 0
184
+ store_prompt_to_irb([], false, 0)
180
185
  @input.call
181
186
  else
182
187
  # nomultiline
183
188
  line = ''
184
189
  line_offset = 0
185
- store_prompt_to_irb [], false, 0
190
+ store_prompt_to_irb([], false, 0)
186
191
  loop do
187
192
  l = @input.call
188
193
  unless l
@@ -194,7 +199,7 @@ module KatakataIrb::RubyLexPatch
194
199
  _line_tokens, _prev_opens, next_opens = KatakataIrb::TRex.parse_line(tokens).last
195
200
  return line if next_opens.empty?
196
201
  line_offset += 1
197
- store_prompt_to_irb next_opens, true, line_offset
202
+ store_prompt_to_irb(next_opens, true, line_offset)
198
203
  end
199
204
  end
200
205
  end
@@ -37,75 +37,92 @@ module KatakataIrb::TRex
37
37
  case state
38
38
  when :in_unquoted_symbol
39
39
  opens.pop
40
- skip = true if %i[on_ident on_const on_op on_cvar on_ivar on_gvar on_kw on_int on_backtick].include? t.event
40
+ skip = true if %i[on_ident on_const on_op on_cvar on_ivar on_gvar on_kw on_int on_backtick].include?(t.event)
41
41
  when :in_method_head
42
- unless %i[on_sp on_ignored_nl].include?(t.event)
42
+ unless %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end].include?(t.event)
43
43
  next_args = []
44
44
  body = nil
45
- if args.include? :receiver
45
+ if args.include?(:receiver)
46
46
  case t.event
47
47
  when :on_lparen, :on_ivar, :on_gvar, :on_cvar
48
+ # def (receiver). | def @ivar. | def $gvar. | def @@cvar.
48
49
  next_args << :dot
49
50
  when :on_kw
50
- if t.tok in 'self' | 'true' | 'false' | 'nil'
51
- next_args.push :arg, :dot
51
+ case t.tok
52
+ when 'self', 'true', 'false', 'nil'
53
+ # def self(arg) | def self.
54
+ next_args.push(:arg, :dot)
52
55
  else
56
+ # def if(arg)
53
57
  skip = true
54
58
  next_args << :arg
55
59
  end
56
- when :on_op
60
+ when :on_op, :on_backtick
61
+ # def +(arg)
57
62
  skip = true
58
63
  next_args << :arg
59
64
  when :on_ident, :on_const
60
- next_args.push :arg, :dot
65
+ # def a(arg) | def a.
66
+ next_args.push(:arg, :dot)
61
67
  end
62
68
  end
63
- if args.include? :dot
69
+ if args.include?(:dot)
70
+ # def receiver.name
64
71
  next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
65
72
  end
66
- if args.include? :name
67
- if %i[on_ident on_const on_op on_kw].include? t.event
73
+ if args.include?(:name)
74
+ if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
75
+ # def name(arg) | def receiver.name(arg)
68
76
  next_args << :arg
69
77
  skip = true
70
78
  end
71
79
  end
72
- if args.include? :arg
80
+ if args.include?(:arg)
73
81
  case t.event
74
- when :on_op
75
- body = :oneliner if t.tok == '='
76
82
  when :on_nl, :on_semicolon
83
+ # def recever.f;
77
84
  body = :normal
78
85
  when :on_lparen
86
+ # def recever.f()
79
87
  next_args << :eq
80
88
  else
81
- next_args << :arg_without_paren
89
+ if t.event == :on_op && t.tok == '='
90
+ # def receiver.f =
91
+ body = :oneliner
92
+ else
93
+ # def recever.f arg
94
+ next_args << :arg_without_paren
95
+ end
82
96
  end
83
97
  end
84
- if args.include? :eq
98
+ if args.include?(:eq)
85
99
  if t.event == :on_op && t.tok == '='
86
100
  body = :oneliner
87
- elsif t.event != :on_embdoc_beg
101
+ else
88
102
  body = :normal
89
103
  end
90
104
  end
91
- if args.include? :args_without_paren
92
- body = :normal if %i[on_semicolon on_nl].include? t.event
105
+ if args.include?(:arg_without_paren)
106
+ if %i[on_semicolon on_nl].include?(t.event)
107
+ # def f a;
108
+ body = :normal
109
+ else
110
+ # def f a, b
111
+ next_args << :arg_without_paren
112
+ end
93
113
  end
94
114
  if body == :oneliner
95
115
  opens.pop
96
116
  elsif body
97
- opens.pop
98
- opens << [last_tok, nil]
117
+ opens[-1] = [last_tok, nil]
99
118
  else
100
- opens.pop
101
- opens << [last_tok, :in_method_head, next_args]
119
+ opens[-1] = [last_tok, :in_method_head, next_args]
102
120
  end
103
121
  end
104
122
  when :in_for_while_until_condition
105
123
  if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
106
124
  skip = true if t.event == :on_kw && t.tok == 'do'
107
- opens.pop
108
- opens << [last_tok, nil]
125
+ opens[-1] = [last_tok, nil]
109
126
  end
110
127
  end
111
128
 
@@ -184,10 +201,10 @@ module KatakataIrb::TRex
184
201
  prev_opens = []
185
202
  min_depth = 0
186
203
  output = []
187
- last_opens = KatakataIrb::TRex.parse(tokens) do |t, _index, opens|
204
+ last_opens = parse(tokens) do |t, _index, opens|
188
205
  depth = t == opens.last&.first ? opens.size - 1 : opens.size
189
206
  min_depth = depth if depth < min_depth
190
- if t.tok.include? "\n"
207
+ if t.tok.include?("\n")
191
208
  t.tok.each_line do |line|
192
209
  line_tokens << [t, line]
193
210
  next if line[-1] != "\n"
@@ -293,7 +293,7 @@ class KatakataIrb::TypeSimulator
293
293
  sexp in [:def, _method_name_exp, params, body_stmt]
294
294
  sexp in [:defs, receiver_exp, _dot_exp, _method_name_exp, params, body_stmt]
295
295
  if receiver_exp
296
- receiver_exp in [:paren, receiver_exp]
296
+ receiver_exp in [:paren, receiver_exp]
297
297
  self_type = simulate_evaluate receiver_exp, scope
298
298
  else
299
299
  current_self_types = scope.self_type.types
@@ -407,6 +407,9 @@ class KatakataIrb::TypeSimulator
407
407
  KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values]
408
408
  in [:hash, nil]
409
409
  KatakataIrb::Types::InstanceType.new Hash
410
+ in [:paren, [Symbol,] | false => statement]
411
+ # workaround for `p ()` and `p (foo)`
412
+ simulate_evaluate statement, scope if statement
410
413
  in [:paren | :ensure | :else, statements]
411
414
  statements.map { simulate_evaluate _1, scope }.last
412
415
  in [:const_path_ref, receiver, [:@const, name,]]
@@ -574,6 +577,7 @@ class KatakataIrb::TypeSimulator
574
577
  simulate_evaluate target, scope
575
578
  simulate_evaluate value, scope
576
579
  in [:massign, targets, value]
580
+ targets in [:mlhs, *targets] # (a,b) = value
577
581
  rhs = simulate_evaluate value, scope
578
582
  evaluate_massign targets, rhs, scope
579
583
  rhs
@@ -85,23 +85,26 @@ module KatakataIrb::Types
85
85
 
86
86
  def self.type_from_object(object, max_level: 4)
87
87
  max_level -= 1
88
+ sample_size = 1000
88
89
  case object
89
90
  when Array
91
+ values = object.size > sample_size ? object.sample(sample_size) : object
90
92
  if max_level > 0
91
- values = object.map { type_from_object(_1, max_level:) }
92
- InstanceType.new Array, { Elem: UnionType[*values] }
93
+ InstanceType.new Array, { Elem: UnionType[*values.map { type_from_object(_1, max_level:) }] }
93
94
  else
94
- InstanceType.new Array, { Elem: UnionType[*object.map(&:class).uniq.map { InstanceType.new _1 }] }
95
+ InstanceType.new Array, { Elem: UnionType[*values.map(&:class).uniq.map { InstanceType.new _1 }] }
95
96
  end
96
97
  when Hash
98
+ keys = object.size > sample_size ? object.keys.sample(sample_size) : object.keys
99
+ values = object.size > sample_size ? object.values.sample(sample_size) : object.values
97
100
  if max_level > 0
98
- keys = object.keys.map { type_from_object(_1, max_level:) }
99
- values = object.values.map { type_from_object(_1, max_level:) }
100
- InstanceType.new Hash, { K: UnionType[*keys], V: UnionType[*values] }
101
+ key_types = keys.map { type_from_object(_1, max_level:) }
102
+ value_types = values.map { type_from_object(_1, max_level:) }
103
+ InstanceType.new Hash, { K: UnionType[*key_types], V: UnionType[*value_types] }
101
104
  else
102
- keys = object.keys.map(&:class).uniq.map { InstanceType.new _1 }
103
- values = object.values.map(&:class).uniq.map { InstanceType.new _1 }
104
- InstanceType.new Hash, { K: UnionType[*keys], V: UnionType[*values] }
105
+ key_types = keys.map(&:class).uniq.map { InstanceType.new _1 }
106
+ value_types = values.map(&:class).uniq.map { InstanceType.new _1 }
107
+ InstanceType.new Hash, { K: UnionType[*key_types], V: UnionType[*value_types] }
105
108
  end
106
109
  when Module
107
110
  SingletonType.new object
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KatakataIrb
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katakata_irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - tompng
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-20 00:00:00.000000000 Z
11
+ date: 2023-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -45,7 +45,6 @@ files:
45
45
  - lib/katakata_irb/completor.rb
46
46
  - lib/katakata_irb/reline_patch.rb
47
47
  - lib/katakata_irb/reline_patches/escapeseq.patch
48
- - lib/katakata_irb/reline_patches/fullwidth.patch
49
48
  - lib/katakata_irb/reline_patches/indent.patch
50
49
  - lib/katakata_irb/reline_patches/raw.patch
51
50
  - lib/katakata_irb/reline_patches/scrollbar.patch
@@ -77,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
76
  - !ruby/object:Gem::Version
78
77
  version: '0'
79
78
  requirements: []
80
- rubygems_version: 3.3.3
79
+ rubygems_version: 3.4.3
81
80
  signing_key:
82
81
  specification_version: 4
83
82
  summary: IRB with Typed Completion
@@ -1,200 +0,0 @@
1
- diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
2
- index c9e613e..6acf969 100644
3
- --- a/lib/reline/line_editor.rb
4
- +++ b/lib/reline/line_editor.rb
5
- @@ -748,3 +741,3 @@ class Reline::LineEditor
6
- end
7
- str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
8
- - str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
9
- + str, = Reline::Unicode.take_range(item, 0, str_width, padding: true)
10
- @@ -793,85 +786,42 @@ class Reline::LineEditor
11
- end
12
- visual_lines.concat(vl)
13
- }
14
- - old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
15
- - y = @first_line_started_from + @started_from
16
- - y_diff = y - old_y
17
- - if (old_y + old_dialog.vertical_offset) < (y + dialog.vertical_offset)
18
- - # rerender top
19
- - move_cursor_down(old_dialog.vertical_offset - y_diff)
20
- - start = visual_start + old_dialog.vertical_offset
21
- - line_num = dialog.vertical_offset - old_dialog.vertical_offset
22
- - line_num.times do |i|
23
- - Reline::IOGate.move_cursor_column(old_dialog.column)
24
- - if visual_lines[start + i].nil?
25
- - s = ' ' * old_dialog.width
26
- - else
27
- - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
28
- - s = padding_space_with_escape_sequences(s, old_dialog.width)
29
- - end
30
- - @output.write "\e[0m#{s}\e[0m"
31
- - move_cursor_down(1) if i < (line_num - 1)
32
- - end
33
- - move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
34
- - end
35
- - if (old_y + old_dialog.vertical_offset + old_dialog.contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
36
- - # rerender bottom
37
- - move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
38
- - start = visual_start + dialog.vertical_offset + dialog.contents.size
39
- - line_num = (old_dialog.vertical_offset + old_dialog.contents.size) - (dialog.vertical_offset + dialog.contents.size)
40
- - line_num.times do |i|
41
- - Reline::IOGate.move_cursor_column(old_dialog.column)
42
- - if visual_lines[start + i].nil?
43
- - s = ' ' * old_dialog.width
44
- - else
45
- - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
46
- - s = padding_space_with_escape_sequences(s, old_dialog.width)
47
- + old_dialog_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
48
- + dialog_y = @first_line_started_from + @started_from
49
- +
50
- + x_range = dialog.column...dialog.column + dialog.width
51
- + old_x_range = old_dialog.column...old_dialog.column + old_dialog.width
52
- + y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size
53
- + old_y_range = old_dialog_y + old_dialog.vertical_offset...old_dialog_y + old_dialog.vertical_offset + old_dialog.contents.size
54
- +
55
- + cursor_y = dialog_y
56
- + old_y_range.each do |y|
57
- + rerender_ranges = []
58
- + if y_range.cover?(y) && x_range.any?(old_x_range)
59
- + if old_x_range.begin < x_range.begin
60
- + # rerender left
61
- + rerender_ranges << [old_x_range.begin...[old_x_range.end, x_range.begin].min, true, false]
62
- end
63
- - @output.write "\e[0m#{s}\e[0m"
64
- - move_cursor_down(1) if i < (line_num - 1)
65
- - end
66
- - move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
67
- - end
68
- - if old_dialog.column < dialog.column
69
- - # rerender left
70
- - move_cursor_down(old_dialog.vertical_offset - y_diff)
71
- - width = dialog.column - old_dialog.column
72
- - start = visual_start + old_dialog.vertical_offset
73
- - line_num = old_dialog.contents.size
74
- - line_num.times do |i|
75
- - Reline::IOGate.move_cursor_column(old_dialog.column)
76
- - if visual_lines[start + i].nil?
77
- - s = ' ' * width
78
- - else
79
- - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
80
- - s = padding_space_with_escape_sequences(s, dialog.width)
81
- + if x_range.end < old_x_range.end
82
- + # rerender right
83
- + rerender_ranges << [[x_range.end, old_x_range.begin].max...old_x_range.end, false, true]
84
- end
85
- + else
86
- + rerender_ranges << [old_x_range, true, true]
87
- + end
88
- +
89
- + rerender_ranges.each do |range, cover_begin, cover_end|
90
- + move_cursor_down(y - cursor_y)
91
- + cursor_y = y
92
- + col = range.begin
93
- + width = range.end - range.begin
94
- + line = visual_lines[y + visual_start - old_dialog_y] || ''
95
- + s, col = Reline::Unicode.take_range(line, col, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
96
- + Reline::IOGate.move_cursor_column(col)
97
- @output.write "\e[0m#{s}\e[0m"
98
- - move_cursor_down(1) if i < (line_num - 1)
99
- - end
100
- - move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
101
- - end
102
- - if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width)
103
- - # rerender right
104
- - move_cursor_down(old_dialog.vertical_offset + y_diff)
105
- - width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width)
106
- - start = visual_start + old_dialog.vertical_offset
107
- - line_num = old_dialog.contents.size
108
- - line_num.times do |i|
109
- - Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width)
110
- - if visual_lines[start + i].nil?
111
- - s = ' ' * width
112
- - else
113
- - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
114
- - rerender_width = old_dialog.width - dialog.width
115
- - s = padding_space_with_escape_sequences(s, rerender_width)
116
- - end
117
- - Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
118
- - @output.write "\e[0m#{s}\e[0m"
119
- - move_cursor_down(1) if i < (line_num - 1)
120
- end
121
- - move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
122
- end
123
- + move_cursor_up(cursor_y - dialog_y)
124
- Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
125
- end
126
-
127
- @@ -912,9 +862,8 @@ class Reline::LineEditor
128
- dialog_vertical_size = dialog.contents.size
129
- dialog_vertical_size.times do |i|
130
- if i < visual_lines_under_dialog.size
131
- - Reline::IOGate.move_cursor_column(dialog.column)
132
- - str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
133
- - str = padding_space_with_escape_sequences(str, dialog.width)
134
- + str, start_pos = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width, cover_begin: true, cover_end: true, padding: true)
135
- + Reline::IOGate.move_cursor_column(start_pos)
136
- @output.write "\e[0m#{str}\e[0m"
137
- else
138
- Reline::IOGate.move_cursor_column(dialog.column)
139
- diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb
140
- index 6000c9f..03074dd 100644
141
- --- a/lib/reline/unicode.rb
142
- +++ b/lib/reline/unicode.rb
143
- @@ -194,17 +194,21 @@ class Reline::Unicode
144
- end
145
-
146
- # Take a chunk of a String cut by width with escape sequences.
147
- - def self.take_range(str, start_col, max_width, encoding = str.encoding)
148
- + def self.take_range(str, start_col, width, encoding: str.encoding, cover_begin: false, cover_end: false, padding: false)
149
- chunk = String.new(encoding: encoding)
150
- total_width = 0
151
- rest = str.encode(Encoding::UTF_8)
152
- in_zero_width = false
153
- + chunk_start_col = nil
154
- + chunk_end_col = nil
155
- rest.scan(WIDTH_SCANNER) do |gc|
156
- case
157
- when gc[NON_PRINTING_START_INDEX]
158
- in_zero_width = true
159
- + chunk << NON_PRINTING_START
160
- when gc[NON_PRINTING_END_INDEX]
161
- in_zero_width = false
162
- + chunk << NON_PRINTING_END
163
- when gc[CSI_REGEXP_INDEX]
164
- chunk << gc[CSI_REGEXP_INDEX]
165
- when gc[OSC_REGEXP_INDEX]
166
- @@ -215,13 +219,31 @@ class Reline::Unicode
167
- chunk << gc
168
- else
169
- mbchar_width = get_mbchar_width(gc)
170
- + prev_width = total_width
171
- total_width += mbchar_width
172
- - break if (start_col + max_width) < total_width
173
- - chunk << gc if start_col < total_width
174
- + break if !cover_end && total_width > start_col + width
175
- + if cover_begin ? start_col < total_width : start_col <= prev_width
176
- + chunk << gc
177
- + chunk_start_col ||= prev_width
178
- + chunk_end_col = total_width
179
- + end
180
- + break if total_width >= start_col + width
181
- end
182
- end
183
- end
184
- - chunk
185
- + chunk_start_col ||= start_col
186
- + chunk_end_col ||= start_col
187
- + if padding
188
- + if start_col < chunk_start_col
189
- + chunk = ' ' * (chunk_start_col - start_col) + chunk
190
- + chunk_start_col = start_col
191
- + end
192
- + if chunk_end_col < start_col + width
193
- + chunk << ' ' * (start_col + width - chunk_end_col)
194
- + chunk_end_col = start_col + width
195
- + end
196
- + end
197
- + [chunk, chunk_start_col, chunk_end_col - chunk_start_col]
198
- end
199
-
200
- def self.get_next_mbchar_size(line, byte_pointer)