irb 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 811c1b4343957dac57d58178adbc13195e6d53de980f63810ec83eec09d1f8c1
4
- data.tar.gz: bbc99d3f46a8e7afa6a7a8e32dc137300e3da15935631e05a56b203a901887fb
3
+ metadata.gz: 810aa12a1fbfdbd0ff9f9c4d5c4ac71e62e8241f87cb1a306be1ff81aaef08cb
4
+ data.tar.gz: 781d169232ac355b9723e8dc03d6c0de4c16327b4a4a3339cd1e90bad7e1c0b1
5
5
  SHA512:
6
- metadata.gz: 76b770cf0a8012031f11306cc96879173d819cbd0501efdd41c434571028c3dee8bc12b5928696232d61215f6e22ecec3b9a6e3ff93e085349282c035c9389ec
7
- data.tar.gz: f48e1f3912d5bb59acfa041718014dafb24d7f39ac23e1f0b267047ca4e4f937af77bdb0d40a940813da71f5920f53b5bbdc987b9dc50d16e5bdb85a754e02b7
6
+ metadata.gz: 51d97e80d977c6ce100f66cc0ad4becae17600d36653be88cc7c2f1e6cd0b7127cec01344bc69b04de1b043f8d45a1d07f6b4b8ad3fad4584585aca269cfa65f
7
+ data.tar.gz: a197cf8c07a1ea728a3c9d0d70fdd28a2a5b9a4e9ebf72a8fd8d6495627cf4c369e6831879a766c5dd8819438bb1f126b0d4d669becc4c63a7ad6c4ea51a3a79
data/Gemfile CHANGED
@@ -17,3 +17,10 @@ gem "rake"
17
17
  gem "test-unit"
18
18
  gem "test-unit-ruby-core"
19
19
  gem "debug", github: "ruby/debug"
20
+
21
+ gem "racc"
22
+
23
+ if RUBY_VERSION >= "3.0.0"
24
+ gem "rbs"
25
+ gem "prism", ">= 0.17.1"
26
+ end
data/README.md CHANGED
@@ -52,6 +52,10 @@ $ gem install irb
52
52
 
53
53
  ## Usage
54
54
 
55
+ > **Note**
56
+ >
57
+ > We're working hard to match Pry's variety of powerful features in IRB, and you can track our progress or find contribution ideas in [this document](https://github.com/ruby/irb/blob/master/COMPARED_WITH_PRY.md).
58
+
55
59
  ### The `irb` Executable
56
60
 
57
61
  You can start a fresh IRB session by typing `irb` in your terminal.
@@ -231,6 +235,79 @@ However, there are also some limitations to be aware of:
231
235
  2. As IRB [doesn't currently support remote-connection](https://github.com/ruby/irb/issues/672), it can't be used with `debug.gem`'s remote debugging feature.
232
236
  3. Access to the previous return value via the underscore `_` is not supported.
233
237
 
238
+ ## Type Based Completion
239
+
240
+ IRB's default completion `IRB::RegexpCompletor` uses Regexp. IRB has another experimental completion `IRB::TypeCompletion` that uses type analysis.
241
+
242
+ ### How to Enable IRB::TypeCompletion
243
+
244
+ To enable IRB::TypeCompletion, run IRB with `--type-completor` option
245
+ ```
246
+ $ irb --type-completor
247
+ ```
248
+ Or write the code below to IRB's rc-file.
249
+ ```ruby
250
+ IRB.conf[:COMPLETOR] = :type # default is :regexp
251
+ ```
252
+ You also need `gem prism` and `gem rbs` to use this feature.
253
+
254
+ To check if it's enabled, type `irb_info` into IRB and see the `Completion` section.
255
+ ```
256
+ irb(main):001> irb_info
257
+ ...
258
+ # Enabled
259
+ Completion: Autocomplete, TypeCompletion::Completor(Prism: 0.17.1, RBS: 3.3.0)
260
+ # Not enabled
261
+ Completion: Autocomplete, RegexpCompletor
262
+ ...
263
+ ```
264
+ If you have `sig/` directory or `rbs_collection.lock.yaml` in current directory, IRB will load it.
265
+
266
+ ### Advantage over Default IRB::RegexpCompletor
267
+
268
+ IRB::TypeCompletion can autocomplete chained methods, block parameters and more if type information is available.
269
+ These are some examples IRB::RegexpCompletor cannot complete.
270
+
271
+ ```ruby
272
+ irb(main):001> 'Ruby'.upcase.chars.s # Array methods (sample, select, shift, size)
273
+ ```
274
+
275
+ ```ruby
276
+ irb(main):001> 10.times.map(&:to_s).each do |s|
277
+ irb(main):002> s.up # String methods (upcase, upcase!, upto)
278
+ ```
279
+
280
+ ```ruby
281
+ irb(main):001> class User < ApplicationRecord
282
+ irb(main):002> def foo
283
+ irb(main):003> sa # save, save!
284
+ ```
285
+
286
+ As a trade-off, completion calculation takes more time than IRB::RegexpCompletor.
287
+
288
+ ### Difference between Steep's Completion
289
+
290
+ Compared with Steep, IRB::TypeCompletion has some difference and limitations.
291
+ ```ruby
292
+ [0, 'a'].sample.
293
+ # Steep completes intersection of Integer methods and String methods
294
+ # IRB::TypeCompletion completes both Integer and String methods
295
+ ```
296
+
297
+ Some features like type narrowing is not implemented.
298
+ ```ruby
299
+ def f(arg = [0, 'a'].sample)
300
+ if arg.is_a?(String)
301
+ arg. # Completes both Integer and String methods
302
+ ```
303
+
304
+ Unlike other static type checker, IRB::TypeCompletion uses runtime information to provide better completion.
305
+ ```ruby
306
+ irb(main):001> a = [1]
307
+ => [1]
308
+ irb(main):002> a.first. # Completes Integer methods
309
+ ```
310
+
234
311
  ## Configuration
235
312
 
236
313
  ### Environment Variables
@@ -269,6 +346,30 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
269
346
 
270
347
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/irb.
271
348
 
349
+ ### Set up the environment
350
+
351
+ 1. Fork the project to your GithHub account
352
+ 2. Clone the fork with `git clone git@github.com:[your_username]/irb.git`
353
+ 3. Run `bundle install`
354
+ 4. Run `bundle exec rake` to make sure tests pass locally
355
+
356
+ ### Run integration tests
357
+
358
+ If your changes affect component rendering, such as the autocompletion's dialog/dropdown, you may need to run IRB's integration tests, known as `yamatanarroroti`.
359
+
360
+ Before running these tests, ensure that you have `libvterm` installed. If you're using Homebrew, you can install it by running:
361
+
362
+ ```bash
363
+ brew install libvterm
364
+ ```
365
+
366
+ After installing `libvterm`, you can run the integration tests using the following commands:
367
+
368
+ ```bash
369
+ WITH_VTERM=1 bundle install
370
+ WITH_VTERM=1 bundle exec rake test test_yamatanooroti
371
+ ```
372
+
272
373
  ## Releasing
273
374
 
274
375
  ```
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test" << "test/lib"
6
6
  t.libs << "lib"
7
7
  t.ruby_opts << "-rhelper"
8
- t.test_files = FileList["test/irb/test_*.rb"]
8
+ t.test_files = FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"]
9
9
  end
10
10
 
11
11
  # To make sure they have been correctly setup for Ruby CI.
@@ -13,7 +13,7 @@ desc "Run each irb test file in isolation."
13
13
  task :test_in_isolation do
14
14
  failed = false
15
15
 
16
- FileList["test/irb/test_*.rb"].each do |test_file|
16
+ FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"].each do |test_file|
17
17
  ENV["TEST"] = test_file
18
18
  begin
19
19
  Rake::Task["test"].execute
@@ -14,6 +14,7 @@ module IRB
14
14
  str = "Ruby version: #{RUBY_VERSION}\n"
15
15
  str += "IRB version: #{IRB.version}\n"
16
16
  str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
17
+ str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
17
18
  str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
18
19
  str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19
20
  str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
@@ -9,6 +9,30 @@ require_relative 'ruby-lex'
9
9
 
10
10
  module IRB
11
11
  class BaseCompletor # :nodoc:
12
+
13
+ # Set of reserved words used by Ruby, you should not use these for
14
+ # constants or variables
15
+ ReservedWords = %w[
16
+ __ENCODING__ __LINE__ __FILE__
17
+ BEGIN END
18
+ alias and
19
+ begin break
20
+ case class
21
+ def defined? do
22
+ else elsif end ensure
23
+ false for
24
+ if in
25
+ module
26
+ next nil not
27
+ or
28
+ redo rescue retry return
29
+ self super
30
+ then true
31
+ undef unless until
32
+ when while
33
+ yield
34
+ ]
35
+
12
36
  def completion_candidates(preposing, target, postposing, bind:)
13
37
  raise NotImplementedError
14
38
  end
@@ -94,28 +118,9 @@ module IRB
94
118
  end
95
119
  }
96
120
 
97
- # Set of reserved words used by Ruby, you should not use these for
98
- # constants or variables
99
- ReservedWords = %w[
100
- __ENCODING__ __LINE__ __FILE__
101
- BEGIN END
102
- alias and
103
- begin break
104
- case class
105
- def defined? do
106
- else elsif end ensure
107
- false for
108
- if in
109
- module
110
- next nil not
111
- or
112
- redo rescue retry return
113
- self super
114
- then true
115
- undef unless until
116
- when while
117
- yield
118
- ]
121
+ def inspect
122
+ 'RegexpCompletor'
123
+ end
119
124
 
120
125
  def complete_require_path(target, preposing, postposing)
121
126
  if target =~ /\A(['"])([^'"]+)\Z/
@@ -415,4 +420,20 @@ module IRB
415
420
  end
416
421
  end
417
422
  end
423
+
424
+ module InputCompletor
425
+ class << self
426
+ private def regexp_completor
427
+ @regexp_completor ||= RegexpCompletor.new
428
+ end
429
+
430
+ def retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
431
+ regexp_completor.retrieve_completion_data(input, bind: bind, doc_namespace: doc_namespace)
432
+ end
433
+ end
434
+ CompletionProc = ->(target, preposing = nil, postposing = nil) {
435
+ regexp_completor.completion_candidates(preposing, target, postposing, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding)
436
+ }
437
+ end
438
+ deprecate_constant :InputCompletor
418
439
  end
data/lib/irb/context.rb CHANGED
@@ -86,14 +86,14 @@ module IRB
86
86
  when nil
87
87
  if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
88
88
  # Both of multiline mode and singleline mode aren't specified.
89
- @io = RelineInputMethod.new
89
+ @io = RelineInputMethod.new(build_completor)
90
90
  else
91
91
  @io = nil
92
92
  end
93
93
  when false
94
94
  @io = nil
95
95
  when true
96
- @io = RelineInputMethod.new
96
+ @io = RelineInputMethod.new(build_completor)
97
97
  end
98
98
  unless @io
99
99
  case use_singleline?
@@ -149,6 +149,43 @@ module IRB
149
149
  @command_aliases = IRB.conf[:COMMAND_ALIASES]
150
150
  end
151
151
 
152
+ private def build_completor
153
+ completor_type = IRB.conf[:COMPLETOR]
154
+ case completor_type
155
+ when :regexp
156
+ return RegexpCompletor.new
157
+ when :type
158
+ completor = build_type_completor
159
+ return completor if completor
160
+ else
161
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
162
+ end
163
+ # Fallback to RegexpCompletor
164
+ RegexpCompletor.new
165
+ end
166
+
167
+ TYPE_COMPLETION_REQUIRED_PRISM_VERSION = '0.17.1'
168
+
169
+ private def build_type_completor
170
+ unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') && RUBY_ENGINE != 'truffleruby'
171
+ warn 'TypeCompletion requires RUBY_VERSION >= 3.0.0'
172
+ return
173
+ end
174
+ begin
175
+ require 'prism'
176
+ rescue LoadError => e
177
+ warn "TypeCompletion requires Prism: #{e.message}"
178
+ return
179
+ end
180
+ unless Gem::Version.new(Prism::VERSION) >= Gem::Version.new(TYPE_COMPLETION_REQUIRED_PRISM_VERSION)
181
+ warn "TypeCompletion requires Prism::VERSION >= #{TYPE_COMPLETION_REQUIRED_PRISM_VERSION}"
182
+ return
183
+ end
184
+ require 'irb/type_completion/completor'
185
+ TypeCompletion::Types.preload_in_thread
186
+ TypeCompletion::Completor.new
187
+ end
188
+
152
189
  def save_history=(val)
153
190
  IRB.conf[:SAVE_HISTORY] = val
154
191
  end
@@ -98,18 +98,26 @@ module IRB
98
98
  end
99
99
  end
100
100
 
101
+ private def easter_egg_logo(type)
102
+ @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103
+ .split(/TYPE: ([A-Z]+)\n/)[1..]
104
+ .each_slice(2)
105
+ .to_h
106
+ @easter_egg_logos[type.to_s.upcase]
107
+ end
108
+
101
109
  private def easter_egg(type = nil)
102
110
  type ||= [:logo, :dancing].sample
103
111
  case type
104
112
  when :logo
105
- File.open(File.join(__dir__, 'ruby_logo.aa')) do |f|
106
- require "rdoc"
107
- RDoc::RI::Driver.new.page do |io|
108
- IO.copy_stream(f, io)
109
- end
113
+ require "rdoc"
114
+ RDoc::RI::Driver.new.page do |io|
115
+ io.write easter_egg_logo(:large)
110
116
  end
111
117
  when :dancing
112
- begin
118
+ STDOUT.cooked do
119
+ interrupted = false
120
+ prev_trap = trap("SIGINT") { interrupted = true }
113
121
  canvas = Canvas.new(Reline.get_screen_size)
114
122
  Reline::IOGate.set_winch_handler do
115
123
  canvas = Canvas.new(Reline.get_screen_size)
@@ -125,10 +133,12 @@ module IRB
125
133
  buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
126
134
  print "\e[H" + buff
127
135
  sleep 0.05
136
+ break if interrupted
128
137
  end
129
138
  rescue Interrupt
130
139
  ensure
131
140
  print "\e[0m\e[?1049l"
141
+ trap("SIGINT", prev_trap)
132
142
  end
133
143
  end
134
144
  end
data/lib/irb/init.rb CHANGED
@@ -5,6 +5,41 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
+ @CONF = {}
9
+ # Displays current configuration.
10
+ #
11
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
12
+ #
13
+ # See IRB@Configuration for more information.
14
+ def IRB.conf
15
+ @CONF
16
+ end
17
+
18
+ def @CONF.inspect
19
+ array = []
20
+ for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
21
+ case k
22
+ when :MAIN_CONTEXT, :__TMP__EHV__
23
+ array.push format("CONF[:%s]=...myself...", k.id2name)
24
+ when :PROMPT
25
+ s = v.collect{
26
+ |kk, vv|
27
+ ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
28
+ format(":%s=>{%s}", kk.id2name, ss.join(", "))
29
+ }
30
+ array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
31
+ else
32
+ array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
33
+ end
34
+ end
35
+ array.join("\n")
36
+ end
37
+
38
+ # Returns the current version of IRB, including release version and last
39
+ # updated date.
40
+ def IRB.version
41
+ format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
42
+ end
8
43
 
9
44
  # initialize config
10
45
  def IRB.setup(ap_path, argv: ::ARGV)
@@ -28,6 +63,7 @@ module IRB # :nodoc:
28
63
  unless ap_path and @CONF[:AP_NAME]
29
64
  ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
30
65
  end
66
+ @CONF[:VERSION] = version
31
67
  @CONF[:AP_NAME] = File::basename(ap_path, ".rb")
32
68
 
33
69
  @CONF[:IRB_NAME] = "irb"
@@ -40,6 +76,7 @@ module IRB # :nodoc:
40
76
  @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
41
77
  @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
42
78
  @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
79
+ @CONF[:COMPLETOR] = :regexp
43
80
  @CONF[:INSPECT_MODE] = true
44
81
  @CONF[:USE_TRACER] = false
45
82
  @CONF[:USE_LOADER] = false
@@ -293,6 +330,10 @@ module IRB # :nodoc:
293
330
  @CONF[:USE_AUTOCOMPLETE] = true
294
331
  when "--noautocomplete"
295
332
  @CONF[:USE_AUTOCOMPLETE] = false
333
+ when "--regexp-completor"
334
+ @CONF[:COMPLETOR] = :regexp
335
+ when "--type-completor"
336
+ @CONF[:COMPLETOR] = :type
296
337
  when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
297
338
  opt = $1 || argv.shift
298
339
  prompt_mode = opt.upcase.tr("-", "_").intern
@@ -193,6 +193,10 @@ module IRB
193
193
  }
194
194
  end
195
195
 
196
+ def completion_info
197
+ 'RegexpCompletor'
198
+ end
199
+
196
200
  # Reads the next line from this input method.
197
201
  #
198
202
  # See IO#gets for more information.
@@ -230,13 +234,13 @@ module IRB
230
234
  HISTORY = Reline::HISTORY
231
235
  include HistorySavingAbility
232
236
  # Creates a new input method object using Reline
233
- def initialize
237
+ def initialize(completor)
234
238
  IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)
235
239
 
236
- super
240
+ super()
237
241
 
238
242
  @eof = false
239
- @completor = RegexpCompletor.new
243
+ @completor = completor
240
244
 
241
245
  Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
242
246
  Reline.completion_append_character = nil
@@ -270,6 +274,11 @@ module IRB
270
274
  end
271
275
  end
272
276
 
277
+ def completion_info
278
+ autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete'
279
+ "#{autocomplete_message}, #{@completor.inspect}"
280
+ end
281
+
273
282
  def check_termination(&block)
274
283
  @check_termination_proc = block
275
284
  end
@@ -303,15 +312,20 @@ module IRB
303
312
  return nil if result.nil? or pointer.nil? or pointer < 0
304
313
 
305
314
  name = doc_namespace.call(result[pointer])
315
+ show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
306
316
 
307
317
  options = {}
308
318
  options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
309
319
  driver = RDoc::RI::Driver.new(options)
310
320
 
311
321
  if key.match?(dialog.name)
312
- begin
313
- driver.display_names([name])
314
- rescue RDoc::RI::Driver::NotFoundError
322
+ if show_easter_egg
323
+ IRB.__send__(:easter_egg)
324
+ else
325
+ begin
326
+ driver.display_names([name])
327
+ rescue RDoc::RI::Driver::NotFoundError
328
+ end
315
329
  end
316
330
  end
317
331
 
@@ -374,8 +388,15 @@ module IRB
374
388
  formatter.width = width
375
389
  dialog.trap_key = alt_d
376
390
  mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
377
- message = "Press #{mod_key}+d to read the full document"
378
- contents = [message] + doc.accept(formatter).split("\n")
391
+ if show_easter_egg
392
+ type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
393
+ contents = IRB.send(:easter_egg_logo, type).split("\n")
394
+ message = "Press #{mod_key}+d to see more"
395
+ contents[0][0, message.size] = message
396
+ else
397
+ message = "Press #{mod_key}+d to read the full document"
398
+ contents = [message] + doc.accept(formatter).split("\n")
399
+ end
379
400
  contents = contents.take(preferred_dialog_height)
380
401
 
381
402
  y = cursor_pos_to_render.y
@@ -30,6 +30,9 @@ Usage: irb.rb [options] [programfile] [arguments]
30
30
  --nocolorize Don't use color-highlighting.
31
31
  --autocomplete Use auto-completion (default).
32
32
  --noautocomplete Don't use auto-completion.
33
+ --regexp-completor
34
+ Use regexp based completion (default).
35
+ --type-completor Use type based completion.
33
36
  --prompt prompt-mode, --prompt-mode prompt-mode
34
37
  Set prompt mode. Pre-defined prompt modes are:
35
38
  'default', 'classic', 'simple', 'inf-ruby', 'xmp', 'null'.
@@ -21,6 +21,9 @@ Usage: irb.rb [options] [programfile] [arguments]
21
21
  --nocolorize 色付けを利用しない.
22
22
  --autocomplete オートコンプリートを利用する.
23
23
  --noautocomplete オートコンプリートを利用しない.
24
+ --regexp-completor
25
+ 補完に正規表現を利用する.
26
+ --type-completor 補完に型情報を利用する.
24
27
  --prompt prompt-mode/--prompt-mode prompt-mode
25
28
  プロンプトモードを切替えます. 現在定義されているプ
26
29
  ロンプトモードは, default, simple, xmp, inf-rubyが
data/lib/irb/ruby_logo.aa CHANGED
@@ -1,3 +1,4 @@
1
+ TYPE: LARGE
1
2
 
2
3
  -+smJYYN?mm-
3
4
  HB"BBYT TQg NggT
@@ -35,3 +36,45 @@
35
36
  m7 NW H N HSVO1z=?11-
36
37
  NgTH bB kH WBHWWHBHWmQgg&gggggNNN
37
38
  NNggggggNN
39
+ TYPE: ASCII
40
+ ,,,;;;;''''';;;'';,
41
+ ,,;'' ';;,;;; ',
42
+ ,,'' ;;'';'''';;;;;;
43
+ ,;' ;; ',, ;
44
+ ,;' ,;' ';, ;
45
+ ;' ,;; ',,,;
46
+ ,' ,;;,,,,,,,,,,,;;;;
47
+ ;' ;;';;;; ,;;
48
+ ;' ,;' ;; '',, ,;;;
49
+ ;; ,;' ; '';, ,; ;'
50
+ ;; ,;;' ;; ;; ;;
51
+ ;;, ,,;;' ; ;'; ;;
52
+ ;';;,,,,;;;;;;;,,, ;; ,' ; ;;
53
+ ; ;;''' ,;'; ''';,,, ; ,;' ;;;;
54
+ ;;;;, ; '; ''';;;' ';;;
55
+ ;'; ;, ;' '; ,;' ', ;;;
56
+ ;;; ; ,; '; ,,' ',, ;;
57
+ ;;; '; ;' ';,,'' ';,;;
58
+ '; ';,; ,,;''''''''';;;;;;,,;;;
59
+ ';,,;;,,;;;;;;;;;;''''''''''''''
60
+ TYPE: UNICODE
61
+ ⣀⣤⣴⣾⣿⣿⣿⡛⠛⠛⠛⠛⣻⣿⠿⠛⠛⠶⣤⡀
62
+ ⣀⣴⠾⠛⠉⠁ ⠙⣿⣶⣤⣶⣟⣉ ⠈⠻⣦
63
+ ⣀⣴⠟⠋ ⢸⣿⠟⠻⣯⡙⠛⠛⠛⠶⠶⠶⢶⣽⣇
64
+ ⣠⡾⠋⠁ ⣾⡿ ⠈⠛⢦⣄ ⣿
65
+ ⣠⡾⠋ ⣰⣿⠃ ⠙⠷⣤⡀ ⣿
66
+ ⢀⡾⠋ ⣰⣿⡏ ⠈⠻⣦⣄⢠⣿
67
+ ⣰⠟⠁ ⣴⣿⣿⣁⣀⣠⣤⣤⣤⣤⣤⣤⣤⣴⠶⠿⣿⡏
68
+ ⣼⠏ ⢀⣾⣿⠟⣿⠿⣯⣍⠁ ⣰⣿⡇
69
+ ⢀⣼⠋ ⢀⣴⣿⠟⠁ ⢸⡇ ⠙⠻⢦⣄⡀ ⢠⡿⣿⡇
70
+ ⢀⣾⡏ ⢀⣴⣿⠟⠁ ⣿ ⠉⠻⢶⣄⡀⣰⡟ ⣿⠃
71
+ ⣾⣿⠁ ⣠⣶⡿⠋⠁ ⢹⡇ ⠈⣿⡏ ⢸⣿
72
+ ⣿⣿⡆ ⢀⣠⣴⣿⡿⠋ ⠈⣿ ⢀⡾⠋⣿ ⢸⣿
73
+ ⣿⠸⣿⣶⣤⣤⣤⣤⣶⣾⠿⠿⣿⣿⠶⣤⣤⣀⡀ ⢹⡇ ⣴⠟⠁ ⣿⡀⢸⣿
74
+ ⣿⢀⣿⣟⠛⠋⠉⠁ ⢰⡟⠹⣧ ⠈⠉⠛⠻⠶⢦⣤⣀⡀ ⠈⣿ ⣠⡾⠃ ⢸⡇⢸⡇
75
+ ⣿⣾⣿⢿⡄ ⣿⠁ ⠘⣧ ⠉⠙⠛⠷⣿⣿⡋ ⠸⣇⣸⡇
76
+ ⣿⠃⣿⠈⢿⡄ ⣸⠇ ⠘⣧ ⢀⣤⠾⠋⠈⠻⣦⡀ ⣿⣿⡇
77
+ ⣿⢸⡏ ⠈⣷⡀ ⢠⡿ ⠘⣧⡀ ⣠⡴⠟⠁ ⠈⠻⣦⣀ ⢿⣿⠁
78
+ ⢻⣾⡇ ⠘⣷ ⣼⠃ ⠘⣷⣠⣴⠟⠋ ⠙⢷⣄⢸⣿
79
+ ⠻⣧⡀ ⠘⣧⣰⡏ ⢀⣠⣤⠶⠛⠉⠛⠛⠛⠛⠛⠛⠻⢶⣶⣶⣶⣶⣶⣤⣤⣽⣿⣿
80
+ ⠈⠛⠷⢦⣤⣽⣿⣥⣤⣶⣶⡿⠿⠿⠶⠶⠶⠶⠾⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠁