class Solargraph::SourceMap::Clip

A static analysis tool for obtaining definitions, completions, signatures, and type inferences from a cursor.

Attributes

api_map[R]

@return [ApiMap]

cursor[R]

@return [Source::Cursor]

Public Class Methods

new(api_map, cursor) click to toggle source

@param api_map [ApiMap] @param cursor [Source::Cursor]

# File lib/solargraph/source_map/clip.rb, line 11
def initialize api_map, cursor
  @api_map = api_map
  @cursor = cursor
end

Public Instance Methods

complete() click to toggle source

@return [Completion]

# File lib/solargraph/source_map/clip.rb, line 25
def complete
  return package_completions([]) if !source_map.source.parsed? || cursor.string?
  return package_completions(api_map.get_symbols) if cursor.chain.literal? && cursor.chain.links.last.word == '<Symbol>'
  return Completion.new([], cursor.range) if cursor.chain.literal?
  if cursor.comment?
    tag_complete
  else
    code_complete
  end
end
define() click to toggle source

@return [Array<Pin::Base>]

# File lib/solargraph/source_map/clip.rb, line 17
def define
  return [] if cursor.comment? || cursor.chain.literal?
  result = cursor.chain.define(api_map, block, locals)
  result.concat((source_map.pins + source_map.locals).select{ |p| p.name == cursor.word && p.location.range.contain?(cursor.position) }) if result.empty?
  result
end
gates() click to toggle source
# File lib/solargraph/source_map/clip.rb, line 63
def gates
  block.gates
end
in_block?() click to toggle source
# File lib/solargraph/source_map/clip.rb, line 67
def in_block?
  return @in_block unless @in_block.nil?
  @in_block = begin
    tree = cursor.source.tree_at(cursor.position.line, cursor.position.column)
    Parser.is_ast_node?(tree[1]) && [:block, :ITER].include?(tree[1].type)
  end
end
infer() click to toggle source

@return [ComplexType]

# File lib/solargraph/source_map/clip.rb, line 44
def infer
  result = cursor.chain.infer(api_map, block, locals)
  return result unless result.tag == 'self'
  ComplexType.try_parse(cursor.chain.base.infer(api_map, block, locals).namespace)
end
locals() click to toggle source

Get an array of all the locals that are visible from the cursors's position. Locals can be local variables, method parameters, or block parameters. The array starts with the nearest local pin.

@return [Array<Solargraph::Pin::Base>]

# File lib/solargraph/source_map/clip.rb, line 55
def locals
  loc_pos = context_pin.location.range.contain?(cursor.position) ? cursor.position : context_pin.location.range.ending
  adj_pos = Position.new(loc_pos.line, (loc_pos.column.zero? ? 0 : loc_pos.column - 1))
  @locals ||= source_map.locals.select { |pin|
    pin.visible_from?(block, adj_pos)
  }.reverse
end
signify() click to toggle source

@return [Array<Pin::Base>]

# File lib/solargraph/source_map/clip.rb, line 37
def signify
  return [] unless cursor.argument?
  chain = Parser.chain(cursor.recipient_node, cursor.filename)
  chain.define(api_map, context_pin, locals).select { |pin| pin.is_a?(Pin::Method) }
end
translate(phrase) click to toggle source

@param phrase [String] @return [Array<Solargraph::Pin::Base>]

# File lib/solargraph/source_map/clip.rb, line 77
def translate phrase
  chain = Parser.chain(Parser.parse(phrase))
  chain.define(api_map, block, locals)
end

Private Instance Methods

block() click to toggle source

@return [Solargraph::Pin::Base]

# File lib/solargraph/source_map/clip.rb, line 96
def block
  @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character)
end
code_complete() click to toggle source
# File lib/solargraph/source_map/clip.rb, line 175
def code_complete
  result = []
  result.concat complete_keyword_parameters
  if cursor.chain.constant? || cursor.start_of_constant?
    full = cursor.chain.links.first.word
    type = if cursor.chain.undefined?
      cursor.chain.base.infer(api_map, context_pin, locals)
    else
      if full.include?('::') && cursor.chain.links.length == 1
        ComplexType.try_parse(full.split('::')[0..-2].join('::'))
      elsif cursor.chain.links.length > 1
        ComplexType.try_parse(full)
      else
        ComplexType::UNDEFINED
      end
    end
    if type.undefined?
      if full.include?('::')
        result.concat api_map.get_constants(full, *gates)
      else
        result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
      end
    else
      result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
    end
  else
    type = cursor.chain.base.infer(api_map, block, locals)
    result.concat api_map.get_complex_type_methods(type, block.binder.namespace, cursor.chain.links.length == 1)
    if cursor.chain.links.length == 1
      if cursor.word.start_with?('@@')
        return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace))
      elsif cursor.word.start_with?('@')
        return package_completions(api_map.get_instance_variable_pins(block.binder.namespace, block.binder.scope))
      elsif cursor.word.start_with?('$')
        return package_completions(api_map.get_global_variable_pins)
      end
      result.concat locals
      result.concat api_map.get_constants(context_pin.context.namespace, *gates)
      result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
      result.concat api_map.get_methods('Kernel')
      # result.concat ApiMap.keywords
      result.concat api_map.keyword_pins
      result.concat yielded_self_pins
    end
  end
  package_completions(result)
end
complete_keyword_parameters() click to toggle source

@return [Array<Pin::KeywordParam]

# File lib/solargraph/source_map/clip.rb, line 123
def complete_keyword_parameters
  return [] unless cursor.argument? && cursor.chain.links.one? && cursor.word =~ /^[a-z0-9_]*:?$/
  pins = signify
  result = []
  done = []
  pins.each do |pin|
    pin.parameters.each do |param|
      next if done.include?(param.name)
      done.push param.name
      next unless param.keyword?
      result.push Pin::KeywordParam.new(pin.location, "#{param.name}:")
    end
    if !pin.parameters.empty? && pin.parameters.last.kwrestarg?
      pin.docstring.tags(:param).each do |tag|
        next if done.include?(tag.name)
        done.push tag.name
        result.push Pin::KeywordParam.new(pin.location, "#{tag.name}:")
      end
    end
  end
  result
end
context_pin() click to toggle source

The context at the current position.

@return [Pin::Base]

# File lib/solargraph/source_map/clip.rb, line 103
def context_pin
  @context_pin ||= source_map.locate_named_path_pin(cursor.node_position.line, cursor.node_position.character)
end
package_completions(result) click to toggle source

@param result [Array<Pin::Base>] @return [Completion]

# File lib/solargraph/source_map/clip.rb, line 148
def package_completions result
  frag_start = cursor.start_of_word.to_s.downcase
  filtered = result.uniq(&:name).select { |s|
    s.name.downcase.start_with?(frag_start) &&
      (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
  }
  Completion.new(filtered, cursor.range)
end
source_map() click to toggle source

@return [SourceMap]

# File lib/solargraph/source_map/clip.rb, line 91
def source_map
  @source_map ||= api_map.source_map(cursor.filename)
end
tag_complete() click to toggle source
# File lib/solargraph/source_map/clip.rb, line 157
def tag_complete
  result = []
  match = source_map.code[0..cursor.offset-1].match(/[\[<, ]([a-z0-9_:]*)\z/i)
  if match
    full = match[1]
    if full.include?('::')
      if full.end_with?('::')
        result.concat api_map.get_constants(full[0..-3], *gates)
      else
        result.concat api_map.get_constants(full.split('::')[0..-2].join('::'), *gates)
      end
    else
      result.concat api_map.get_constants('', full.end_with?('::') ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
    end
  end
  package_completions(result)
end
yielded_self_pins() click to toggle source

@return [Array<Pin::Base>]

# File lib/solargraph/source_map/clip.rb, line 108
def yielded_self_pins
  return [] unless block.is_a?(Pin::Block) && block.receiver
  chain = Parser.chain(block.receiver, source_map.source.filename)
  receiver_pin = chain.define(api_map, context_pin, locals).first
  return [] if receiver_pin.nil?
  result = []
  ys = receiver_pin.docstring.tag(:yieldpublic)
  unless ys.nil? || ys.types.empty?
    ysct = ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
    result.concat api_map.get_complex_type_methods(ysct, '', false)
  end
  result
end