Module: SyntaxSuggest
Constant Summary
- 
    DEFAULT_VALUE =
    # File 'lib/syntax_suggest/api.rb', line 33Used to indicate a default value that cannot be confused with another input. Object.new.freeze 
- 
    TIMEOUT_DEFAULT =
    
 # File 'lib/syntax_suggest/api.rb', line 36ENV.fetch("SYNTAX_SUGGEST_TIMEOUT", 1).to_i 
- 
    VERSION =
    
 # File 'lib/syntax_suggest/version.rb', line 4"2.0.2"
Class Attribute Summary
- 
    
      .use_prism_parser?  ⇒ Boolean 
    
    readonly
    use_prism_parser?[Private].
Class Method Summary
- 
    
      .call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr)  
    
    call[Private].
- 
    
      .handle_error(e, re_raise: true, io: $stderr)  
    
    handle_error[Public].
- 
    
      .invalid?(source)  
    
    See additional method definition at line 160. 
- 
    
      .module_for_detailed_message  
    
    module_for_detailed_message[Private].
- 
    
      .record_dir(dir)  
    
    record_dir[Private].
- 
    
      .valid?(source)  ⇒ Boolean 
    
    valid?[Private].
- 
    
      .valid_without?(without_lines:, code_lines:)  ⇒ Boolean 
    
    valid_without?[Private].
Class Attribute Details
    .use_prism_parser?  ⇒ Boolean  (readonly)
  
use_prism_parser? [Private]
Tells us if the prism parser is available for use or if we should fallback to Ripper
# File 'lib/syntax_suggest/api.rb', line 42
def self.use_prism_parser? defined?(Prism) end
Class Method Details
.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr)
call [Private]
Main private interface
# File 'lib/syntax_suggest/api.rb', line 91
def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: DEFAULT_VALUE, timeout: TIMEOUT_DEFAULT, io: $stderr) search = nil filename = nil if filename == DEFAULT_VALUE Timeout.timeout(timeout) do record_dir ||= ENV["DEBUG"] ? "tmp" : nil search = CodeSearch.new(source, record_dir: record_dir).call end blocks = search.invalid_blocks DisplayInvalidBlocks.new( io: io, blocks: blocks, filename: filename, terminal: terminal, code_lines: search.code_lines ).call rescue Timeout::Error => e io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with SYNTAX_SUGGEST_DEBUG=1 for more info" io.puts e.backtrace.first(3).join($/) end
.handle_error(e, re_raise: true, io: $stderr)
handle_error [Public]
Takes a SyntaxError exception, uses the error message to locate the file. Then the file will be analyzed to find the location of the syntax error and emit that location to stderr.
Example:
begin
  require 'bad_file'
rescue => e
  SyntaxSuggest.handle_error(e)
endBy default it will re-raise the exception unless re_raise: false. The message output location can be configured using the io: $stderr input.
If a valid filename cannot be determined, the original exception will be re-raised (even with ‘re_raise: false`).
# File 'lib/syntax_suggest/api.rb', line 68
def self.handle_error(e, re_raise: true, io: $stderr) unless e.is_a?(SyntaxError) io.puts("SyntaxSuggest: Must pass a SyntaxError, got: #{e.class}") raise e end file = PathnameFromMessage.new(e., io: io).call.name raise e unless file io.sync = true call( io: io, source: file.read, filename: file ) raise e if re_raise end
.invalid?(source)
See additional method definition at line 160.
# File 'lib/syntax_suggest/api.rb', line 167
def self.invalid?(source) source = source.join if source.is_a?(Array) source = source.to_s Prism.parse(source).failure? end
.module_for_detailed_message
module_for_detailed_message [Private]
Used to monkeypatch SyntaxError via Module.prepend
# File 'lib/syntax_suggest/core_ext.rb', line 9
def self. Module.new { def (highlight: true, syntax_suggest: true, **kwargs) return super unless syntax_suggest require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE) = super if path file = Pathname.new(path) io = SyntaxSuggest::MiniStringIO.new SyntaxSuggest.call( io: io, source: file.read, filename: file, terminal: highlight ) annotation = io.string annotation += "\n" unless annotation.end_with?("\n") annotation + else end rescue => e if ENV["SYNTAX_SUGGEST_DEBUG"] $stderr.warn(e.) $stderr.warn(e.backtrace) end # Ignore internal errors end } end
.record_dir(dir)
record_dir [Private]
Used to generate a unique directory to record search steps for debugging
# File 'lib/syntax_suggest/api.rb', line 116
def self.record_dir(dir) time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N") dir = Pathname(dir) dir.join(time).tap { |path| path.mkpath alias_dir = dir.join("last") FileUtils.rm_rf(alias_dir) if alias_dir.exist? FileUtils.ln_sf(time, alias_dir) } end
    .valid?(source)  ⇒ Boolean 
  
valid? [Private]
Returns truthy if a given input source is valid syntax
SyntaxSuggest.valid?(<<~EOM) # => true
  def foo
  end
EOM
SyntaxSuggest.valid?(<<~EOM) # => false
  def foo
    def bar # Syntax error here
  end
EOMYou can also pass in an array of lines and they’ll be joined before evaluating
SyntaxSuggest.valid?(
  [
    "def foo\n",
    "end\n"
  ]
) # => true
SyntaxSuggest.valid?(
  [
    "def foo\n",
    "  def bar\n", # Syntax error here
    "end\n"
  ]
) # => falseAs an FYI the ::SyntaxSuggest::CodeLine class instances respond to to_s so passing a ::SyntaxSuggest::CodeLine in as an object or as an array will convert it to it’s code representation.
# File 'lib/syntax_suggest/api.rb', line 211
def self.valid?(source) !invalid?(source) end
    .valid_without?(without_lines:, code_lines:)  ⇒ Boolean 
  
valid_without? [Private]
This will tell you if the code_lines would be valid if you removed the without_lines. In short it’s a way to detect if we’ve found the lines with syntax errors in our document yet.
code_lines = [
  CodeLine.new(line: "def foo\n",   index: 0)
  CodeLine.new(line: "  def bar\n", index: 1)
  CodeLine.new(line: "end\n",       index: 2)
]
SyntaxSuggest.valid_without?(
  without_lines: code_lines[1],
  code_lines: code_lines
)                                    # => true
SyntaxSuggest.valid?(code_lines) # => false# File 'lib/syntax_suggest/api.rb', line 146
def self.valid_without?(without_lines:, code_lines:) lines = code_lines - Array(without_lines).flatten if lines.empty? true else valid?(lines) end end