Skip to content

Commit e4cc8c0

Browse files
authored
Merge pull request #28 from zombocom/schneems/trailing-do
[Close #21] Concat lines with trailing slash
2 parents 8c8ea53 + 4853d63 commit e4cc8c0

File tree

10 files changed

+257
-32
lines changed

10 files changed

+257
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/pkg/
77
/spec/reports/
88
/tmp/
9+
scratch.rb
910

1011
# rspec failure tracking
1112
.rspec_status

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## HEAD (unreleased)
22

3+
- Trailing slashes are now handled (joined) before the code search (https://github.com/zombocom/syntax_search/pull/28)
4+
35
## 0.2.0
46

57
- Simplify large file output so minimal context around the invalid section is shown (https://github.com/zombocom/syntax_search/pull/26)

lib/syntax_search.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
require 'stringio'
77
require 'pathname'
88
require 'ripper'
9+
require 'timeout'
910

1011
module SyntaxErrorSearch
1112
class Error < StandardError; end
1213
SEARCH_SOURCE_ON_ERROR_DEFAULT = true
14+
TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SEARCH_TIMEOUT", 5).to_i
1315

1416
def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT)
1517
raise e if !e.message.include?("end-of-input")
@@ -32,8 +34,11 @@ def self.handle_error(e, search_source_on_error: SEARCH_SOURCE_ON_ERROR_DEFAULT)
3234
raise e
3335
end
3436

35-
def self.call(source: , filename: , terminal: false, record_dir: nil)
36-
search = CodeSearch.new(source, record_dir: record_dir).call
37+
def self.call(source: , filename: , terminal: false, record_dir: nil, timeout: TIMEOUT_DEFAULT)
38+
search = nil
39+
Timeout.timeout(timeout) do
40+
search = CodeSearch.new(source, record_dir: record_dir).call
41+
end
3742

3843
blocks = search.invalid_blocks
3944
DisplayInvalidBlocks.new(
@@ -44,6 +49,8 @@ def self.call(source: , filename: , terminal: false, record_dir: nil)
4449
invalid_type: invalid_type(source),
4550
io: $stderr
4651
).call
52+
rescue Timeout::Error
53+
$stderr.puts "Syntax search timed out SYNTAX_SEARCH_TIMEOUT=#{timeout}, run with DEBUG=1 for more info"
4754
end
4855

4956
# Used for counting spaces
@@ -143,3 +150,4 @@ def self.invalid_type(source)
143150
require_relative "syntax_search/who_dis_syntax_error"
144151
require_relative "syntax_search/heredoc_block_parse"
145152
require_relative "syntax_search/lex_all"
153+
require_relative "syntax_search/trailing_slash_join"

lib/syntax_search/code_line.rb

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module SyntaxErrorSearch
2929
# Marking a line as invisible also lets the overall program know
3030
# that it should not check that area for syntax errors.
3131
class CodeLine
32+
TRAILING_SLASH = ("\\" + $/).freeze
33+
3234
attr_reader :line, :index, :indent, :original_line
3335

3436
def initialize(line: , index:)
@@ -40,24 +42,34 @@ def initialize(line: , index:)
4042
@status = nil # valid, invalid, unknown
4143
@invalid = false
4244

43-
@kw_count = 0
44-
@end_count = 0
45-
@lex = LexAll.new(source: line)
46-
@lex.each do |lex|
45+
lex_detect!
46+
end
47+
48+
private def lex_detect!
49+
lex = LexAll.new(source: line)
50+
kw_count = 0
51+
end_count = 0
52+
lex.each do |lex|
4753
next unless lex.type == :on_kw
4854

4955
case lex.token
5056
when 'def', 'case', 'for', 'begin', 'class', 'module', 'if', 'unless', 'while', 'until' , 'do'
51-
@kw_count += 1
57+
kw_count += 1
5258
when 'end'
53-
@end_count += 1
59+
end_count += 1
5460
end
5561
end
5662

57-
@is_comment = true if @lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
63+
@is_kw = (kw_count - end_count) > 0
64+
@is_end = (end_count - kw_count) > 0
65+
@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
66+
@is_trailing_slash = lex.last.token == TRAILING_SLASH
67+
end
68+
69+
alias :original :original_line
5870

59-
@is_kw = (@kw_count - @end_count) > 0
60-
@is_end = (@end_count - @kw_count) > 0
71+
def trailing_slash?
72+
@is_trailing_slash
6173
end
6274

6375
def <=>(b)
@@ -110,7 +122,6 @@ def hidden?
110122
def line_number
111123
index + 1
112124
end
113-
114125
alias :number :line_number
115126

116127
def not_empty?

lib/syntax_search/code_search.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@ def initialize(source, record_dir: ENV["SYNTAX_SEARCH_RECORD_DIR"] || ENV["DEBUG
3535
@record_dir = Pathname(record_dir).join(@time).tap {|p| p.mkpath }
3636
@write_count = 0
3737
end
38-
@code_lines = source.lines.map.with_index do |line, i|
38+
code_lines = source.lines.map.with_index do |line, i|
3939
CodeLine.new(line: line, index: i)
4040
end
41+
42+
@code_lines = TrailingSlashJoin.new(code_lines: code_lines).call
43+
4144
@frontier = CodeFrontier.new(code_lines: @code_lines)
4245
@invalid_blocks = []
4346
@name_tick = Hash.new {|hash, k| hash[k] = 0 }

lib/syntax_search/display_code_with_line_numbers.rb

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,48 @@ class DisplayCodeWithLineNumbers
2424
TERMINAL_END = "\e[0m"
2525

2626
def initialize(lines: , highlight_lines: [], terminal: false)
27-
@lines = lines.sort
27+
@lines = Array(lines).sort
2828
@terminal = terminal
29-
@highlight_line_hash = highlight_lines.each_with_object({}) {|line, h| h[line] = true }
29+
@highlight_line_hash = Array(highlight_lines).each_with_object({}) {|line, h| h[line] = true }
3030
@digit_count = @lines.last&.line_number.to_s.length
3131
end
3232

3333
def call
3434
@lines.map do |line|
35-
string = String.new("")
36-
if @highlight_line_hash[line]
37-
string << "❯ "
38-
else
39-
string << " "
40-
end
35+
format_line(line)
36+
end.join
37+
end
4138

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
39+
private def format_line(code_line)
40+
# Handle trailing slash lines
41+
code_line.original.lines.map.with_index do |contents, i|
42+
format(
43+
empty: code_line.empty?,
44+
number: (code_line.number + i).to_s,
45+
contents: contents,
46+
highlight: @highlight_line_hash[code_line]
47+
)
5348
end.join
5449
end
50+
51+
private def format(contents: , number: , highlight: false, empty:)
52+
string = String.new("")
53+
if highlight
54+
string << "❯ "
55+
else
56+
string << " "
57+
end
58+
59+
string << number.rjust(@digit_count).to_s
60+
if empty
61+
string << contents
62+
else
63+
string << " "
64+
string << TERMINAL_HIGHLIGHT if @terminal && highlight
65+
string << contents
66+
string << TERMINAL_END if @terminal
67+
end
68+
string
69+
end
5570
end
5671
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
module SyntaxErrorSearch
4+
# Handles code that contains trailing slashes
5+
# by turning multiple lines with trailing slash(es) into
6+
# a single code line
7+
#
8+
# expect(code_lines.join).to eq(<<~EOM)
9+
# it "trailing \
10+
# "slash" do
11+
# end
12+
# EOM
13+
#
14+
# lines = TrailngSlashJoin(code_lines: code_lines).call
15+
# expect(lines.first.to_s).to eq(<<~EOM)
16+
# it "trailing \
17+
# "slash" do
18+
# EOM
19+
#
20+
class TrailingSlashJoin
21+
def initialize(code_lines:)
22+
@code_lines = code_lines
23+
@code_lines_dup = code_lines.dup
24+
end
25+
26+
def call
27+
@trailing_lines = []
28+
@code_lines.select(&:trailing_slash?).each do |trailing|
29+
stop_next = false
30+
lines = @code_lines[trailing.index..-1].take_while do |line|
31+
next false if stop_next
32+
33+
if !line.trailing_slash?
34+
stop_next = true
35+
end
36+
37+
true
38+
end
39+
40+
joined_line = CodeLine.new(line: lines.map(&:original_line).join, index: trailing.index)
41+
42+
@code_lines_dup[trailing.index] = joined_line
43+
44+
@trailing_lines << joined_line
45+
46+
lines.shift # Don't hide first trailing slash line
47+
lines.each(&:mark_invisible)
48+
end
49+
50+
return @code_lines_dup
51+
end
52+
end
53+
end

spec/unit/code_line_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44

55
module SyntaxErrorSearch
66
RSpec.describe CodeLine do
7+
it "trailing slash" do
8+
code_lines = code_line_array(<<~'EOM')
9+
it "trailing s" \
10+
"lash" do
11+
EOM
12+
13+
expect(code_lines.map(&:trailing_slash?)).to eq([true, false])
14+
15+
code_lines = code_line_array(<<~'EOM')
16+
amazing_print: ->(obj) { obj.ai + "\n" },
17+
EOM
18+
expect(code_lines.map(&:trailing_slash?)).to eq([false])
19+
end
20+
721
it "knows it's a comment" do
822
line = CodeLine.new(line: " # iama comment", index: 0)
923
expect(line.is_comment?).to be_truthy

spec/unit/code_search_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,33 @@
44

55
module SyntaxErrorSearch
66
RSpec.describe CodeSearch do
7+
it "handles no spaces between blocks" do
8+
search = CodeSearch.new(<<~'EOM')
9+
require "rails_helper"
10+
RSpec.describe TelehealthAppointment, type: :model do
11+
describe "#participants_state" do
12+
context "timezones workaround" do
13+
it "should receive a time in UTC format and return the time with the"\
14+
"office's UTC offset substracted from it" do
15+
travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
16+
office = build(:office)
17+
end
18+
end
19+
end
20+
end
21+
describe "#past?" do
22+
context "more than 15 min have passed since appointment start time" do
23+
it "returns true" do # <== HERE
24+
end
25+
end
26+
end
27+
EOM
28+
29+
search.call
30+
31+
expect(search.invalid_blocks.join.strip).to eq('it "returns true" do # <== HERE')
32+
end
33+
734
it "handles no spaces between blocks" do
835
search = CodeSearch.new(<<~EOM)
936
context "timezones workaround" do

0 commit comments

Comments
 (0)