Class: Pry::Code

Inherits:
Object show all
Extended by:
MethodSource::CodeHelpers
Defined in:
lib/pry/code.rb

Overview

Pry::Code is a class that encapsulates lines of source code and their line numbers and formats them for terminal output. It can read from a file or method definition or be instantiated with a String or an Array.

In general, the formatting methods in Code return a new Code object which will format the text as specified when #to_s is called. This allows arbitrary chaining of formatting methods without mutating the original object.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lines = [], start_line = 1, code_type = :ruby) ⇒ Code

Instantiate a Code object containing code from the given Array, String, or IO. The first line will be line 1 unless specified otherwise. If you need non-contiguous line numbers, you can create an empty Code object and then use #push to insert the lines.

Parameters:

  • lines (Array<String>, String, IO) (defaults to: [])
  • start_line (Fixnum?) (defaults to: 1)
  • code_type (Symbol?) (defaults to: :ruby)


126
127
128
129
130
131
132
133
# File 'lib/pry/code.rb', line 126

def initialize(lines=[], start_line=1, code_type=:ruby)
  if lines.is_a? String
    lines = lines.lines
  end

  @lines = lines.each_with_index.map { |l, i| [l.chomp, i + start_line.to_i] }
  @code_type = code_type
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object

Forward any missing methods to the output of #to_s.



390
391
392
# File 'lib/pry/code.rb', line 390

def method_missing(name, *args, &blk)
  to_s.send(name, *args, &blk)
end

Instance Attribute Details

#code_typeSymbol

Returns The type of code stored in this wrapper.

Returns:

  • (Symbol)

    The type of code stored in this wrapper.



116
117
118
# File 'lib/pry/code.rb', line 116

def code_type
  @code_type
end

Class Method Details

.from_file(fn, code_type = nil) ⇒ Code

Instantiate a Code object containing code loaded from a file or Pry's line buffer.

Parameters:

  • fn (String)

    The name of a file, or "(pry)".

  • code_type (Symbol) (defaults to: nil)

    The type of code the file contains.

Returns:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/pry/code.rb', line 38

def from_file(fn, code_type = nil)
  if fn == Pry.eval_path
    f = Pry.line_buffer.drop(1)
  else
    if File.readable?(fn)
      f = File.open(fn, 'r')
      code_type = type_from_filename(fn)
    else
      raise MethodSource::SourceNotFoundError, "Cannot open #{fn.inspect} for reading."
    end
  end
  new(f, 1, code_type || :ruby)
ensure
  f.close if f.respond_to?(:close)
end

.from_method(meth, start_line = nil) ⇒ Code

Instantiate a Code object containing code extracted from a ::Method, UnboundMethod, Proc, or Pry::Method object.

Parameters:

  • meth (::Method, UnboundMethod, Proc, Pry::Method)

    The method object.

  • start_line (Fixnum, nil) (defaults to: nil)

    The line number to start on, or nil to use the method's original line numbers.

Returns:



62
63
64
65
66
# File 'lib/pry/code.rb', line 62

def from_method(meth, start_line=nil)
  meth = Pry::Method(meth)
  start_line ||= meth.source_line || 1
  new(meth.source, start_line, meth.source_type)
end

.from_module(mod, start_line = nil, candidate_rank = 0) ⇒ Code

Attempt to extract the source code for module (or class) mod.

Parameters:

  • mod (Module, Class)

    The module (or class) of interest.

  • start_line (Fixnum, nil) (defaults to: nil)

    The line number to start on, or nil to use the method's original line numbers.

  • candidate_rank (Fixnum) (defaults to: 0)

    The module candidate (by rank) to use (see Pry::WrappedModule::Candidate for more information).

Returns:



76
77
78
79
80
81
# File 'lib/pry/code.rb', line 76

def from_module(mod, start_line=nil, candidate_rank=0)
  candidate = Pry::WrappedModule(mod).candidate(candidate_rank)

  start_line ||= candidate.line
  new(candidate.source, start_line, :ruby)
end

Instance Method Details

#==(other) ⇒ Boolean

Two Code objects are equal if they contain the same lines with the same numbers. Otherwise, call to_s and chomp and compare as Strings.

Parameters:

Returns:

  • (Boolean)


378
379
380
381
382
383
384
385
386
387
# File 'lib/pry/code.rb', line 378

def ==(other)
  if other.is_a?(Code)
    @other_lines = other.instance_variable_get(:@lines)
    @lines.each_with_index.all? do |(l, ln), i|
      l == @other_lines[i].first && ln == @other_lines[i].last
    end
  else
    to_s.chomp == other.to_s.chomp
  end
end

#after(line_num, lines = 1) ⇒ Code

Remove all lines except for the lines after and excluding line_num.

Parameters:

  • line_num (Fixnum)
  • lines (Fixnum) (defaults to: 1)

Returns:



243
244
245
246
247
248
249
# File 'lib/pry/code.rb', line 243

def after(line_num, lines=1)
  return self unless line_num

  select do |l, ln|
    ln > line_num && ln <= line_num + lines
  end
end

#around(line_num, lines = 1) ⇒ Code

Remove all lines except for the lines on either side of and including line_num.

Parameters:

  • line_num (Fixnum)
  • lines (Fixnum) (defaults to: 1)

Returns:



230
231
232
233
234
235
236
# File 'lib/pry/code.rb', line 230

def around(line_num, lines=1)
  return self unless line_num

  select do |l, ln|
    ln >= line_num - lines && ln <= line_num + lines
  end
end

#before(line_num, lines = 1) ⇒ Code

Remove all lines except for the lines up to and excluding line_num.

Parameters:

  • line_num (Fixnum)
  • lines (Fixnum) (defaults to: 1)

Returns:



216
217
218
219
220
221
222
# File 'lib/pry/code.rb', line 216

def before(line_num, lines=1)
  return self unless line_num

  select do |l, ln|
    ln >= line_num - lines && ln < line_num
  end
end

#between(start_line, end_line = nil) ⇒ Code

Remove all lines that aren't in the given range, expressed either as a Range object or a first and last line number (inclusive). Negative indices count from the end of the array of lines.

Parameters:

  • start_line (Range, Fixnum)
  • end_line (Fixnum?) (defaults to: nil)

Returns:



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
# File 'lib/pry/code.rb', line 165

def between(start_line, end_line=nil)
  return self unless start_line

  if start_line.is_a? Range
    end_line = start_line.last
    end_line -= 1 if start_line.exclude_end?

    start_line = start_line.first
  else
    end_line ||= start_line
  end

  if start_line > 0
    start_idx = @lines.index { |l| l.last >= start_line } || @lines.length
  else
    start_idx = start_line
  end

  if end_line > 0
    end_idx = (@lines.index { |l| l.last > end_line } || 0) - 1
  else
    end_idx = end_line
  end

  alter do
    @lines = @lines[start_idx..end_idx] || []
  end
end

#comment_describing(line_number) ⇒ String

Get the comment that describes the expression on the given line number.

Parameters:

  • line_number (Fixnum)

    (1-based)

Returns:

  • (String)

    the code.



347
348
349
# File 'lib/pry/code.rb', line 347

def comment_describing(line_number)
  self.class.comment_describing(raw, line_number)
end

#expression_at(line_number, consume = 0) ⇒ String

Get the multiline expression that starts on the given line number.

Parameters:

  • line_number (Fixnum)

    (1-based)

Returns:

  • (String)

    the code.



355
356
357
# File 'lib/pry/code.rb', line 355

def expression_at(line_number, consume=0)
  self.class.expression_at(raw, line_number, :consume => consume)
end

#grep(pattern) ⇒ Code

Remove all lines that don't match the given pattern.

Parameters:

  • pattern (Regexp)

Returns:



255
256
257
258
259
260
261
262
# File 'lib/pry/code.rb', line 255

def grep(pattern)
  return self unless pattern
  pattern = Regexp.new(pattern)

  select do |l, ln|
    l =~ pattern
  end
end

#inspectString

Returns:

  • (String)


299
300
301
# File 'lib/pry/code.rb', line 299

def inspect
  Object.instance_method(:to_s).bind(self).call
end

#lengthFixnum

Return the number of lines stored.

Returns:

  • (Fixnum)


369
370
371
# File 'lib/pry/code.rb', line 369

def length
  @lines ? @lines.length : 0
end

#push(line, line_num = nil) ⇒ String Also known as: <<

Append the given line. line_num is one more than the last existing line, unless specified otherwise.

Parameters:

  • line (String)
  • line_num (Fixnum?) (defaults to: nil)

Returns:

  • (String)

    The inserted line.



141
142
143
144
145
# File 'lib/pry/code.rb', line 141

def push(line, line_num=nil)
  line_num = @lines.last.last + 1 unless line_num
  @lines.push([line.chomp, line_num])
  line
end

#rawString

Return an unformatted String of the code.

Returns:

  • (String)


362
363
364
# File 'lib/pry/code.rb', line 362

def raw
  @lines.map(&:first).join("\n") + "\n"
end

#select {|line| ... } ⇒ Code

Filter the lines using the given block.

Yields:

  • (line)

Returns:



152
153
154
155
156
# File 'lib/pry/code.rb', line 152

def select(&blk)
  alter do
    @lines = @lines.select(&blk)
  end
end

#take_lines(start_line, num_lines) ⇒ Code

Take num_lines from start_line, forward or backwards

Parameters:

  • start_line (Fixnum)
  • num_lines (Fixnum)

Returns:



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/pry/code.rb', line 199

def take_lines(start_line, num_lines)
  if start_line >= 0
    start_idx = @lines.index { |l| l.last >= start_line } || @lines.length
  else
    start_idx = @lines.length + start_line
  end

  alter do
    @lines = @lines.slice(start_idx, num_lines)
  end
end

#to_sString

Based on the configuration of the object, return a formatted String representation.

Returns:

  • (String)


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/pry/code.rb', line 307

def to_s
  lines = @lines.map(&:dup)

  if Pry.color
    lines.each do |l|
      l[0] = CodeRay.scan(l[0], @code_type).term
    end
  end

  if @with_line_numbers
    max_width = lines.last.last.to_s.length if lines.length > 0
    lines.each do |l|
      padded_line_num = l[1].to_s.rjust(max_width)
      l[0] = "#{Pry::Helpers::BaseHelpers.colorize_code(padded_line_num.to_s)}: #{l[0]}"
    end
  end

  if @with_marker
    lines.each do |l|
      if l[1] == @marker_line_num
        l[0] = " => #{l[0]}"
      else
        l[0] = "    #{l[0]}"
      end
    end
  end

  if @with_indentation
    lines.each do |l|
      l[0] = "#{' ' * @indentation_num}#{l[0]}"
    end
  end

  lines.map { |l| "#{l.first}\n" }.join
end

#with_indentation(spaces = 0) ⇒ Code

Format output with the specified number of spaces in front of every line, unless spaces is falsy.

Parameters:

  • spaces (Fixnum?) (defaults to: 0)

Returns:



291
292
293
294
295
296
# File 'lib/pry/code.rb', line 291

def with_indentation(spaces=0)
  alter do
    @with_indentation = !!spaces
    @indentation_num  = spaces
  end
end

#with_line_numbers(y_n = true) ⇒ Code

Format output with line numbers next to it, unless y_n is falsy.

Parameters:

  • y_n (Boolean?) (defaults to: true)

Returns:



268
269
270
271
272
# File 'lib/pry/code.rb', line 268

def with_line_numbers(y_n=true)
  alter do
    @with_line_numbers = y_n
  end
end

#with_marker(line_num = 1) ⇒ Code

Format output with a marker next to the given line_num, unless line_num is falsy.

Parameters:

  • line_num (Fixnum?) (defaults to: 1)

Returns:



279
280
281
282
283
284
# File 'lib/pry/code.rb', line 279

def with_marker(line_num=1)
  alter do
    @with_marker     = !!line_num
    @marker_line_num = line_num
  end
end