Skip to content

Commit 4032543

Browse files
jneenjneenstraight-shoota
authored
Improve cursor window format in StringScanner#inspect (#16594)
Discussion: #16455 (comment) --------- Co-authored-by: jneen <jneen@jneen.net> Co-authored-by: Johannes Müller <straightshoota@gmail.com>
1 parent 8aa91ff commit 4032543

File tree

2 files changed

+61
-11
lines changed

2 files changed

+61
-11
lines changed

spec/std/string_scanner_spec.cr

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -391,20 +391,52 @@ describe StringScanner do
391391
describe "#inspect" do
392392
it "has information on the scanner" do
393393
s = StringScanner.new("this is a string")
394-
s.inspect.should eq(%(#<StringScanner 0/16 "this " >))
394+
s.inspect.should eq(%(#<StringScanner 0/16 "this >))
395395
s.scan(/\w+\s/)
396-
s.inspect.should eq(%(#<StringScanner 5/16 "s is " >))
396+
s.inspect.should eq(%(#<StringScanner 5/16 …s ‣is …>))
397+
s.scan(1)
398+
s.inspect.should eq(%(#<StringScanner 6/16 … i‣s a…>))
397399
s.scan(/\w+\s/)
398-
s.inspect.should eq(%(#<StringScanner 8/16 "s a s" >))
400+
s.inspect.should eq(%(#<StringScanner 8/16 …s ‣a s>))
399401
s.scan(/\w+\s\w+/)
400-
s.inspect.should eq(%(#<StringScanner 16/16 "tring" >))
402+
s.inspect.should eq(%(#<StringScanner 16/16 tring‣">))
401403
end
402404

403405
it "works with small strings" do
404406
s = StringScanner.new("hi")
405-
s.inspect.should eq(%(#<StringScanner 0/2 "hi" >))
406-
s.scan(/\w\w/)
407-
s.inspect.should eq(%(#<StringScanner 2/2 "hi" >))
407+
s.inspect.should eq(%(#<StringScanner 0/2 "‣hi">))
408+
s.scan(/\w/)
409+
s.inspect.should eq(%(#<StringScanner 1/2 "h‣i">))
410+
s.scan(/\w/)
411+
s.inspect.should eq(%(#<StringScanner 2/2 "hi‣">))
412+
413+
s = StringScanner.new(" a\n\t ")
414+
s.skip(1)
415+
s.inspect.should eq(%(#<StringScanner 1/5 " ‣a\\n\\t ">))
416+
417+
s = StringScanner.new("\0\e")
418+
s.skip(1)
419+
s.inspect.should eq(%(#<StringScanner 1/2 "\\u0000‣\\e">))
420+
end
421+
422+
it "works with multi-byte strings" do
423+
s = StringScanner.new("これは文字列である")
424+
s.inspect.should eq(%(#<StringScanner 0/9 "‣これは文字…>))
425+
s.scan(1)
426+
s.inspect.should eq(%(#<StringScanner 1/9 "こ‣れは文字…>))
427+
s.scan(1)
428+
s.inspect.should eq(%(#<StringScanner 2/9 "これ‣は文字…>))
429+
s.scan(1)
430+
s.inspect.should eq(%(#<StringScanner 3/9 …れは‣文字列…>))
431+
s.scan(3)
432+
s.inspect.should eq(%(#<StringScanner 6/9 …字列‣である">))
433+
s.terminate
434+
s.inspect.should eq(%(#<StringScanner 9/9 …字列である‣">))
435+
end
436+
437+
it "works with empty string" do
438+
s = StringScanner.new("")
439+
s.inspect.should eq(%(#<StringScanner 0/0 "‣">))
408440
end
409441
end
410442

src/string_scanner.cr

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -531,16 +531,34 @@ class StringScanner
531531
@str.byte_slice(@byte_offset, @str.bytesize - @byte_offset)
532532
end
533533

534+
private INSPECT_INDICATOR_CHAR = '‣'
535+
private INSPECT_ELLIPSIS_CHAR = '…'
536+
private INSPECT_ENDING_CHAR = '"'
537+
534538
# Writes a representation of the scanner.
535539
#
536540
# Includes the current position of the offset, the total size of the string,
537541
# and five characters near the current position.
538542
def inspect(io : IO) : Nil
543+
offset = self.offset
544+
size = @str.size
545+
remaining = size - offset
546+
539547
io << "#<StringScanner "
540-
offset = offset()
541-
io << offset << '/' << @str.size
542-
start = Math.min(Math.max(offset - 2, 0), Math.max(0, @str.size - 5))
543-
io << " \"" << @str[start, 5] << "\" >"
548+
io << offset << '/' << size << ' '
549+
550+
# find a range of 5 characters with the scan head as close to
551+
# the middle as possible.
552+
chars_after = Math.max(3, 5 - offset).clamp(0, remaining)
553+
chars_before = (5 - chars_after).clamp(0, offset)
554+
555+
io << (chars_before < offset ? INSPECT_ELLIPSIS_CHAR : INSPECT_ENDING_CHAR)
556+
peek_behind(chars_before).inspect_unquoted(io) if chars_before > 0
557+
io << INSPECT_INDICATOR_CHAR
558+
peek(chars_after).inspect_unquoted(io) if chars_after > 0
559+
io << (chars_after < remaining ? INSPECT_ELLIPSIS_CHAR : INSPECT_ENDING_CHAR)
560+
561+
io << '>'
544562
end
545563

546564
private def make_char_reader : Char::Reader

0 commit comments

Comments
 (0)