晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
| DIR:/opt/alt/ruby33/share/ruby/syntax_suggest/ |
| Current File : //opt/alt/ruby33/share/ruby/syntax_suggest/code_search.rb |
# frozen_string_literal: true
module SyntaxSuggest
# Searches code for a syntax error
#
# There are three main phases in the algorithm:
#
# 1. Sanitize/format input source
# 2. Search for invalid blocks
# 3. Format invalid blocks into something meaninful
#
# This class handles the part.
#
# The bulk of the heavy lifting is done in:
#
# - CodeFrontier (Holds information for generating blocks and determining if we can stop searching)
# - ParseBlocksFromLine (Creates blocks into the frontier)
# - BlockExpand (Expands existing blocks to search more code)
#
# ## Syntax error detection
#
# When the frontier holds the syntax error, we can stop searching
#
# search = CodeSearch.new(<<~EOM)
# def dog
# def lol
# end
# EOM
#
# search.call
#
# search.invalid_blocks.map(&:to_s) # =>
# # => ["def lol\n"]
#
class CodeSearch
private
attr_reader :frontier
public
attr_reader :invalid_blocks, :record_dir, :code_lines
def initialize(source, record_dir: DEFAULT_VALUE)
record_dir = if record_dir == DEFAULT_VALUE
(ENV["SYNTAX_SUGGEST_RECORD_DIR"] || ENV["SYNTAX_SUGGEST_DEBUG"]) ? "tmp" : nil
else
record_dir
end
if record_dir
@record_dir = SyntaxSuggest.record_dir(record_dir)
@write_count = 0
end
@tick = 0
@source = source
@name_tick = Hash.new { |hash, k| hash[k] = 0 }
@invalid_blocks = []
@code_lines = CleanDocument.new(source: source).call.lines
@frontier = CodeFrontier.new(code_lines: @code_lines)
@block_expand = BlockExpand.new(code_lines: @code_lines)
@parse_blocks_from_indent_line = ParseBlocksFromIndentLine.new(code_lines: @code_lines)
end
# Used for debugging
def record(block:, name: "record")
return unless @record_dir
@name_tick[name] += 1
filename = "#{@write_count += 1}-#{name}-#{@name_tick[name]}-(#{block.starts_at}__#{block.ends_at}).txt"
if ENV["SYNTAX_SUGGEST_DEBUG"]
puts "\n\n==== #{filename} ===="
puts "\n```#{block.starts_at}..#{block.ends_at}"
puts block
puts "```"
puts " block indent: #{block.current_indent}"
end
@record_dir.join(filename).open(mode: "a") do |f|
document = DisplayCodeWithLineNumbers.new(
lines: @code_lines.select(&:visible?),
terminal: false,
highlight_lines: block.lines
).call
f.write(" Block lines: #{block.starts_at..block.ends_at} (#{name}) \n\n#{document}")
end
end
def push(block, name:)
record(block: block, name: name)
block.mark_invisible if block.valid?
frontier << block
end
# Parses the most indented lines into blocks that are marked
# and added to the frontier
def create_blocks_from_untracked_lines
max_indent = frontier.next_indent_line&.indent
while (line = frontier.next_indent_line) && (line.indent == max_indent)
@parse_blocks_from_indent_line.each_neighbor_block(frontier.next_indent_line) do |block|
push(block, name: "add")
end
end
end
# Given an already existing block in the frontier, expand it to see
# if it contains our invalid syntax
def expand_existing
block = frontier.pop
return unless block
record(block: block, name: "before-expand")
block = @block_expand.call(block)
push(block, name: "expand")
end
# Main search loop
def call
until frontier.holds_all_syntax_errors?
@tick += 1
if frontier.expand?
expand_existing
else
create_blocks_from_untracked_lines
end
end
@invalid_blocks.concat(frontier.detect_invalid_blocks)
@invalid_blocks.sort_by! { |block| block.starts_at }
self
end
end
end
|