Results for: "to_proc"

Raised by Resolver when a dependency requests a gem for which there is no spec.

No documentation available
No documentation available

This class is responsible for taking a code block that exists at a far indentaion and then iteratively increasing the block so that it captures everything within the same indentation block.

def dog
  puts "bow"
  puts "wow"
end

block = BlockExpand.new(code_lines: code_lines)

.call(CodeBlock.new(lines: code_lines[1]))

puts block.to_s # => puts “bow”

puts "wow"

Once a code block has captured everything at a given indentation level then it will expand to capture surrounding indentation.

block = BlockExpand.new(code_lines: code_lines)

.call(block)

block.to_s # => def dog

  puts "bow"
  puts "wow"
end

Parses and sanitizes source into a lexically aware document

Internally the document is represented by an array with each index containing a CodeLine correlating to a line from the source code.

There are three main phases in the algorithm:

  1. Sanitize/format input source

  2. Search for invalid blocks

  3. Format invalid blocks into something meaningful

This class handles the first part.

The reason this class exists is to format input source for better/easier/cleaner exploration.

The CodeSearch class operates at the line level so we must be careful to not introduce lines that look valid by themselves, but when removed will trigger syntax errors or strange behavior.

## Join Trailing slashes

Code with a trailing slash is logically treated as a single line:

1 it "code can be split" \
2    "across multiple lines" do

In this case removing line 2 would add a syntax error. We get around this by internally joining the two lines into a single “line” object

## Logically Consecutive lines

Code that can be broken over multiple lines such as method calls are on different lines:

1 User.
2   where(name: "schneems").
3   first

Removing line 2 can introduce a syntax error. To fix this, all lines are joined into one.

## Heredocs

A heredoc is an way of defining a multi-line string. They can cause many problems. If left as a single line, the parser would try to parse the contents as ruby code rather than as a string. Even without this problem, we still hit an issue with indentation:

1 foo = <<~HEREDOC
2  "Be yourself; everyone else is already taken.""
3    ― Oscar Wilde
4      puts "I look like ruby code" # but i'm still a heredoc
5 HEREDOC

If we didn’t join these lines then our algorithm would think that line 4 is separate from the rest, has a higher indentation, then look at it first and remove it.

If the code evaluates line 5 by itself it will think line 5 is a constant, remove it, and introduce a syntax errror.

All of these problems are fixed by joining the whole heredoc into a single line.

## Comments and whitespace

Comments can throw off the way the lexer tells us that the line logically belongs with the next line. This is valid ruby but results in a different lex output than before:

1 User.
2   where(name: "schneems").
3   # Comment here
4   first

To handle this we can replace comment lines with empty lines and then re-lex the source. This removal and re-lexing preserves line index and document size, but generates an easier to work with document.

Multiple lines form a singular CodeBlock

Source code is made of multiple CodeBlocks.

Example:

code_block.to_s # =>
  #   def foo
  #     puts "foo"
  #   end

code_block.valid? # => true
code_block.in_valid? # => false

There are three main phases in the algorithm:

  1. Sanitize/format input source

  2. Search for invalid blocks

  3. Format invalid blocks into something meaningful

The Code frontier is a critical part of the second step

## Knowing where we’ve been

Once a code block is generated it is added onto the frontier. Then it will be sorted by indentation and frontier can be filtered. Large blocks that fully enclose a smaller block will cause the smaller block to be evicted.

CodeFrontier#<<(block) # Adds block to frontier
CodeFrontier#pop # Removes block from frontier

## Knowing where we can go

Internally the frontier keeps track of “unvisited” lines which are exposed via ‘next_indent_line` when called, this method returns, a line of code with the highest indentation.

The returned line of code can be used to build a CodeBlock and then that code block is added back to the frontier. Then, the lines are removed from the “unvisited” so we don’t double-create the same block.

CodeFrontier#next_indent_line # Shows next line
CodeFrontier#register_indent_block(block) # Removes lines from unvisited

## Knowing when to stop

The frontier knows how to check the entire document for a syntax error. When blocks are added onto the frontier, they’re removed from the document. When all code containing syntax errors has been added to the frontier, the document will be parsable without a syntax error and the search can stop.

CodeFrontier#holds_all_syntax_errors? # Returns true when frontier holds all syntax errors

## Filtering false positives

Once the search is completed, the frontier may have multiple blocks that do not contain the syntax error. To limit the result to the smallest subset of “invalid blocks” call:

CodeFrontier#detect_invalid_blocks

Used for formatting invalid blocks

No documentation available

Converts a SyntaxError message to a path

Handles the case where the filename has a colon in it such as on a windows file system: github.com/ruby/syntax_suggest/issues/111

Example:

message = "/tmp/scratch:2:in `require_relative': /private/tmp/bad.rb:1: syntax error, unexpected `end' (SyntaxError)"
puts PathnameFromMessage.new(message).call.name
# => "/tmp/scratch.rb"

Keeps track of what elements are in the queue in priority and also ensures that when one element engulfs/covers/eats another that the larger element evicts the smaller element

Holds elements in a priority heap on insert

Instead of constantly calling ‘sort!`, put the element where it belongs the first time around

Example:

queue = PriorityQueue.new
queue << 33
queue << 44
queue << 1

puts queue.peek # => 44

Capture parse errors from Ripper

Prism returns the errors with their messages, but Ripper does not. To get them we must make a custom subclass.

Example:

puts RipperErrors.new(" def foo").call.errors
# => ["syntax error, unexpected end-of-input, expecting ';' or '\\n'"]

Raised by Timeout.timeout when the block times out.

Base class for all URI exceptions.

Not a URI.

Not a URI component.

URI is valid, bad usage is not.

RefError is raised when a referenced object has been recycled by the garbage collector

Raised when a mathematical function is evaluated outside of its domain of definition.

For example, since cos returns values in the range -1..1, its inverse function acos is only defined on that interval:

Math.acos(42)

produces:

Math::DomainError: Numerical argument is out of domain - "acos"
No documentation available
No documentation available

Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor. Its cause will contain the original exception, and ractor is the original ractor it was raised in.

r = Ractor.new { raise "Something weird happened" }

begin
  r.take
rescue => e
  p e             # => #<Ractor::RemoteError: thrown by remote Ractor.>
  p e.ractor == r # => true
  p e.cause       # => #<RuntimeError: Something weird happened>
end

Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.

r = Ractor.new { sleep }

ary = [1, 2, 3]
r.send(ary, move: true)
ary.inspect
# Ractor::MovedError (can not send any methods to a moved object)

Raised when an attempt is made to send a message to a closed port, or to retrieve a message from a closed and empty port. Ports may be closed explicitly with Ractor#close_outgoing/close_incoming and are closed implicitly when a Ractor terminates.

r = Ractor.new { sleep(500) }
r.close_outgoing
r.take # Ractor::ClosedError

ClosedError is a descendant of StopIteration, so the closing of the ractor will break the loops without propagating the error:

r = Ractor.new do
  loop do
    msg = receive # raises ClosedError and loop traps it
    puts "Received: #{msg}"
  end
  puts "loop exited"
end

3.times{|i| r << i}
r.close_incoming
r.take
puts "Continue successfully"

This will print:

Received: 0
Received: 1
Received: 2
loop exited
Continue successfully
Search took: 6ms  ·  Total Results: 2407