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 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