Skip to content

[Close #21] Concat lines with trailing slash #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/pkg/
/spec/reports/
/tmp/
scratch.rb

# rspec failure tracking
.rspec_status
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## HEAD (unreleased)

- Trailing slashes are now handled (joined) before the code search (https://github.com/zombocom/syntax_search/pull/28)

## 0.2.0

- Simplify large file output so minimal context around the invalid section is shown (https://github.com/zombocom/syntax_search/pull/26)
Expand Down
12 changes: 10 additions & 2 deletions lib/syntax_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
require 'stringio'
require 'pathname'
require 'ripper'
require 'timeout'

module SyntaxErrorSearch
class Error < StandardError; end
SEARCH_SOURCE_ON_ERROR_DEFAULT = true
TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SEARCH_TIMEOUT", 5).to_i

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

def self.call(source: , filename: , terminal: false, record_dir: nil)
search = CodeSearch.new(source, record_dir: record_dir).call
def self.call(source: , filename: , terminal: false, record_dir: nil, timeout: TIMEOUT_DEFAULT)
search = nil
Timeout.timeout(timeout) do
search = CodeSearch.new(source, record_dir: record_dir).call
end

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

# Used for counting spaces
Expand Down Expand Up @@ -143,3 +150,4 @@ def self.invalid_type(source)
require_relative "syntax_search/who_dis_syntax_error"
require_relative "syntax_search/heredoc_block_parse"
require_relative "syntax_search/lex_all"
require_relative "syntax_search/trailing_slash_join"
31 changes: 21 additions & 10 deletions lib/syntax_search/code_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ module SyntaxErrorSearch
# Marking a line as invisible also lets the overall program know
# that it should not check that area for syntax errors.
class CodeLine
TRAILING_SLASH = ("\\" + $/).freeze

attr_reader :line, :index, :indent, :original_line

def initialize(line: , index:)
Expand All @@ -40,24 +42,34 @@ def initialize(line: , index:)
@status = nil # valid, invalid, unknown
@invalid = false

@kw_count = 0
@end_count = 0
@lex = LexAll.new(source: line)
@lex.each do |lex|
lex_detect!
end

private def lex_detect!
lex = LexAll.new(source: line)
kw_count = 0
end_count = 0
lex.each do |lex|
next unless lex.type == :on_kw

case lex.token
when 'def', 'case', 'for', 'begin', 'class', 'module', 'if', 'unless', 'while', 'until' , 'do'
@kw_count += 1
kw_count += 1
when 'end'
@end_count += 1
end_count += 1
end
end

@is_comment = true if @lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
@is_kw = (kw_count - end_count) > 0
@is_end = (end_count - kw_count) > 0
@is_comment = lex.detect {|lex| lex.type != :on_sp}&.type == :on_comment
@is_trailing_slash = lex.last.token == TRAILING_SLASH
end

alias :original :original_line

@is_kw = (@kw_count - @end_count) > 0
@is_end = (@end_count - @kw_count) > 0
def trailing_slash?
@is_trailing_slash
end

def <=>(b)
Expand Down Expand Up @@ -110,7 +122,6 @@ def hidden?
def line_number
index + 1
end

alias :number :line_number

def not_empty?
Expand Down
5 changes: 4 additions & 1 deletion lib/syntax_search/code_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ def initialize(source, record_dir: ENV["SYNTAX_SEARCH_RECORD_DIR"] || ENV["DEBUG
@record_dir = Pathname(record_dir).join(@time).tap {|p| p.mkpath }
@write_count = 0
end
@code_lines = source.lines.map.with_index do |line, i|
code_lines = source.lines.map.with_index do |line, i|
CodeLine.new(line: line, index: i)
end

@code_lines = TrailingSlashJoin.new(code_lines: code_lines).call

@frontier = CodeFrontier.new(code_lines: @code_lines)
@invalid_blocks = []
@name_tick = Hash.new {|hash, k| hash[k] = 0 }
Expand Down
53 changes: 34 additions & 19 deletions lib/syntax_search/display_code_with_line_numbers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,48 @@ class DisplayCodeWithLineNumbers
TERMINAL_END = "\e[0m"

def initialize(lines: , highlight_lines: [], terminal: false)
@lines = lines.sort
@lines = Array(lines).sort
@terminal = terminal
@highlight_line_hash = highlight_lines.each_with_object({}) {|line, h| h[line] = true }
@highlight_line_hash = Array(highlight_lines).each_with_object({}) {|line, h| h[line] = true }
@digit_count = @lines.last&.line_number.to_s.length
end

def call
@lines.map do |line|
string = String.new("")
if @highlight_line_hash[line]
string << "❯ "
else
string << " "
end
format_line(line)
end.join
end

number = line.line_number.to_s.rjust(@digit_count)
string << number.to_s
if line.empty?
string << line.original_line
else
string << " "
string << TERMINAL_HIGHLIGHT if @terminal && @highlight_line_hash[line] # Bold, italics
string << line.original_line
string << TERMINAL_END if @terminal
end
string
private def format_line(code_line)
# Handle trailing slash lines
code_line.original.lines.map.with_index do |contents, i|
format(
empty: code_line.empty?,
number: (code_line.number + i).to_s,
contents: contents,
highlight: @highlight_line_hash[code_line]
)
end.join
end

private def format(contents: , number: , highlight: false, empty:)
string = String.new("")
if highlight
string << "❯ "
else
string << " "
end

string << number.rjust(@digit_count).to_s
if empty
string << contents
else
string << " "
string << TERMINAL_HIGHLIGHT if @terminal && highlight
string << contents
string << TERMINAL_END if @terminal
end
string
end
end
end
53 changes: 53 additions & 0 deletions lib/syntax_search/trailing_slash_join.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

module SyntaxErrorSearch
# Handles code that contains trailing slashes
# by turning multiple lines with trailing slash(es) into
# a single code line
#
# expect(code_lines.join).to eq(<<~EOM)
# it "trailing \
# "slash" do
# end
# EOM
#
# lines = TrailngSlashJoin(code_lines: code_lines).call
# expect(lines.first.to_s).to eq(<<~EOM)
# it "trailing \
# "slash" do
# EOM
#
class TrailingSlashJoin
def initialize(code_lines:)
@code_lines = code_lines
@code_lines_dup = code_lines.dup
end

def call
@trailing_lines = []
@code_lines.select(&:trailing_slash?).each do |trailing|
stop_next = false
lines = @code_lines[trailing.index..-1].take_while do |line|
next false if stop_next

if !line.trailing_slash?
stop_next = true
end

true
end

joined_line = CodeLine.new(line: lines.map(&:original_line).join, index: trailing.index)

@code_lines_dup[trailing.index] = joined_line

@trailing_lines << joined_line

lines.shift # Don't hide first trailing slash line
lines.each(&:mark_invisible)
end

return @code_lines_dup
end
end
end
14 changes: 14 additions & 0 deletions spec/unit/code_line_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

module SyntaxErrorSearch
RSpec.describe CodeLine do
it "trailing slash" do
code_lines = code_line_array(<<~'EOM')
it "trailing s" \
"lash" do
EOM

expect(code_lines.map(&:trailing_slash?)).to eq([true, false])

code_lines = code_line_array(<<~'EOM')
amazing_print: ->(obj) { obj.ai + "\n" },
EOM
expect(code_lines.map(&:trailing_slash?)).to eq([false])
end

it "knows it's a comment" do
line = CodeLine.new(line: " # iama comment", index: 0)
expect(line.is_comment?).to be_truthy
Expand Down
27 changes: 27 additions & 0 deletions spec/unit/code_search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@

module SyntaxErrorSearch
RSpec.describe CodeSearch do
it "handles no spaces between blocks" do
search = CodeSearch.new(<<~'EOM')
require "rails_helper"
RSpec.describe TelehealthAppointment, type: :model do
describe "#participants_state" do
context "timezones workaround" do
it "should receive a time in UTC format and return the time with the"\
"office's UTC offset substracted from it" do
travel_to DateTime.new(2020, 10, 1, 10, 0, 0) do
office = build(:office)
end
end
end
end
describe "#past?" do
context "more than 15 min have passed since appointment start time" do
it "returns true" do # <== HERE
end
end
end
EOM

search.call

expect(search.invalid_blocks.join.strip).to eq('it "returns true" do # <== HERE')
end

it "handles no spaces between blocks" do
search = CodeSearch.new(<<~EOM)
context "timezones workaround" do
Expand Down
Loading