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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/exe/syntax_search +1 -0
- data/lib/syntax_search.rb +1 -0
- data/lib/syntax_search/around_block_scan.rb +106 -4
- data/lib/syntax_search/block_expand.rb +4 -8
- data/lib/syntax_search/capture_code_context.rb +62 -0
- data/lib/syntax_search/code_block.rb +9 -1
- data/lib/syntax_search/code_line.rb +42 -1
- data/lib/syntax_search/code_search.rb +17 -2
- data/lib/syntax_search/display_code_with_line_numbers.rb +56 -0
- data/lib/syntax_search/display_invalid_blocks.rb +22 -32
- data/lib/syntax_search/heredoc_block_parse.rb +5 -5
- data/lib/syntax_search/lex_all.rb +58 -0
- data/lib/syntax_search/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66acadf6481512affd39e4a1fb691ed41a6be537a8a63801f71316863d30494d
|
4
|
+
data.tar.gz: 1bcb85706516cbe5c3f53a1ed48bf5215479672952fc62d2e3221c583e468b22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e60777bd284d78436db8608f34d11b4b5aa8456bfe0c9507ddf3a0e3b97b902a2d00d1537433ed914c637a0729ccb253fc9047fb6db9e3b18a8cc8d0294ae06
|
7
|
+
data.tar.gz: 2fb1b35e70507f4b358be4b767cbf266c2d47c55efba1b77d7ca5f260cac4e77d519fc7439f55c78c5294c0c87240d15b4cab3721c8cfd580b518f5051b7cd0c
|
data/CHANGELOG.md
CHANGED
@@ -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)
|
data/Gemfile.lock
CHANGED
data/exe/syntax_search
CHANGED
data/lib/syntax_search.rb
CHANGED
@@ -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
|
-
|
66
|
-
|
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
|
178
|
+
@before_index ||= @orig_before_index
|
77
179
|
end
|
78
180
|
|
79
181
|
def after_index
|
80
|
-
@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
|
-
.
|
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
|
-
|
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 <<
|
75
|
+
string << code_with_context
|
74
76
|
string
|
75
77
|
end
|
76
78
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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 =
|
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 |
|
17
|
-
case
|
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
|
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.
|
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-
|
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
|