syntax_search 0.1.5 → 0.2.0

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: 77f054eda8b4e25dcaf0306818ae17bb1ff3da9c52f2d606141ff44cd343c51d
4
- data.tar.gz: 44f4347d5078d0250286e78a08ff730b02094aeb05897c9088963381a95e9071
3
+ metadata.gz: 66acadf6481512affd39e4a1fb691ed41a6be537a8a63801f71316863d30494d
4
+ data.tar.gz: 1bcb85706516cbe5c3f53a1ed48bf5215479672952fc62d2e3221c583e468b22
5
5
  SHA512:
6
- metadata.gz: 6ccc7e2ed620689614bdfae8943b8c15b81b4567d3b42e2c734ac7974699ed231272ce52929eff9d49eb25edd843ae3f7fa2398d0552b77665be779a4412461f
7
- data.tar.gz: 1e866b4aaafb534f946da5e00efb50536a4d20bf0e1527f8641fb298cca5cc6b7d0e1f744f63da443b9920500d7d4ddf0113ffa8bde2b4689cd6793538a12b6e
6
+ metadata.gz: 4e60777bd284d78436db8608f34d11b4b5aa8456bfe0c9507ddf3a0e3b97b902a2d00d1537433ed914c637a0729ccb253fc9047fb6db9e3b18a8cc8d0294ae06
7
+ data.tar.gz: 2fb1b35e70507f4b358be4b767cbf266c2d47c55efba1b77d7ca5f260cac4e77d519fc7439f55c78c5294c0c87240d15b4cab3721c8cfd580b518f5051b7cd0c
@@ -1,5 +1,11 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
+ ## 0.2.0
4
+
5
+ - Simplify large file output so minimal context around the invalid section is shown (https://github.com/zombocom/syntax_search/pull/26)
6
+ - Block expansion is now lexically aware of keywords (def/do/end etc.) (https://github.com/zombocom/syntax_search/pull/24)
7
+ - Fix bug where not all of a source is lexed which is used in heredoc detection/removal (https://github.com/zombocom/syntax_search/pull/23)
8
+
3
9
  ## 0.1.5
4
10
 
5
11
  - Strip out heredocs in documents first (https://github.com/zombocom/syntax_search/pull/19)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- syntax_search (0.1.5)
4
+ syntax_search (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -62,6 +62,7 @@ if file.nil? || file.empty?
62
62
  end
63
63
 
64
64
  file = Pathname(file)
65
+ options[:record_dir] = "tmp" if ENV["DEBUG"]
65
66
 
66
67
  $stderr.puts "Record dir: #{options[:record_dir]}" if options[:record_dir]
67
68
 
@@ -142,3 +142,4 @@ require_relative "syntax_search/parse_blocks_from_indent_line"
142
142
  require_relative "syntax_search/code_search"
143
143
  require_relative "syntax_search/who_dis_syntax_error"
144
144
  require_relative "syntax_search/heredoc_block_parse"
145
+ require_relative "syntax_search/lex_all"
@@ -32,9 +32,11 @@ module SyntaxErrorSearch
32
32
  @code_lines = code_lines
33
33
  @orig_before_index = block.lines.first.index
34
34
  @orig_after_index = block.lines.last.index
35
+ @orig_indent = block.current_indent
35
36
  @skip_array = []
36
37
  @after_array = []
37
38
  @before_array = []
39
+ @stop_after_kw = false
38
40
  end
39
41
 
40
42
  def skip(name)
@@ -42,29 +44,129 @@ module SyntaxErrorSearch
42
44
  self
43
45
  end
44
46
 
47
+ def stop_after_kw
48
+ @stop_after_kw = true
49
+ self
50
+ end
51
+
45
52
  def scan_while(&block)
53
+ stop_next = false
54
+
55
+ kw_count = 0
56
+ end_count = 0
46
57
  @before_index = before_lines.reverse_each.take_while do |line|
58
+ next false if stop_next
47
59
  next true if @skip_array.detect {|meth| line.send(meth) }
48
60
 
61
+ kw_count += 1 if line.is_kw?
62
+ end_count += 1 if line.is_end?
63
+ if @stop_after_kw && kw_count > end_count
64
+ stop_next = true
65
+ end
66
+
49
67
  block.call(line)
50
68
  end.reverse.first&.index
51
69
 
70
+ stop_next = false
71
+ kw_count = 0
72
+ end_count = 0
52
73
  @after_index = after_lines.take_while do |line|
74
+ next false if stop_next
53
75
  next true if @skip_array.detect {|meth| line.send(meth) }
54
76
 
77
+ kw_count += 1 if line.is_kw?
78
+ end_count += 1 if line.is_end?
79
+ if @stop_after_kw && end_count > kw_count
80
+ stop_next = true
81
+ end
82
+
55
83
  block.call(line)
56
84
  end.last&.index
57
85
  self
58
86
  end
59
87
 
88
+ def capture_neighbor_context
89
+ lines = []
90
+ kw_count = 0
91
+ end_count = 0
92
+ before_lines.reverse.each do |line|
93
+ next if line.empty?
94
+ break if line.indent < @orig_indent
95
+ next if line.indent != @orig_indent
96
+
97
+ kw_count += 1 if line.is_kw?
98
+ end_count += 1 if line.is_end?
99
+ if kw_count != 0 && kw_count == end_count
100
+ lines << line
101
+ break
102
+ end
103
+
104
+ lines << line
105
+ end
106
+
107
+ lines.reverse!
108
+
109
+ kw_count = 0
110
+ end_count = 0
111
+ after_lines.each do |line|
112
+ # puts "line: #{line.number} #{line.original_line}, indent: #{line.indent}, #{line.empty?} #{line.indent == @orig_indent}"
113
+
114
+ next if line.empty?
115
+ break if line.indent < @orig_indent
116
+ next if line.indent != @orig_indent
117
+
118
+ kw_count += 1 if line.is_kw?
119
+ end_count += 1 if line.is_end?
120
+ if kw_count != 0 && kw_count == end_count
121
+ lines << line
122
+ break
123
+ end
124
+
125
+ lines << line
126
+ end
127
+ lines.select! {|line| !line.is_comment? }
128
+
129
+ lines
130
+ end
131
+
132
+ def on_falling_indent
133
+ last_indent = @orig_indent
134
+ before_lines.reverse.each do |line|
135
+ next if line.empty?
136
+ if line.indent < last_indent
137
+ yield line
138
+ last_indent = line.indent
139
+ end
140
+ end
141
+
142
+ last_indent = @orig_indent
143
+ after_lines.each do |line|
144
+ next if line.empty?
145
+ if line.indent < last_indent
146
+ yield line
147
+ last_indent = line.indent
148
+ end
149
+ end
150
+ end
151
+
152
+ def scan_neighbors
153
+ self.scan_while {|line| line.not_empty? && line.indent >= @orig_indent }
154
+ end
155
+
60
156
  def scan_adjacent_indent
61
157
  before_indent = @code_lines[@orig_before_index.pred]&.indent || 0
62
158
  after_indent = @code_lines[@orig_after_index.next]&.indent || 0
63
159
 
64
160
  indent = [before_indent, after_indent].min
65
- @before_index = before_index.pred if before_indent >= indent
66
- @after_index = after_index.next if after_indent >= indent
161
+ self.scan_while {|line| line.not_empty? && line.indent >= indent }
162
+
163
+ self
164
+ end
67
165
 
166
+ def start_at_next_line
167
+ before_index; after_index
168
+ @before_index -= 1
169
+ @after_index += 1
68
170
  self
69
171
  end
70
172
 
@@ -73,11 +175,11 @@ module SyntaxErrorSearch
73
175
  end
74
176
 
75
177
  def before_index
76
- @before_index || @orig_before_index
178
+ @before_index ||= @orig_before_index
77
179
  end
78
180
 
79
181
  def after_index
80
- @after_index || @orig_after_index
182
+ @after_index ||= @orig_after_index
81
183
  end
82
184
 
83
185
  private def before_lines
@@ -44,21 +44,17 @@ module SyntaxErrorSearch
44
44
 
45
45
  def expand_indent(block)
46
46
  block = AroundBlockScan.new(code_lines: @code_lines, block: block)
47
+ .skip(:hidden?)
48
+ .stop_after_kw
47
49
  .scan_adjacent_indent
48
50
  .code_block
49
-
50
- # Handle if/else/end case
51
- if (next_block = expand_neighbors(block, grab_empty: false))
52
- return next_block
53
- else
54
- return block
55
- end
56
51
  end
57
52
 
58
53
  def expand_neighbors(block, grab_empty: true)
59
54
  scan = AroundBlockScan.new(code_lines: @code_lines, block: block)
60
55
  .skip(:hidden?)
61
- .scan_while {|line| line.not_empty? && line.indent >= block.current_indent }
56
+ .stop_after_kw
57
+ .scan_neighbors
62
58
 
63
59
  # Slurp up empties
64
60
  if grab_empty
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxErrorSearch
4
+
5
+ # Given a block, this method will capture surrounding
6
+ # code to give the user more context for the location of
7
+ # the problem.
8
+ #
9
+ # Return is an array of CodeLines to be rendered.
10
+ #
11
+ # Surrounding code is captured regardless of visible state
12
+ #
13
+ # puts block.to_s # => "def bark"
14
+ #
15
+ # context = CaptureCodeContext.new(
16
+ # blocks: block,
17
+ # code_lines: code_lines
18
+ # )
19
+ #
20
+ # puts context.call.join
21
+ # # =>
22
+ # class Dog
23
+ # def bark
24
+ # end
25
+ #
26
+ class CaptureCodeContext
27
+ attr_reader :code_lines
28
+
29
+ def initialize(blocks: , code_lines:)
30
+ @blocks = Array(blocks)
31
+ @code_lines = code_lines
32
+ @visible_lines = @blocks.map(&:visible_lines).flatten
33
+ @lines_to_output = @visible_lines.dup
34
+ end
35
+
36
+ def call
37
+ @blocks.each do |block|
38
+ around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
39
+ .start_at_next_line
40
+ .capture_neighbor_context
41
+
42
+ around_lines -= block.lines
43
+
44
+ @lines_to_output.concat(around_lines)
45
+
46
+ AroundBlockScan.new(
47
+ block: block,
48
+ code_lines: @code_lines,
49
+ ).on_falling_indent do |line|
50
+ @lines_to_output << line
51
+ end
52
+ end
53
+
54
+ @lines_to_output.select!(&:not_empty?)
55
+ @lines_to_output.select!(&:not_comment?)
56
+ @lines_to_output.uniq!
57
+ @lines_to_output.sort!
58
+
59
+ return @lines_to_output
60
+ end
61
+ end
62
+ end
@@ -23,6 +23,10 @@ module SyntaxErrorSearch
23
23
  @lines = Array(lines)
24
24
  end
25
25
 
26
+ def visible_lines
27
+ @lines.select(&:visible?).select(&:not_empty?)
28
+ end
29
+
26
30
  def mark_invisible
27
31
  @lines.map(&:mark_invisible)
28
32
  end
@@ -48,7 +52,11 @@ module SyntaxErrorSearch
48
52
  # populate an array with multiple code blocks then call `sort!`
49
53
  # on it without having to specify the sorting criteria
50
54
  def <=>(other)
51
- self.current_indent <=> other.current_indent
55
+ out = self.current_indent <=> other.current_indent
56
+ return out if out != 0
57
+
58
+ # Stable sort
59
+ self.starts_at <=> other.starts_at
52
60
  end
53
61
 
54
62
  def current_indent
@@ -29,7 +29,7 @@ module SyntaxErrorSearch
29
29
  # Marking a line as invisible also lets the overall program know
30
30
  # that it should not check that area for syntax errors.
31
31
  class CodeLine
32
- attr_reader :line, :index, :indent
32
+ attr_reader :line, :index, :indent, :original_line
33
33
 
34
34
  def initialize(line: , index:)
35
35
  @original_line = line.freeze
@@ -39,6 +39,45 @@ module SyntaxErrorSearch
39
39
  @indent = SpaceCount.indent(line)
40
40
  @status = nil # valid, invalid, unknown
41
41
  @invalid = false
42
+
43
+ @kw_count = 0
44
+ @end_count = 0
45
+ @lex = LexAll.new(source: line)
46
+ @lex.each do |lex|
47
+ next unless lex.type == :on_kw
48
+
49
+ case lex.token
50
+ when 'def', 'case', 'for', 'begin', 'class', 'module', 'if', 'unless', 'while', 'until' , 'do'
51
+ @kw_count += 1
52
+ when 'end'
53
+ @end_count += 1
54
+ end
55
+ end
56
+
57
+ @is_comment = true if @lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
58
+
59
+ @is_kw = (@kw_count - @end_count) > 0
60
+ @is_end = (@end_count - @kw_count) > 0
61
+ end
62
+
63
+ def <=>(b)
64
+ self.index <=> b.index
65
+ end
66
+
67
+ def is_comment?
68
+ @is_comment
69
+ end
70
+
71
+ def not_comment?
72
+ !is_comment?
73
+ end
74
+
75
+ def is_kw?
76
+ @is_kw
77
+ end
78
+
79
+ def is_end?
80
+ @is_end
42
81
  end
43
82
 
44
83
  def mark_invalid
@@ -72,6 +111,8 @@ module SyntaxErrorSearch
72
111
  index + 1
73
112
  end
74
113
 
114
+ alias :number :line_number
115
+
75
116
  def not_empty?
76
117
  !empty?
77
118
  end
@@ -28,7 +28,7 @@ module SyntaxErrorSearch
28
28
  private; attr_reader :frontier; public
29
29
  public; attr_reader :invalid_blocks, :record_dir, :code_lines
30
30
 
31
- def initialize(source, record_dir: ENV["SYNTAX_SEARCH_RECORD_DIR"])
31
+ def initialize(source, record_dir: ENV["SYNTAX_SEARCH_RECORD_DIR"] || ENV["DEBUG"] ? "tmp" : nil)
32
32
  @source = source
33
33
  if record_dir
34
34
  @time = Time.now.strftime('%Y-%m-%d-%H-%M-%s-%N')
@@ -79,6 +79,14 @@ module SyntaxErrorSearch
79
79
  end
80
80
  end
81
81
 
82
+ # Removes the block without putting it back in the frontier
83
+ def sweep(block:, name: )
84
+ record(block: block, name: name)
85
+
86
+ block.lines.each(&:mark_invisible)
87
+ frontier.register_indent_block(block)
88
+ end
89
+
82
90
  # Parses the most indented lines into blocks that are marked
83
91
  # and added to the frontier
84
92
  def add_invalid_blocks
@@ -108,7 +116,6 @@ module SyntaxErrorSearch
108
116
  push(block, name: "expand")
109
117
  end
110
118
 
111
-
112
119
  def sweep_heredocs
113
120
  HeredocBlockParse.new(
114
121
  source: @source,
@@ -118,9 +125,17 @@ module SyntaxErrorSearch
118
125
  end
119
126
  end
120
127
 
128
+ def sweep_comments
129
+ lines = @code_lines.select(&:is_comment?)
130
+ return if lines.empty?
131
+ block = CodeBlock.new(lines: lines)
132
+ sweep(block: block, name: "comments")
133
+ end
134
+
121
135
  # Main search loop
122
136
  def call
123
137
  sweep_heredocs
138
+ sweep_comments
124
139
  until frontier.holds_all_syntax_errors?
125
140
  @tick += 1
126
141
 
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxErrorSearch
4
+ # Outputs code with highlighted lines
5
+ #
6
+ # Whatever is passed to this class will be rendered
7
+ # even if it is "marked invisible" any filtering of
8
+ # output should be done before calling this class.
9
+ #
10
+ #
11
+ # DisplayCodeWithLineNumbers.new(
12
+ # lines: lines,
13
+ # highlight_lines: [lines[2], lines[3]]
14
+ # ).call
15
+ # # =>
16
+ # 1
17
+ # 2 def cat
18
+ # ❯ 3 Dir.chdir
19
+ # ❯ 4 end
20
+ # 5 end
21
+ # 6
22
+ class DisplayCodeWithLineNumbers
23
+ TERMINAL_HIGHLIGHT = "\e[1;3m" # Bold, italics
24
+ TERMINAL_END = "\e[0m"
25
+
26
+ def initialize(lines: , highlight_lines: [], terminal: false)
27
+ @lines = lines.sort
28
+ @terminal = terminal
29
+ @highlight_line_hash = highlight_lines.each_with_object({}) {|line, h| h[line] = true }
30
+ @digit_count = @lines.last&.line_number.to_s.length
31
+ end
32
+
33
+ def call
34
+ @lines.map do |line|
35
+ string = String.new("")
36
+ if @highlight_line_hash[line]
37
+ string << "❯ "
38
+ else
39
+ string << " "
40
+ end
41
+
42
+ number = line.line_number.to_s.rjust(@digit_count)
43
+ string << number.to_s
44
+ if line.empty?
45
+ string << line.original_line
46
+ else
47
+ string << " "
48
+ string << TERMINAL_HIGHLIGHT if @terminal && @highlight_line_hash[line] # Bold, italics
49
+ string << line.original_line
50
+ string << TERMINAL_END if @terminal
51
+ end
52
+ string
53
+ end.join
54
+ end
55
+ end
56
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "capture_code_context"
4
+ require_relative "display_code_with_line_numbers"
5
+
3
6
  module SyntaxErrorSearch
4
7
  # Used for formatting invalid blocks
5
8
  class DisplayInvalidBlocks
@@ -11,11 +14,10 @@ module SyntaxErrorSearch
11
14
  @io = io
12
15
 
13
16
  @blocks = Array(blocks)
14
- @lines = @blocks.map(&:lines).flatten
17
+
18
+ @invalid_lines = @blocks.map(&:lines).flatten
15
19
  @code_lines = code_lines
16
- @digit_count = @code_lines.last&.line_number.to_s.length
17
20
 
18
- @invalid_line_hash = @lines.each_with_object({}) {|line, h| h[line] = true }
19
21
  @invalid_type = invalid_type
20
22
  end
21
23
 
@@ -70,41 +72,29 @@ module SyntaxErrorSearch
70
72
 
71
73
  def code_block
72
74
  string = String.new("")
73
- string << code_with_lines
75
+ string << code_with_context
74
76
  string
75
77
  end
76
78
 
77
- def terminal_end
78
- "\e[0m"
79
- end
80
-
81
- def terminal_highlight
82
- "\e[1;3m" # Bold, italics
79
+ def code_with_context
80
+ lines = CaptureCodeContext.new(
81
+ blocks: @blocks,
82
+ code_lines: @code_lines
83
+ ).call
84
+
85
+ DisplayCodeWithLineNumbers.new(
86
+ lines: lines,
87
+ terminal: @terminal,
88
+ highlight_lines: @invalid_lines,
89
+ ).call
83
90
  end
84
91
 
85
92
  def code_with_lines
86
- @code_lines.map do |line|
87
- next if line.hidden?
88
-
89
- string = String.new("")
90
- if @invalid_line_hash[line]
91
- string << "❯ "
92
- else
93
- string << " "
94
- end
95
-
96
- number = line.line_number.to_s.rjust(@digit_count)
97
- string << number.to_s
98
- if line.empty?
99
- string << line.to_s
100
- else
101
- string << " "
102
- string << terminal_highlight if @terminal && @invalid_line_hash[line] # Bold, italics
103
- string << line.to_s
104
- string << terminal_end if @terminal
105
- end
106
- string
107
- end.join
93
+ DisplayCodeWithLineNumbers.new(
94
+ lines: @code_lines.select(&:visible?),
95
+ terminal: @terminal,
96
+ highlight_lines: @invalid_lines,
97
+ ).call
108
98
  end
109
99
  end
110
100
  end
@@ -7,19 +7,19 @@ module SyntaxErrorSearch
7
7
 
8
8
  def initialize(source:, code_lines: )
9
9
  @code_lines = code_lines
10
- @lex = Ripper.lex(source)
10
+ @lex = LexAll.new(source: source)
11
11
  end
12
12
 
13
13
  def call
14
14
  blocks = []
15
15
  beginning = []
16
- @lex.each do |(line, col), event, *_|
17
- case event
16
+ @lex.each do |lex|
17
+ case lex.type
18
18
  when :on_heredoc_beg
19
- beginning << line
19
+ beginning << lex.line
20
20
  when :on_heredoc_end
21
21
  start_index = beginning.pop - 1
22
- end_index = line - 1
22
+ end_index = lex.line - 1
23
23
  blocks << CodeBlock.new(lines: code_lines[start_index..end_index])
24
24
  end
25
25
  end
@@ -0,0 +1,58 @@
1
+ module SyntaxErrorSearch
2
+ # Ripper.lex is not guaranteed to lex the entire source document
3
+ #
4
+ # lex = LexAll.new(source: source)
5
+ # lex.each do |value|
6
+ # puts value.line
7
+ # end
8
+ class LexAll
9
+ include Enumerable
10
+
11
+ def initialize(source: )
12
+ @lex = Ripper.lex(source)
13
+ lineno = @lex.last&.first&.first + 1
14
+ source_lines = source.lines
15
+ last_lineno = source_lines.count
16
+
17
+ until lineno >= last_lineno
18
+ lines = source_lines[lineno..-1]
19
+
20
+ @lex.concat(Ripper.lex(lines.join, '-', lineno + 1))
21
+ lineno = @lex.last&.first&.first + 1
22
+ end
23
+
24
+ @lex.map! {|(line, _), type, token| LexValue.new(line, _, type, token) }
25
+ end
26
+
27
+ def each
28
+ return @lex.each unless block_given?
29
+ @lex.each do |x|
30
+ yield x
31
+ end
32
+ end
33
+
34
+ def last
35
+ @lex.last
36
+ end
37
+
38
+ # Value object for accessing lex values
39
+ #
40
+ # This lex:
41
+ #
42
+ # [1, 0], :on_ident, "describe", CMDARG
43
+ #
44
+ # Would translate into:
45
+ #
46
+ # lex.line # => 1
47
+ # lex.type # => :on_indent
48
+ # lex.token # => "describe"
49
+ class LexValue
50
+ attr_reader :line, :type, :token
51
+ def initialize(line, _, type, token)
52
+ @line = line
53
+ @type = type
54
+ @token = token
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxErrorSearch
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - schneems
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-02 00:00:00.000000000 Z
11
+ date: 2020-12-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: When you get an "unexpected end" in your syntax this gem helps you find
14
14
  it
@@ -39,13 +39,16 @@ files:
39
39
  - lib/syntax_search/around_block_scan.rb
40
40
  - lib/syntax_search/auto.rb
41
41
  - lib/syntax_search/block_expand.rb
42
+ - lib/syntax_search/capture_code_context.rb
42
43
  - lib/syntax_search/code_block.rb
43
44
  - lib/syntax_search/code_frontier.rb
44
45
  - lib/syntax_search/code_line.rb
45
46
  - lib/syntax_search/code_search.rb
47
+ - lib/syntax_search/display_code_with_line_numbers.rb
46
48
  - lib/syntax_search/display_invalid_blocks.rb
47
49
  - lib/syntax_search/fyi.rb
48
50
  - lib/syntax_search/heredoc_block_parse.rb
51
+ - lib/syntax_search/lex_all.rb
49
52
  - lib/syntax_search/parse_blocks_from_indent_line.rb
50
53
  - lib/syntax_search/version.rb
51
54
  - lib/syntax_search/who_dis_syntax_error.rb