Top Level Namespace

Constant Summary collapse

ZIP_FILENAME =
::File.join ::TreeStand.config.parser_path, "temp.zip"

Instance Method Summary collapse

Instance Method Details

#ContextGrep(what, where = ".") ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/context_grep.rb', line 9

def ContextGrep what, where = "."

  `#{system("rg --version >/dev/null 2>&1") ? "rg -n" : "grep -nrI"} #{::Shellwords.shelljoin [what, where]} 2>/dev/null`
  .scan(/^([^:]+):(\d+):/).group_by(&:first).map do |file, group|
    lang = ::Linguist::FileBlob.new(file).language
    next ::STDERR.puts "unsupported grammar #{lang} at #{file}" unless ts_name = {
        "Bash" => "bash",
        "C" => "c",
        "C#" => "c_sharp",
        "COBOL" => "cobol",
        "Groovy" => "groovy",
        "Haml" => "haml",
        "HTML" => "html",
        "Java" => "java",
        "JavaScript" => "javascript",
        "JSON" => "json",
        "Pascal" => "pascal",
        "PHP" => "php",
        "Python" => "python",
        "Ruby" => "ruby",
        "Rust" => "rust",
        "XML" => "xml",
    }[lang.name]
    src = ::File.read file
    stack = [
      begin
        ::TreeStand::Parser.new ts_name
      rescue ::TreeSitter::ParserNotFoundError
        ::FileUtils.mkdir_p ::TreeStand.config.parser_path
        require "etc"
        require "open-uri"
        "https://github.com/Faveod/tree-sitter-parsers/releases/download/v5.0/tree-sitter-parsers-5.0-#{
          "Darwin" == ::Etc.uname[:sysname] ? "macos" : "linux"
        }-#{
          ::RbConfig::CONFIG["host_cpu"] =~ /x86_64|amd64/ ? "x64" : "arm64"
        }.zip".tap do |url|
            ::STDERR.puts "downloading #{url} to #{::TreeStand.config.parser_path}"
          ::File.binwrite ZIP_FILENAME, ::URI.open(url, &:read)
        end unless File.exist? ZIP_FILENAME
        require "zip"
        lambda do
          ::Zip::File.open(ZIP_FILENAME) do |zip|
          for entry in zip
            next if entry.directory?
            target_path = ::File.join ::TreeStand.config.parser_path, ::File.basename(entry.name)
              next if ::File.exists? target_path
            entry.extract target_path
              begin
                return ::TreeStand::Parser.new ts_name
              rescue ::TreeSitter::ParserNotFoundError
                ::FileUtils.rm_f target_path
              end
          end
          end
          nil
        end.call or next ::STDERR.puts "can't find grammar library for #{lang} at #{file}"
      end.parse_string(src).root_node
    ]
    nodes = {}
    for node in stack
      stack.concat node.children.each{ |_| nodes[_] = node }
    end
    [
      file,
      group.flat_map do |_, grep_lineno|
        node = nodes.keys
        .select{ |_| _.ts_node.start_point.row <= grep_lineno.to_i - 1 && grep_lineno.to_i - 1 <= _.ts_node.end_point.row }
        .max_by{ |_| _.ts_node.start_byte } || fail
        [].tap do |lines|
          begin
            if ::ENV["COGREP_DEBUGTYPES"]
              s = node.ts_node.start_point.row + 1
              e = node.ts_node.end_point.row + 1
              ::STDERR.puts "#{::File.basename file} #{node.type.inspect} #{s}-#{e}: #{src.lines[s - 1]}"
            end
            lines.push node.ts_node.start_point.row unless %i{ program body_statement translation_unit }.include? node.type
          end while node = nodes[node]
        end
      end.uniq.sort.map{ |_| [_ + 1, src.lines[_]] }
    ]
  end.compact

ensure
  ::FileUtils.rm_f ZIP_FILENAME
end