зеркало из https://github.com/github/ruby.git
225 строки
4.9 KiB
Ruby
225 строки
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module SyntaxSuggest
|
|
# This class is useful for exploring contents before and after
|
|
# a block
|
|
#
|
|
# It searches above and below the passed in block to match for
|
|
# whatever criteria you give it:
|
|
#
|
|
# Example:
|
|
#
|
|
# def dog # 1
|
|
# puts "bark" # 2
|
|
# puts "bark" # 3
|
|
# end # 4
|
|
#
|
|
# scan = AroundBlockScan.new(
|
|
# code_lines: code_lines
|
|
# block: CodeBlock.new(lines: code_lines[1])
|
|
# )
|
|
#
|
|
# scan.scan_while { true }
|
|
#
|
|
# puts scan.before_index # => 0
|
|
# puts scan.after_index # => 3
|
|
#
|
|
# Contents can also be filtered using AroundBlockScan#skip
|
|
#
|
|
# To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
|
|
class AroundBlockScan
|
|
def initialize(code_lines:, block:)
|
|
@code_lines = code_lines
|
|
@orig_before_index = block.lines.first.index
|
|
@orig_after_index = block.lines.last.index
|
|
@orig_indent = block.current_indent
|
|
@skip_array = []
|
|
@after_array = []
|
|
@before_array = []
|
|
@stop_after_kw = false
|
|
|
|
@skip_hidden = false
|
|
@skip_empty = false
|
|
end
|
|
|
|
def skip(name)
|
|
case name
|
|
when :hidden?
|
|
@skip_hidden = true
|
|
when :empty?
|
|
@skip_empty = true
|
|
else
|
|
raise "Unsupported skip #{name}"
|
|
end
|
|
self
|
|
end
|
|
|
|
def stop_after_kw
|
|
@stop_after_kw = true
|
|
self
|
|
end
|
|
|
|
def scan_while
|
|
stop_next = false
|
|
|
|
kw_count = 0
|
|
end_count = 0
|
|
index = before_lines.reverse_each.take_while do |line|
|
|
next false if stop_next
|
|
next true if @skip_hidden && line.hidden?
|
|
next true if @skip_empty && line.empty?
|
|
|
|
kw_count += 1 if line.is_kw?
|
|
end_count += 1 if line.is_end?
|
|
if @stop_after_kw && kw_count > end_count
|
|
stop_next = true
|
|
end
|
|
|
|
yield line
|
|
end.last&.index
|
|
|
|
if index && index < before_index
|
|
@before_index = index
|
|
end
|
|
|
|
stop_next = false
|
|
kw_count = 0
|
|
end_count = 0
|
|
index = after_lines.take_while do |line|
|
|
next false if stop_next
|
|
next true if @skip_hidden && line.hidden?
|
|
next true if @skip_empty && line.empty?
|
|
|
|
kw_count += 1 if line.is_kw?
|
|
end_count += 1 if line.is_end?
|
|
if @stop_after_kw && end_count > kw_count
|
|
stop_next = true
|
|
end
|
|
|
|
yield line
|
|
end.last&.index
|
|
|
|
if index && index > after_index
|
|
@after_index = index
|
|
end
|
|
self
|
|
end
|
|
|
|
def capture_neighbor_context
|
|
lines = []
|
|
kw_count = 0
|
|
end_count = 0
|
|
before_lines.reverse_each do |line|
|
|
next if line.empty?
|
|
break if line.indent < @orig_indent
|
|
next if line.indent != @orig_indent
|
|
|
|
kw_count += 1 if line.is_kw?
|
|
end_count += 1 if line.is_end?
|
|
if kw_count != 0 && kw_count == end_count
|
|
lines << line
|
|
break
|
|
end
|
|
|
|
lines << line
|
|
end
|
|
|
|
lines.reverse!
|
|
|
|
kw_count = 0
|
|
end_count = 0
|
|
after_lines.each do |line|
|
|
next if line.empty?
|
|
break if line.indent < @orig_indent
|
|
next if line.indent != @orig_indent
|
|
|
|
kw_count += 1 if line.is_kw?
|
|
end_count += 1 if line.is_end?
|
|
if kw_count != 0 && kw_count == end_count
|
|
lines << line
|
|
break
|
|
end
|
|
|
|
lines << line
|
|
end
|
|
|
|
lines
|
|
end
|
|
|
|
def on_falling_indent
|
|
last_indent = @orig_indent
|
|
before_lines.reverse_each do |line|
|
|
next if line.empty?
|
|
if line.indent < last_indent
|
|
yield line
|
|
last_indent = line.indent
|
|
end
|
|
end
|
|
|
|
last_indent = @orig_indent
|
|
after_lines.each do |line|
|
|
next if line.empty?
|
|
if line.indent < last_indent
|
|
yield line
|
|
last_indent = line.indent
|
|
end
|
|
end
|
|
end
|
|
|
|
def scan_neighbors
|
|
scan_while { |line| line.not_empty? && line.indent >= @orig_indent }
|
|
end
|
|
|
|
def next_up
|
|
@code_lines[before_index.pred]
|
|
end
|
|
|
|
def next_down
|
|
@code_lines[after_index.next]
|
|
end
|
|
|
|
def scan_adjacent_indent
|
|
before_after_indent = []
|
|
before_after_indent << (next_up&.indent || 0)
|
|
before_after_indent << (next_down&.indent || 0)
|
|
|
|
indent = before_after_indent.min
|
|
scan_while { |line| line.not_empty? && line.indent >= indent }
|
|
|
|
self
|
|
end
|
|
|
|
def start_at_next_line
|
|
before_index
|
|
after_index
|
|
@before_index -= 1
|
|
@after_index += 1
|
|
self
|
|
end
|
|
|
|
def code_block
|
|
CodeBlock.new(lines: lines)
|
|
end
|
|
|
|
def lines
|
|
@code_lines[before_index..after_index]
|
|
end
|
|
|
|
def before_index
|
|
@before_index ||= @orig_before_index
|
|
end
|
|
|
|
def after_index
|
|
@after_index ||= @orig_after_index
|
|
end
|
|
|
|
private def before_lines
|
|
@code_lines[0...before_index] || []
|
|
end
|
|
|
|
private def after_lines
|
|
@code_lines[after_index.next..-1] || []
|
|
end
|
|
end
|
|
end
|