pry-doc 0.13.0pre3 → 0.13.0pre4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7e74381e247695c408882563c53406aeb996c24
4
- data.tar.gz: fbf9f1366c81d8cc86b4f9c6f1179715001ee38d
3
+ metadata.gz: fd492dfe13c09cb7670fd0d7fda310b63f6f4aaf
4
+ data.tar.gz: 8e2f80d56db88200b35e49957323f65ff8923e7b
5
5
  SHA512:
6
- metadata.gz: b03fddd7aa4460fef37a96e5d3087df1daa9c2421067469a38c2c27e2dad3447b20d11d9456d8e290e5774cae9acb33e291a330ffb455de077ecf3b6a1d8e909
7
- data.tar.gz: 8a1ddc498eb8f3948bbcf90c2c106c32c6e137ca1bb95a3d4b3adf3336dd98a92ce4c8cfebbce7bfbdb2b5abf67d2f4afb5e6dc2da85dd4834772bb7db7afa09
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/c_extractor"
1
+ require_relative "show_source_with_c_internals/code_fetcher"
2
2
 
3
- class ShowSourceWithCInternals < Pry::Command::ShowSource
4
- def options(opt)
5
- super(opt)
6
- opt.on :c, "c-source", "Show source of a C symbol in MRI"
7
- end
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
- def extract_c_source
10
- if opts.present?(:all)
11
- result = CExtractor.new(opts).show_all_definitions(obj_name)
12
- else
13
- result = CExtractor.new(opts).show_first_definition(obj_name)
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
- if result
16
- _pry_.pager.page result
17
- else
18
- raise Pry::CommandError, no_definition_message
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
- def process
23
- if opts.present?(:c)
24
- extract_c_source
25
- return
26
- else
27
- super
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
- Pry::Commands.add_command(ShowSourceWithCInternals)
51
+ Pry::Commands.add_command(ShowSourceWithCInternals)
52
+ end
34
53
  end
@@ -1,33 +1,49 @@
1
- class CFile
2
- SourceLocation = Struct.new(:file, :line, :original_symbol)
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
- attr_accessor :symbols, :file_name
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
- def self.from_str(str)
7
- new(str).tap(&:process_symbols)
8
- end
14
+ # Used to separate symbol from line number
15
+ SYMBOL_SEPARATOR = "\x7f"
9
16
 
10
- def initialize(str)
11
- @lines = str.lines
12
- @file_name = @lines.shift.split(",").first
13
- end
17
+ attr_accessor :symbols, :file_name
14
18
 
15
- def process_symbols
16
- @symbols = @lines.map do |v|
17
- symbol, line_number = v.split("\x7f")
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
- private
23
+ def initialize(str)
24
+ @lines = str.lines
25
+ @file_name = @lines.shift.split(",").first
26
+ end
24
27
 
25
- def cleanup_symbol(symbol)
26
- symbol = symbol.split.last
27
- symbol.chomp("(").chomp("*").chomp(";")
28
- end
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
- def cleanup_linenumber(line_number)
31
- line_number.split.first.to_i
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
@@ -1,3 +1,3 @@
1
1
  module PryDoc
2
- VERSION = '0.13.0pre3'
2
+ VERSION = '0.13.0pre4'
3
3
  end
@@ -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'
@@ -0,0 +1,4 @@
1
+ char
2
+ foo(int*) {
3
+ return 'a';
4
+ }
@@ -0,0 +1,20 @@
1
+ int
2
+ foo(void) {
3
+ }
4
+
5
+ #define baby do { \
6
+ printf("baby"); \
7
+ } while(0)
8
+
9
+ typedef int wassup;
10
+
11
+ enum bar {
12
+ alpha,
13
+ beta,
14
+ gamma
15
+ };
16
+
17
+ struct baz {
18
+ int x;
19
+ int y;
20
+ };
@@ -0,0 +1,15 @@
1
+
2
+ goodbye.c,9
3
+ foo(2,5
4
+
5
+ hello.c,166
6
+ foo(2,4
7
+ #define baby 5,19
8
+ typedef int wassup;9,73
9
+ enum bar 11,94
10
+ alpha,12,105
11
+ beta,13,114
12
+ gamma14,122
13
+ struct baz 17,134
14
+ int x;18,147
15
+ int y;19,156
@@ -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.0pre3
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-06 00:00:00.000000000 Z
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: '0.9'
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: '0.9'
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