Class: Pry::Code

Inherits:
Object show all
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

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

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: [])
  • (1) (Fixnum?)

    start_line

  • (:ruby) (Symbol?)

    code_type



184
185
186
187
188
189
190
191
# File 'lib/pry/code.rb', line 184

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

- (Object) method_missing(name, *args, &blk)

Forward any missing methods to the output of #to_s.



432
433
434
# File 'lib/pry/code.rb', line 432

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

Instance Attribute Details

- (Object) code_type

Returns the value of attribute code_type



174
175
176
# File 'lib/pry/code.rb', line 174

def code_type
  @code_type
end

Class Method Details

+ (Boolean) complete_expression?(str)

Determine if a string of code is a complete Ruby expression.

Examples:

complete_expression?("class Hello") #=> false
complete_expression?("class Hello; end") #=> true

Parameters:

  • code (String)

    The code to validate.

Returns:

  • (Boolean)

    Whether or not the code is a complete Ruby expression.

Raises:

  • (SyntaxError)

    Any SyntaxError that does not represent incompleteness.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pry/code.rb', line 38

def complete_expression?(str)
  if defined?(Rubinius::Melbourne19) && RUBY_VERSION =~ /^1\.9/
    Rubinius::Melbourne19.parse_string(str, Pry.eval_path)
  elsif defined?(Rubinius::Melbourne)
    Rubinius::Melbourne.parse_string(str, Pry.eval_path)
  else
    catch(:valid) do
      Helpers::BaseHelpers.silence_warnings do
        eval("BEGIN{throw :valid}\n#{str}", binding, Pry.eval_path)
      end
    end
  end

  # Assert that a line which ends with a , or \ is incomplete.
  str !~ /[,\\]\s*\z/
rescue SyntaxError => e
  if incomplete_user_input_exception?(e)
    false
  else
    raise e
  end
end

+ (Code) from_file(fn, code_type = nil)

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)

    (:ruby) The type of code the file contains.

Returns:



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pry/code.rb', line 102

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 CommandError, "Cannot open #{fn.inspect} for reading."
    end
  end
  new(f, 1, code_type || :ruby)
ensure
  f.close if f.respond_to?(:close)
end

+ (Code) from_method(meth, start_line = nil)

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.

  • The (Fixnum, nil)

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

Returns:



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

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

+ (Code) from_module(mod, start_line = nil)

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

Parameters:

  • mod (Module, Class)

    The module (or class) of interest.

Returns:



136
137
138
139
140
# File 'lib/pry/code.rb', line 136

def from_module(mod, start_line=nil)
  mod = Pry::WrappedModule(mod)
  start_line ||= mod.source_line || 1
  new(mod.source, start_line, :ruby)
end

+ (Boolean) incomplete_user_input_exception?(ex) (private)

Check whether the exception indicates that the user should input more.

Parameters:

  • the (SyntaxError)

    exception object that was raised.

  • The (Array<String>)

    stack frame of the function that executed eval.

Returns:

  • (Boolean)


67
68
69
70
71
72
73
74
75
76
# File 'lib/pry/code.rb', line 67

def incomplete_user_input_exception?(ex)
  case ex.message
  when /unexpected (\$end|end-of-file|END_OF_FILE)/, # mri, jruby, ironruby
    /unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
    /missing 'end' for/, /: expecting '[})\]]'$/, /can't find string ".*" anywhere before EOF/, /: expecting keyword_end/, /expecting kWHEN/ # rbx
    true
  else
    false
  end
end

+ (String?) retrieve_complete_expression_from(str_or_lines)

Retrieve the first complete expression from the passed string.

Parameters:

  • str (String, Array<String>)

    The string (or array of lines) to extract the complete expression from.

Returns:

  • (String, nil)

    The first complete expression, or nil if none found.



85
86
87
88
89
90
91
92
93
94
# File 'lib/pry/code.rb', line 85

def retrieve_complete_expression_from(str_or_lines)
  lines = str_or_lines.is_a?(Array) ? str_or_lines : str_or_lines.each_line.to_a

  code = ""
  lines.each do |v|
    code << v
    return code if complete_expression?(code)
  end
  nil
end

Instance Method Details

- (Boolean) ==(other)

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)


420
421
422
423
424
425
426
427
428
429
# File 'lib/pry/code.rb', line 420

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

- (Code) after(line_num, lines = 1)

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

Parameters:

  • line_num (Fixnum)
  • (1) (Fixnum)

    lines

Returns:



301
302
303
304
305
306
307
# File 'lib/pry/code.rb', line 301

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

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

- (Code) around(line_num, lines = 1)

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

Parameters:

  • line_num (Fixnum)
  • (1) (Fixnum)

    lines

Returns:



288
289
290
291
292
293
294
# File 'lib/pry/code.rb', line 288

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

- (Code) before(line_num, lines = 1)

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

Parameters:

  • line_num (Fixnum)
  • (1) (Fixnum)

    lines

Returns:



274
275
276
277
278
279
280
# File 'lib/pry/code.rb', line 274

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

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

- (Code) between(start_line, end_line = nil)

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:



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/pry/code.rb', line 223

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

- (Code) grep(pattern)

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

Parameters:

  • pattern (Regexp)

Returns:



313
314
315
316
317
318
319
320
# File 'lib/pry/code.rb', line 313

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

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

- (String) inspect

Returns:

  • (String)


357
358
359
# File 'lib/pry/code.rb', line 357

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

- (Fixnum) length

Return the number of lines stored.

Returns:

  • (Fixnum)


411
412
413
# File 'lib/pry/code.rb', line 411

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

- (String) push(line, line_num = nil) 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.



199
200
201
202
203
# File 'lib/pry/code.rb', line 199

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

- (String) raw

Return an unformatted String of the code.

Returns:

  • (String)


404
405
406
# File 'lib/pry/code.rb', line 404

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

- (Code) select {|line| ... }

Filter the lines using the given block.

Yields:

  • (line)

Returns:



210
211
212
213
214
# File 'lib/pry/code.rb', line 210

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

- (Code) take_lines(start_line, num_lines)

Take num_lines from start_line, forward or backwards

Parameters:

  • start_line (Fixnum)
  • num_lines (Fixnum)

Returns:



257
258
259
260
261
262
263
264
265
266
267
# File 'lib/pry/code.rb', line 257

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

- (String) to_s

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

Returns:

  • (String)


365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/pry/code.rb', line 365

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::Text.blue(padded_line_num)}: #{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

- (Code) with_indentation(spaces = 0)

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

Parameters:

  • (0) (Fixnum?)

    spaces

Returns:



349
350
351
352
353
354
# File 'lib/pry/code.rb', line 349

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

- (Code) with_line_numbers(y_n = true)

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

Parameters:

  • (true) (Boolean?)

    y_n

Returns:



326
327
328
329
330
# File 'lib/pry/code.rb', line 326

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

- (Code) with_marker(line_num = 1)

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

Parameters:

  • (1) (Fixnum?)

    line_num

Returns:



337
338
339
340
341
342
# File 'lib/pry/code.rb', line 337

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