pry-doc 0.13.0pre3 → 0.13.0pre4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +4 -0
- data/lib/pry-doc/pry_ext/show_source_with_c_internals.rb +45 -26
- data/lib/pry-doc/pry_ext/show_source_with_c_internals/c_file.rb +40 -24
- data/lib/pry-doc/pry_ext/show_source_with_c_internals/code_fetcher.rb +108 -0
- data/lib/pry-doc/pry_ext/show_source_with_c_internals/symbol_extractor.rb +120 -0
- data/lib/pry-doc/version.rb +1 -1
- data/pry-doc.gemspec +1 -1
- data/spec/fixtures/c_source/goodbye.c +4 -0
- data/spec/fixtures/c_source/hello.c +20 -0
- data/spec/fixtures/c_source/tags +15 -0
- data/spec/pry-doc_spec.rb +115 -1
- metadata +9 -5
- data/lib/pry-doc/pry_ext/show_source_with_c_internals/c_extractor.rb +0 -195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd492dfe13c09cb7670fd0d7fda310b63f6f4aaf
|
4
|
+
data.tar.gz: 8e2f80d56db88200b35e49957323f65ff8923e7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09e41a9f8fbc1fb92152f444c943a7a1b70697b5d492e629d0f6f98b7e76563df345ac8937814aac5b84d48455d0832ebba2e12ca9a6aea999b9b45fa2ff2bc7'
|
7
|
+
data.tar.gz: 258d4bc55cfed74cbf1417ac54112f1570862c1389f50c7e24923324b3dd158409a126c73b363661f11228e14ff32b8a689ef598b64226060a8e9cd019171fc1
|
data/Rakefile
CHANGED
@@ -12,6 +12,10 @@ require 'latest_ruby'
|
|
12
12
|
require 'rake/clean'
|
13
13
|
require "#{direc}/lib/#{PROJECT_NAME}/version"
|
14
14
|
|
15
|
+
desc "generate fixture etags"
|
16
|
+
task :etags do
|
17
|
+
sh 'etags spec/fixtures/c_source/*.c -o spec/fixtures/c_source/tags'
|
18
|
+
end
|
15
19
|
desc "reinstall gem"
|
16
20
|
task :reinstall => :gems do
|
17
21
|
sh "gem uninstall pry-doc" rescue nil
|
@@ -1,34 +1,53 @@
|
|
1
|
-
require_relative "show_source_with_c_internals/
|
1
|
+
require_relative "show_source_with_c_internals/code_fetcher"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Pry::CInternals
|
4
|
+
class ShowSourceWithCInternals < Pry::Command::ShowSource
|
5
|
+
def options(opt)
|
6
|
+
super(opt)
|
7
|
+
opt.on :c, "c-source", "Show source of a C symbol in MRI"
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def show_c_source
|
11
|
+
if opts.present?(:all)
|
12
|
+
result, file = CodeFetcher.new(line_number_style).fetch_all_definitions(obj_name)
|
13
|
+
else
|
14
|
+
result, file = CodeFetcher.new(line_number_style).fetch_first_definition(obj_name)
|
15
|
+
end
|
16
|
+
if result
|
17
|
+
set_file_and_dir_locals(file)
|
18
|
+
_pry_.pager.page result
|
19
|
+
else
|
20
|
+
raise Pry::CommandError, no_definition_message
|
21
|
+
end
|
14
22
|
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
|
24
|
+
def process
|
25
|
+
if opts.present?(:c)
|
26
|
+
show_c_source
|
27
|
+
return
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
rescue Pry::CommandError
|
32
|
+
show_c_source
|
19
33
|
end
|
20
|
-
end
|
21
34
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
35
|
+
private
|
36
|
+
|
37
|
+
# We can number lines with their actual line numbers
|
38
|
+
# or starting with 1 (base-one)
|
39
|
+
def line_number_style
|
40
|
+
style = if opts.present?(:'base-one')
|
41
|
+
:'base-one'
|
42
|
+
elsif opts.present?(:'line-numbers')
|
43
|
+
:'line-numbers'
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
{ line_number_style: style }
|
28
49
|
end
|
29
|
-
rescue Pry::CommandError
|
30
|
-
extract_c_source
|
31
|
-
end
|
32
50
|
|
33
|
-
|
51
|
+
Pry::Commands.add_command(ShowSourceWithCInternals)
|
52
|
+
end
|
34
53
|
end
|
@@ -1,33 +1,49 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Data looks like:
|
2
|
+
#
|
3
|
+
# thread.c,11784
|
4
|
+
# static VALUE rb_cThreadShield;<\x7f>86,2497
|
5
|
+
# static VALUE sym_immediate;<\x7f>88,2529
|
6
|
+
# static VALUE sym_on_blocking;<\x7f>89,2557
|
3
7
|
|
4
|
-
|
8
|
+
# First line is the name of the file
|
9
|
+
# Following lines are the symbols followed by line number with char 127 as separator.
|
10
|
+
module Pry::CInternals
|
11
|
+
class CFile
|
12
|
+
SourceLocation = Struct.new(:file, :line, :original_symbol)
|
5
13
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
14
|
+
# Used to separate symbol from line number
|
15
|
+
SYMBOL_SEPARATOR = "\x7f"
|
9
16
|
|
10
|
-
|
11
|
-
@lines = str.lines
|
12
|
-
@file_name = @lines.shift.split(",").first
|
13
|
-
end
|
17
|
+
attr_accessor :symbols, :file_name
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
[cleanup_symbol(symbol),
|
19
|
-
[SourceLocation.new(@file_name, cleanup_linenumber(line_number), symbol.strip)]]
|
20
|
-
end.to_h
|
21
|
-
end
|
19
|
+
def self.from_str(str)
|
20
|
+
new(str).tap(&:process_symbols)
|
21
|
+
end
|
22
22
|
|
23
|
-
|
23
|
+
def initialize(str)
|
24
|
+
@lines = str.lines
|
25
|
+
@file_name = @lines.shift.split(",").first
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
def process_symbols
|
29
|
+
array = @lines.map do |v|
|
30
|
+
symbol, line_number = v.split(SYMBOL_SEPARATOR)
|
31
|
+
[cleanup_symbol(symbol),
|
32
|
+
[SourceLocation.new(@file_name, cleanup_linenumber(line_number), symbol.strip)]]
|
33
|
+
end
|
34
|
+
|
35
|
+
@symbols = Hash[array]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def cleanup_symbol(symbol)
|
41
|
+
symbol = symbol.split.last
|
42
|
+
symbol.chomp("(").chomp("*").chomp(";")
|
43
|
+
end
|
29
44
|
|
30
|
-
|
31
|
-
|
45
|
+
def cleanup_linenumber(line_number)
|
46
|
+
line_number.split.first.to_i
|
47
|
+
end
|
32
48
|
end
|
33
49
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative 'c_file'
|
3
|
+
require_relative 'symbol_extractor'
|
4
|
+
|
5
|
+
module Pry::CInternals
|
6
|
+
class CodeFetcher
|
7
|
+
include Pry::Helpers::Text
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :ruby_source_folder
|
11
|
+
end
|
12
|
+
|
13
|
+
# normalized
|
14
|
+
def self.ruby_version() RUBY_VERSION.tr(".", "_") end
|
15
|
+
|
16
|
+
self.ruby_source_folder = File.join(File.expand_path("~/.pry.d"), "ruby-#{ruby_version}")
|
17
|
+
|
18
|
+
attr_reader :line_number_style
|
19
|
+
attr_reader :symbol_extractor
|
20
|
+
|
21
|
+
def initialize(line_number_style: nil)
|
22
|
+
@line_number_style = line_number_style
|
23
|
+
@symbol_extractor = SymbolExtractor.new(self.class.ruby_source_folder)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_all_definitions(symbol)
|
27
|
+
infos = self.class.symbol_map[symbol]
|
28
|
+
return unless infos
|
29
|
+
|
30
|
+
"".tap do |result|
|
31
|
+
infos.count.times do |index|
|
32
|
+
result << fetch_first_definition(symbol, index).first << "\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
return [result, full_path_for(infos.first.file)]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_first_definition(symbol, index=nil)
|
40
|
+
infos = self.class.symbol_map[symbol]
|
41
|
+
return unless infos
|
42
|
+
|
43
|
+
info = infos[index || 0]
|
44
|
+
code = symbol_extractor.extract(info)
|
45
|
+
|
46
|
+
"".tap do |result|
|
47
|
+
result << "\n#{bold('From: ')}#{info.file} @ line #{info.line}:\n"
|
48
|
+
result << "#{bold('Number of implementations:')} #{infos.count}\n" unless index
|
49
|
+
result << "#{bold('Number of lines: ')} #{code.lines.count}\n\n"
|
50
|
+
result << Pry::Code.new(code, start_line_for(info.line), :c).
|
51
|
+
with_line_numbers(use_line_numbers?).highlighted
|
52
|
+
|
53
|
+
return [result, full_path_for(info.file)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def full_path_for(file)
|
60
|
+
File.join(self.class.ruby_source_folder, file)
|
61
|
+
end
|
62
|
+
|
63
|
+
def use_line_numbers?
|
64
|
+
!!line_number_style
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_line_for(line)
|
68
|
+
if line_number_style == :'base-one'
|
69
|
+
1
|
70
|
+
else
|
71
|
+
line || 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.symbol_map
|
76
|
+
parse_tagfile
|
77
|
+
@symbol_map ||= @c_files.each_with_object({}) do |v, h|
|
78
|
+
h.merge!(v.symbols) { |k, old_val, new_val| old_val + new_val }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.parse_tagfile
|
83
|
+
@c_files ||= tagfile.split("\f\n")[1..-1].map do |v|
|
84
|
+
CFile.from_str(v)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.tagfile
|
89
|
+
install_and_setup_ruby_source unless File.directory?(ruby_source_folder)
|
90
|
+
|
91
|
+
@tagfile ||= File.read(File.join(ruby_source_folder, "tags"))
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.install_and_setup_ruby_source
|
95
|
+
puts "Downloading and setting up Ruby #{ruby_version} source..."
|
96
|
+
FileUtils.mkdir_p(ruby_source_folder)
|
97
|
+
FileUtils.cd(File.dirname(ruby_source_folder)) do
|
98
|
+
%x{ curl -L https://github.com/ruby/ruby/archive/v#{ruby_version}.tar.gz | tar xzvf - > /dev/null 2>&1 }
|
99
|
+
end
|
100
|
+
|
101
|
+
FileUtils.cd(ruby_source_folder) do
|
102
|
+
puts "Generating tagfile!"
|
103
|
+
%x{ find . -type f -name "*.[chy]" | etags - -o tags }
|
104
|
+
end
|
105
|
+
puts "...Finished!"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Pry::CInternals
|
2
|
+
class SymbolExtractor
|
3
|
+
class << self
|
4
|
+
attr_accessor :file_cache
|
5
|
+
end
|
6
|
+
@file_cache = {}
|
7
|
+
|
8
|
+
def initialize(ruby_source_folder)
|
9
|
+
@ruby_source_folder = ruby_source_folder
|
10
|
+
end
|
11
|
+
|
12
|
+
def extract(info)
|
13
|
+
if info.original_symbol.start_with?("#define")
|
14
|
+
extract_macro(info)
|
15
|
+
elsif info.original_symbol =~ /\s*(struct|enum)\s*/
|
16
|
+
extract_struct(info)
|
17
|
+
elsif info.original_symbol.start_with?("}")
|
18
|
+
extract_typedef_struct(info)
|
19
|
+
elsif info.original_symbol =~/^typedef.*;$/
|
20
|
+
extract_typedef_oneliner(info)
|
21
|
+
else
|
22
|
+
extract_function(info)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def extract_macro(info)
|
29
|
+
extract_code(info) do |code|
|
30
|
+
return code unless code.lines.last.strip.end_with?('\\')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_struct(info)
|
35
|
+
extract_code(info) do |code|
|
36
|
+
return code if balanced?(code)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_typedef_struct(info)
|
41
|
+
extract_code(info) do |code, direction: :reverse|
|
42
|
+
return code if balanced?(code)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract_typedef_oneliner(info)
|
47
|
+
source_file = source_from_file(info.file)
|
48
|
+
return source_file[info.line]
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_function(info)
|
52
|
+
source_file = source_from_file(info.file)
|
53
|
+
offset = 1
|
54
|
+
start_line = info.line
|
55
|
+
|
56
|
+
if !complete_function_signature?(source_file[info.line]) && function_return_type?(source_file[info.line - 1])
|
57
|
+
start_line = info.line - 1
|
58
|
+
offset += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
if !source_file[info.line].strip.end_with?("{")
|
62
|
+
offset += 1
|
63
|
+
end
|
64
|
+
|
65
|
+
extract_code(info, offset: offset, start_line: start_line) do |code|
|
66
|
+
return code if balanced?(code)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def extract_code(info, offset: 1, start_line: info.line, direction: :forward, &block)
|
71
|
+
source_file = source_from_file(info.file)
|
72
|
+
|
73
|
+
loop do
|
74
|
+
code = if direction == :reverse
|
75
|
+
source_file[start_line - offset..info.line].join
|
76
|
+
else
|
77
|
+
source_file[start_line, offset].join
|
78
|
+
end
|
79
|
+
yield code
|
80
|
+
offset += 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def complete_function_signature?(str)
|
85
|
+
str =~ /\w+\s*\*?\s+\w+\(/
|
86
|
+
end
|
87
|
+
|
88
|
+
def function_return_type?(str)
|
89
|
+
str.strip =~ /[\w\*]$/
|
90
|
+
end
|
91
|
+
|
92
|
+
def balanced?(str)
|
93
|
+
tokens = CodeRay.scan(str, :c).tokens.each_slice(2)
|
94
|
+
token_count(tokens, /{/) == token_count(tokens, /}/)
|
95
|
+
end
|
96
|
+
|
97
|
+
def token_count(tokens, token)
|
98
|
+
tokens.count { |v| v.first =~ token && v.last == :operator }
|
99
|
+
end
|
100
|
+
|
101
|
+
def source_from_file(file)
|
102
|
+
file_cache = self.class.file_cache
|
103
|
+
if file_cache.key?(file)
|
104
|
+
file_cache[file]
|
105
|
+
else
|
106
|
+
# inject a "\n" as first element to align array index and line number
|
107
|
+
file_cache[file] = ["\n", *File.read(full_path_for(file)).lines]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def full_path_for(file)
|
112
|
+
File.join(@ruby_source_folder, file)
|
113
|
+
end
|
114
|
+
|
115
|
+
# normalized
|
116
|
+
def ruby_version
|
117
|
+
RUBY_VERSION.tr(".", "_")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/pry-doc/version.rb
CHANGED
data/pry-doc.gemspec
CHANGED
@@ -22,7 +22,7 @@ DESCR
|
|
22
22
|
|
23
23
|
s.required_ruby_version = '>= 2.0'
|
24
24
|
|
25
|
-
s.add_dependency 'yard', "~> 0.9"
|
25
|
+
s.add_dependency 'yard', "~> 0.9.11"
|
26
26
|
s.add_dependency 'pry', ">= 0.11.3"
|
27
27
|
s.add_development_dependency 'latest_ruby', '~> 0.0'
|
28
28
|
s.add_development_dependency 'rspec', '~> 3.5'
|
data/spec/pry-doc_spec.rb
CHANGED
@@ -12,6 +12,121 @@ puts "Testing pry-doc version #{PryDoc::VERSION}..."
|
|
12
12
|
puts "Ruby version: #{RUBY_VERSION}"
|
13
13
|
|
14
14
|
RSpec.describe PryDoc do
|
15
|
+
describe Pry::CInternals::CodeFetcher do
|
16
|
+
def decolor(str)
|
17
|
+
Pry::Helpers::Text.strip_color(str)
|
18
|
+
end
|
19
|
+
|
20
|
+
before(:all) do
|
21
|
+
described_class.ruby_source_folder = File.join(File.dirname(__FILE__), "fixtures/c_source")
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ".symbol_map" do
|
25
|
+
it "generates the map with the correct symbols" do
|
26
|
+
expect(described_class.symbol_map).to have_key("foo")
|
27
|
+
expect(described_class.symbol_map).to have_key("baby")
|
28
|
+
expect(described_class.symbol_map).to have_key("wassup")
|
29
|
+
expect(described_class.symbol_map).to have_key("bar")
|
30
|
+
expect(described_class.symbol_map).to have_key("baz")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with line numbers" do
|
35
|
+
context "normal style (actual line numbers)" do
|
36
|
+
it "displays actual line numbers" do
|
37
|
+
code, = described_class.new(line_number_style: :'line-numbers').fetch_first_definition("bar")
|
38
|
+
expect(decolor code).to include <<EOF
|
39
|
+
11: enum bar {
|
40
|
+
12: alpha,
|
41
|
+
13: beta,
|
42
|
+
14: gamma
|
43
|
+
15: };
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
context "base one style (line numbers start with 1)" do
|
48
|
+
it "displays actual line numbers" do
|
49
|
+
code, = described_class.new(line_number_style: :'base-one').fetch_first_definition("bar")
|
50
|
+
expect(decolor code).to include <<EOF
|
51
|
+
1: enum bar {
|
52
|
+
2: alpha,
|
53
|
+
3: beta,
|
54
|
+
4: gamma
|
55
|
+
5: };
|
56
|
+
EOF
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#fetch_all_definitions" do
|
63
|
+
it "returns the code for all symbols" do
|
64
|
+
code, = described_class.new(line_number_style: nil).fetch_all_definitions("foo")
|
65
|
+
expect(decolor code).to include <<EOF
|
66
|
+
int
|
67
|
+
foo(void) {
|
68
|
+
}
|
69
|
+
EOF
|
70
|
+
|
71
|
+
expect(decolor code).to include <<EOF
|
72
|
+
char
|
73
|
+
foo(int*) {
|
74
|
+
return 'a';
|
75
|
+
}
|
76
|
+
EOF
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#fetch_first_definition" do
|
81
|
+
it "returns the code for a function" do
|
82
|
+
code, = described_class.new(line_number_style: nil).fetch_first_definition("foo")
|
83
|
+
expect(decolor code).to include(<<EOF
|
84
|
+
int
|
85
|
+
foo(void) {
|
86
|
+
}
|
87
|
+
EOF
|
88
|
+
).or include <<EOF
|
89
|
+
char
|
90
|
+
foo(int*) {
|
91
|
+
return 'a';
|
92
|
+
}
|
93
|
+
EOF
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns the code for an enum" do
|
97
|
+
code, = described_class.new(line_number_style: nil).fetch_first_definition("bar")
|
98
|
+
expect(decolor code).to include <<EOF
|
99
|
+
enum bar {
|
100
|
+
alpha,
|
101
|
+
beta,
|
102
|
+
gamma
|
103
|
+
};
|
104
|
+
EOF
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns the code for a macro" do
|
108
|
+
code, = described_class.new(line_number_style: nil).fetch_first_definition("baby")
|
109
|
+
expect(decolor code).to include('#define baby do {')
|
110
|
+
expect(decolor code).to include('printf("baby");')
|
111
|
+
expect(decolor code).to include('while(0)')
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns the code for a typedef" do
|
115
|
+
code, = described_class.new(line_number_style: nil).fetch_first_definition("wassup")
|
116
|
+
expect(decolor code).to include('typedef int wassup;')
|
117
|
+
end
|
118
|
+
|
119
|
+
it "returns the code for a struct" do
|
120
|
+
code, = described_class.new(line_number_style: nil).fetch_first_definition("baz")
|
121
|
+
expect(decolor code).to include <<EOF
|
122
|
+
struct baz {
|
123
|
+
int x;
|
124
|
+
int y;
|
125
|
+
};
|
126
|
+
EOF
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
15
130
|
|
16
131
|
describe "core C methods" do
|
17
132
|
it 'should look up core (C) methods' do
|
@@ -193,5 +308,4 @@ RSpec.describe PryDoc do
|
|
193
308
|
end
|
194
309
|
end
|
195
310
|
end
|
196
|
-
|
197
311
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pry-doc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.13.
|
4
|
+
version: 0.13.0pre4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Mair (banisterfiend)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-02-
|
11
|
+
date: 2018-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.9.11
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.9.11
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pry
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,10 +129,14 @@ files:
|
|
129
129
|
- lib/pry-doc/docs/25/proxy_types
|
130
130
|
- lib/pry-doc/pry_ext/method_info.rb
|
131
131
|
- lib/pry-doc/pry_ext/show_source_with_c_internals.rb
|
132
|
-
- lib/pry-doc/pry_ext/show_source_with_c_internals/c_extractor.rb
|
133
132
|
- lib/pry-doc/pry_ext/show_source_with_c_internals/c_file.rb
|
133
|
+
- lib/pry-doc/pry_ext/show_source_with_c_internals/code_fetcher.rb
|
134
|
+
- lib/pry-doc/pry_ext/show_source_with_c_internals/symbol_extractor.rb
|
134
135
|
- lib/pry-doc/version.rb
|
135
136
|
- pry-doc.gemspec
|
137
|
+
- spec/fixtures/c_source/goodbye.c
|
138
|
+
- spec/fixtures/c_source/hello.c
|
139
|
+
- spec/fixtures/c_source/tags
|
136
140
|
- spec/gem_with_cext/gems/ext/extconf.rb
|
137
141
|
- spec/gem_with_cext/gems/ext/sample.c
|
138
142
|
- spec/gem_with_cext/gems/lib/sample.rb
|
@@ -1,195 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require_relative 'c_file'
|
3
|
-
|
4
|
-
class CExtractor
|
5
|
-
include Pry::Helpers::Text
|
6
|
-
|
7
|
-
class << self
|
8
|
-
attr_accessor :file_cache
|
9
|
-
end
|
10
|
-
@file_cache = {}
|
11
|
-
|
12
|
-
attr_reader :opts
|
13
|
-
|
14
|
-
def initialize(opts)
|
15
|
-
@opts = opts
|
16
|
-
end
|
17
|
-
|
18
|
-
def balanced?(str)
|
19
|
-
tokens = CodeRay.scan(str, :c).tokens.each_slice(2).to_a
|
20
|
-
tokens.count { |v|
|
21
|
-
v.first =~ /{/ && v.last == :operator } == tokens.count { |v|
|
22
|
-
v.first =~ /}/ && v.last == :operator
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
def extract_struct(info)
|
27
|
-
source_file = source_from_file(info.file)
|
28
|
-
offset = 1
|
29
|
-
loop do
|
30
|
-
code = source_file[info.line, offset].join
|
31
|
-
break code if balanced?(code)
|
32
|
-
offset += 1
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def extract_typedef_struct(info)
|
37
|
-
source_file = source_from_file(info.file)
|
38
|
-
offset = 1
|
39
|
-
loop do
|
40
|
-
code = source_file[info.line - offset..info.line].join
|
41
|
-
break code if balanced?(code)
|
42
|
-
offset += 1
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def extract_macro(info)
|
47
|
-
source_file = source_from_file(info.file)
|
48
|
-
offset = 1
|
49
|
-
loop do
|
50
|
-
code = source_file[info.line, offset].join
|
51
|
-
break code unless source_file[info.line + offset - 1].strip.end_with?('\\')
|
52
|
-
offset += 1
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def extract_typedef_oneliner(info)
|
57
|
-
source_file = source_from_file(info.file)
|
58
|
-
return source_file[info.line]
|
59
|
-
end
|
60
|
-
|
61
|
-
def extract_function(info)
|
62
|
-
source_file = source_from_file(info.file)
|
63
|
-
offset = 1
|
64
|
-
|
65
|
-
if source_file[info.line] !~ /\w+\s+\w\(/ && source_file[info.line - 1].strip =~ /[\w\*]$/
|
66
|
-
start_line = info.line - 1
|
67
|
-
offset += 1
|
68
|
-
else
|
69
|
-
start_line = info.line
|
70
|
-
end
|
71
|
-
|
72
|
-
if !source_file[info.line].strip.end_with?("{")
|
73
|
-
offset += 1
|
74
|
-
end
|
75
|
-
|
76
|
-
loop do
|
77
|
-
code = source_file[start_line, offset].join
|
78
|
-
break code if balanced?(code)
|
79
|
-
offset += 1
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def file_cache
|
84
|
-
self.class.file_cache
|
85
|
-
end
|
86
|
-
|
87
|
-
def file_cache=(v)
|
88
|
-
self.class.file_cache = v
|
89
|
-
end
|
90
|
-
|
91
|
-
def full_path_for(file)
|
92
|
-
File.join(File.expand_path("~/.pry.d/ruby-#{ruby_version}"), file)
|
93
|
-
end
|
94
|
-
|
95
|
-
def source_from_file(file)
|
96
|
-
if file_cache.key?(file)
|
97
|
-
file_cache[file]
|
98
|
-
else
|
99
|
-
file_cache[file] = File.read(full_path_for(file)).lines
|
100
|
-
file_cache[file].unshift("\n")
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def use_line_numbers?
|
105
|
-
opts.present?(:b) || opts.present?(:l)
|
106
|
-
end
|
107
|
-
|
108
|
-
def show_all_definitions(x)
|
109
|
-
infos = self.class.symbol_map[x]
|
110
|
-
return unless infos
|
111
|
-
|
112
|
-
result = ""
|
113
|
-
infos.count.times do |index|
|
114
|
-
result << show_first_definition(x, index) << "\n"
|
115
|
-
end
|
116
|
-
result
|
117
|
-
end
|
118
|
-
|
119
|
-
def show_first_definition(x, index=nil)
|
120
|
-
infos = self.class.symbol_map[x]
|
121
|
-
return unless infos
|
122
|
-
|
123
|
-
count = infos.count
|
124
|
-
info = infos[index || 0]
|
125
|
-
code = if info.original_symbol.start_with?("#define")
|
126
|
-
extract_macro(info)
|
127
|
-
elsif info.original_symbol =~ /\s*struct\s*/ || info.original_symbol.start_with?("enum")
|
128
|
-
extract_struct(info)
|
129
|
-
elsif info.original_symbol.start_with?("}")
|
130
|
-
extract_typedef_struct(info)
|
131
|
-
elsif info.original_symbol =~/^typedef.*;$/
|
132
|
-
extract_typedef_oneliner(info)
|
133
|
-
else
|
134
|
-
extract_function(info)
|
135
|
-
end
|
136
|
-
|
137
|
-
h = "\n#{bold('From: ')}#{info.file} @ line #{info.line}:\n"
|
138
|
-
h << "#{bold('Number of implementations:')} #{count}\n" unless index
|
139
|
-
h << "#{bold('Number of lines: ')} #{code.lines.count}\n\n"
|
140
|
-
h << Pry::Code.new(code, start_line_for(info.line), :c).
|
141
|
-
with_line_numbers(use_line_numbers?).highlighted
|
142
|
-
end
|
143
|
-
|
144
|
-
def start_line_for(line)
|
145
|
-
if opts.present?(:'base-one')
|
146
|
-
1
|
147
|
-
else
|
148
|
-
line || 1
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def self.install_and_setup_ruby_source
|
153
|
-
puts "Downloading and setting up Ruby #{ruby_version} source..."
|
154
|
-
FileUtils.mkdir_p(File.expand_path("~/.pry.d/"))
|
155
|
-
FileUtils.cd(File.expand_path("~/.pry.d")) do
|
156
|
-
%x{ curl -L https://github.com/ruby/ruby/archive/v#{ruby_version}.tar.gz | tar xzvf - > /dev/null 2>&1 }
|
157
|
-
end
|
158
|
-
|
159
|
-
FileUtils.cd(File.expand_path("~/.pry.d/ruby-#{ruby_version}")) do
|
160
|
-
puts "Generating tagfile!"
|
161
|
-
%x{ find . -type f -name "*.[chy]" | etags - -o tags }
|
162
|
-
end
|
163
|
-
puts "...Finished!"
|
164
|
-
end
|
165
|
-
|
166
|
-
def self.tagfile
|
167
|
-
if !File.directory?(File.expand_path("~/.pry.d/ruby-#{ruby_version}"))
|
168
|
-
install_and_setup_ruby_source
|
169
|
-
end
|
170
|
-
|
171
|
-
@tagfile ||= File.read(File.expand_path("~/.pry.d/ruby-#{ruby_version}/tags"))
|
172
|
-
end
|
173
|
-
|
174
|
-
def ruby_version
|
175
|
-
self.class.ruby_version
|
176
|
-
end
|
177
|
-
|
178
|
-
# normalized
|
179
|
-
def self.ruby_version
|
180
|
-
RUBY_VERSION.tr(".", "_")
|
181
|
-
end
|
182
|
-
|
183
|
-
def self.parse_tagfile
|
184
|
-
@c_files ||= tagfile.split("\f\n")[1..-1].map do |v|
|
185
|
-
CFile.from_str(v)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def self.symbol_map
|
190
|
-
parse_tagfile
|
191
|
-
@symbol_map ||= @c_files.each_with_object({}) do |v, h|
|
192
|
-
h.merge!(v.symbols) { |k, old_val, new_val| old_val + new_val }
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|