/
whereami.rb
205 lines (163 loc) · 5.57 KB
/
whereami.rb
1
2
3
4
5
6
7
8
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# frozen_string_literal: true
require 'method_source'
class Pry
class Command
class Whereami < Pry::ClassCommand
def initialize(*)
super
@method_code = nil
end
class << self
attr_accessor :method_size_cutoff
end
@method_size_cutoff = 30
match 'whereami'
description 'Show code surrounding the current context.'
group 'Context'
banner <<-'BANNER'
Usage: whereami [-qn] [LINES]
Describe the current location. If you use `binding.pry` inside a method then
whereami will print out the source for that method.
If a number is passed, then LINES lines before and after the current line will be
shown instead of the method itself.
The `-q` flag can be used to suppress error messages in the case that there's
no code to show. This is used by pry in the default before_session hook to show
you when you arrive at a `binding.pry`.
The `-n` flag can be used to hide line numbers so that code can be copy/pasted
effectively.
When pry was started on an Object and there is no associated method, whereami
will instead output a brief description of the current object.
BANNER
def setup
if target.respond_to?(:source_location)
file, @line = target.source_location
@file = expand_path(file)
else
@file = expand_path(target.eval('__FILE__'))
@line = target.eval('__LINE__')
end
@method = Pry::Method.from_binding(target)
end
def options(opt)
opt.on :q, :quiet, "Don't display anything in case of an error"
opt.on :n, :"no-line-numbers", "Do not display line numbers"
opt.on :m, :method, "Show the complete source for the current method."
opt.on :c, :class, "Show the complete source for the current class or module."
opt.on :f, :file, "Show the complete source for the current file."
end
def code
@code ||= if opts.present?(:m)
method_code || raise(CommandError, "Cannot find method code.")
elsif opts.present?(:c)
class_code || raise(CommandError, "Cannot find class code.")
elsif opts.present?(:f)
Pry::Code.from_file(@file)
elsif args.any?
code_window
else
default_code
end
end
def code?
!!code
rescue MethodSource::SourceNotFoundError
false
end
def bad_option_combination?
[opts.present?(:m), opts.present?(:f),
opts.present?(:c), args.any?].count(true) > 1
end
def location
"#{@file}:#{@line} #{@method && @method.name_with_owner}"
end
def process
if bad_option_combination?
raise CommandError, "Only one of -m, -c, -f, and LINES may be specified."
end
return if nothing_to_do?
if internal_binding?(target)
handle_internal_binding
return
end
set_file_and_dir_locals(@file)
pretty_code = code.with_line_numbers(use_line_numbers?)
.with_marker(marker)
.highlighted
pry_instance.pager.page(
"\n#{bold('From:')} #{location}:\n\n" + pretty_code + "\n"
)
end
private
def nothing_to_do?
opts.quiet? && (internal_binding?(target) || !code?)
end
def use_line_numbers?
!opts.present?(:n)
end
def marker
!opts.present?(:n) && @line
end
def top_level?
target_self == Pry.main
end
def handle_internal_binding
if top_level?
output.puts "At the top level."
else
output.puts "Inside #{Pry.view_clip(target_self)}."
end
end
def small_method?
@method.source_range.count < self.class.method_size_cutoff
end
def default_code
if method_code && small_method?
method_code
else
code_window
end
end
def code_window
Pry::Code.from_file(@file).around(@line, window_size)
end
def method_code
return @method_code if @method_code
@method_code = Pry::Code.from_method(@method) if valid_method?
end
# This either returns the `target_self`
# or it returns the class of `target_self` if `target_self` is not a class.
# @return [Pry::WrappedModule]
def target_class
return Pry::WrappedModule(target_self) if target_self.is_a?(Module)
Pry::WrappedModule(target_self.class)
end
def class_code
@class_code ||=
begin
mod = @method ? Pry::WrappedModule(@method.owner) : target_class
idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file }
idx && Pry::Code.from_module(mod, idx)
end
end
def valid_method?
@method && @method.source? && expand_path(@method.source_file) == @file &&
@method.source_range.include?(@line)
end
def expand_path(filename)
return unless filename
return filename if Pry.eval_path == filename
File.expand_path(filename)
end
def window_size
if args.empty?
pry_instance.config.default_window_size
else
args.first.to_i
end
end
end
Pry::Commands.add_command(Pry::Command::Whereami)
Pry::Commands.alias_command '@', 'whereami'
Pry::Commands.alias_command(/whereami[!?]+/, 'whereami')
end
end