Skip to content

Commit fc77d61

Browse files
committed
Merge pull request #68 from valich/master
Expect escaped backslashes in expressions to inspect
2 parents 83a7a2f + 314460a commit fc77d61

File tree

8 files changed

+149
-15
lines changed

8 files changed

+149
-15
lines changed

lib/ruby-debug-ide/command.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ def method_missing(meth, *args, &block)
6464
def options
6565
@options ||= {}
6666
end
67+
68+
def unescape_incoming(str)
69+
str.gsub(/((?:^|[^\\])(?:\\\\)*)\\n/, "\\1\n").
70+
gsub(/\\\\/, '\\')
71+
end
6772
end
6873

6974
def initialize(state, printer)
@@ -104,12 +109,12 @@ def timeout(sec)
104109
y.kill if y and y.alive?
105110
end
106111
end
107-
112+
108113
def debug_eval(str, b = get_binding)
109114
begin
110115
str = str.to_s
116+
to_inspect = Command.unescape_incoming(str)
111117
max_time = Debugger.evaluation_timeout
112-
to_inspect = str.gsub(/\\n/, "\n")
113118
@printer.print_debug("Evaluating #{str} with timeout after %i sec", max_time)
114119
timeout(max_time) do
115120
eval(to_inspect, b)

lib/ruby-debug-ide/commands/expression_info.rb

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,11 @@ def regexp
99
end
1010

1111
def execute
12-
string_to_parse = @match.post_match.gsub("\\n", "\n") + "\n\n\n"
12+
string_to_parse = Command.unescape_incoming(@match.post_match) + "\n\n\n"
1313
total_lines = string_to_parse.count("\n") + 1
1414

1515
lexer = RubyLex.new
16-
io = StringIO.new(string_to_parse)
17-
# for passing to the lexer
18-
io.instance_exec(string_to_parse.encoding) do |string_encoding|
19-
@my_encoding = string_encoding
20-
def self.encoding
21-
@my_encoding
22-
end
23-
end
24-
25-
lexer.set_input(io)
16+
lexer.set_input(create_io_reader(string_to_parse))
2617

2718
last_statement = ''
2819
last_prompt = ''
@@ -39,13 +30,28 @@ def self.encoding
3930
end
4031

4132
incomplete = true
42-
if /\A\s*\Z/m =~ last_statement[0]
33+
if /\A\s*\Z/m =~ last_statement
4334
incomplete = false
4435
end
4536

4637
@printer.print_expression_info(incomplete, last_prompt, last_indent)
4738
end
4839

40+
def create_io_reader(string_to_parse)
41+
io = StringIO.new(string_to_parse)
42+
43+
if string_to_parse.respond_to?(:encoding)
44+
io.instance_exec(string_to_parse.encoding) do |string_encoding|
45+
@my_encoding = string_encoding
46+
def self.encoding
47+
@my_encoding
48+
end
49+
end
50+
end
51+
52+
io
53+
end
54+
4955
class << self
5056
def help_command
5157
"expression_info"

lib/ruby-debug-ide/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Debugger
2-
IDE_VERSION='0.4.27'
2+
IDE_VERSION='0.4.28'
33
end

test-base/expression_info_test.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env ruby
2+
3+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
4+
5+
require 'test_base'
6+
7+
module ExpressionInfoTest
8+
9+
def test_info_multiline_expression
10+
create_socket ["sleep 0.1"]
11+
run_to_line(1)
12+
send_ruby('expression_info 1+')
13+
expression_info = read_expression_info
14+
assert_equal("true", expression_info.incomplete)
15+
16+
send_ruby('expression_info 1+\\n1')
17+
expression_info = read_expression_info
18+
assert_equal("false", expression_info.incomplete)
19+
20+
send_ruby('expression_info "')
21+
expression_info = read_expression_info
22+
assert_equal("true", expression_info.incomplete)
23+
24+
send_ruby('expression_info "\\\\"')
25+
expression_info = read_expression_info
26+
assert_equal("true", expression_info.incomplete)
27+
28+
send_ruby('expression_info "\\\\\\n"')
29+
expression_info = read_expression_info
30+
assert_equal("false", expression_info.incomplete)
31+
32+
send_ruby('expression_info def my_meth')
33+
expression_info = read_expression_info
34+
assert_equal("true", expression_info.incomplete)
35+
send_cont
36+
end
37+
38+
end
39+

test-base/inspect_test.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,32 @@ def test_inspect_multiline_expression
6868
send_cont
6969
end
7070

71+
def test_inspect_unescaping_escaped_newlines
72+
create_socket ["sleep 0.1"]
73+
run_to_line(1)
74+
75+
send_ruby('v inspect "\\\\n".size')
76+
variables = read_variables
77+
assert_equal(1, variables.length, "There is one variable returned.")
78+
assert_equal("1", variables[0].value, "There is one char (newline)")
79+
80+
send_ruby('v inspect "\\\\\\n".size')
81+
variables = read_variables
82+
assert_equal(1, variables.length, "There is one variable returned.")
83+
assert_equal("0", variables[0].value, "There are no chars, it's line continuation here")
84+
85+
send_ruby('v inspect "\\\\\\\\\\n".size')
86+
variables = read_variables
87+
assert_equal(1, variables.length, "There is one variable returned.")
88+
assert_equal("2", variables[0].value, "There are two chars: escaped backslash and newline")
89+
90+
send_ruby('v inspect \'"\\\\n\'.size')
91+
variables = read_variables
92+
assert_equal(1, variables.length, "There is one variable returned.")
93+
assert_equal("3", variables[0].value, "There are three chars: quote, backslash and n")
94+
95+
send_cont
96+
end
97+
7198
end
7299

test-base/readers.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Readers
1919
RubyThread = Struct.new("RubyThread", :id, :status)
2020
Frame = Struct.new("Frame", :no, :file, :line)
2121
Variable = Struct.new("Variable", :name, :kind, :value, :type, :hasChildren, :objectId)
22+
ExpressionInfo = Struct.new("ExpressionInfo", :incomplete, :prompt, :indent)
2223

2324
def read_breakpoint_added
2425
(@breakpoint_added_reader ||= BreakPointAddedReader.new(parser)).read
@@ -88,6 +89,10 @@ def read_processing_exception
8889
(@processing_exception_reader ||= ProcessingExceptionReader.new(parser)).read
8990
end
9091

92+
def read_expression_info
93+
(@expression_info_reader ||= ExpressionInfoReader.new(parser)).read
94+
end
95+
9196
def parser
9297
fail '"parser" method must be defined'
9398
end
@@ -315,4 +320,11 @@ def read
315320
end
316321
end
317322

323+
class ExpressionInfoReader < BaseReader
324+
def read
325+
data = read_element_data("expressionInfo")
326+
ExpressionInfo.new(data['incomplete'], data['prompt'], data['indent'])
327+
end
328+
end
329+
318330
end

test/rd_expression_info_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'rd_test_base'
4+
require 'expression_info_test'
5+
6+
class RDExpressionInfoTest < RDTestBase
7+
8+
include ExpressionInfoTest
9+
10+
end
11+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'test/unit'
2+
require 'ruby-debug-ide/command'
3+
4+
class UnescaperTest < Test::Unit::TestCase
5+
6+
def test_empty
7+
do_test('', '')
8+
do_test('a', 'a')
9+
end
10+
11+
def test_newline
12+
do_test('\n', "\n")
13+
do_test('a\n', "a\n")
14+
end
15+
16+
def test_escaped_newline
17+
do_test('\\\\n', '\n')
18+
do_test('a\\\\n', 'a\n')
19+
end
20+
21+
def test_backslash_and_newline
22+
do_test('\\\\\\n', "\\\n")
23+
do_test('a\\\\\\n', "a\\\n")
24+
end
25+
26+
def test_something
27+
do_test('hello\nthere\\\\n', "hello\nthere\\n")
28+
do_test('"\\\\\\n".size', "\"\\\n\".size")
29+
end
30+
31+
def do_test(input, expected_result)
32+
assert_equal(expected_result, Debugger::Command.unescape_incoming(input))
33+
end
34+
end

0 commit comments

Comments
 (0)