class Solargraph::Library

A Library handles coordination between a Workspace and an ApiMap.

Attributes

current[R]

@return [Source, nil]

name[R]

@return [String, nil]

workspace[R]

@return [Solargraph::Workspace]

Public Class Methods

load(directory = '', name = nil) click to toggle source

Create a library from a directory.

@param directory [String] The path to be used for the workspace @param name [String, nil] @return [Solargraph::Library]

# File lib/solargraph/library.rb, line 374
def self.load directory = '', name = nil
  Solargraph::Library.new(Solargraph::Workspace.new(directory), name)
end
new(workspace = Solargraph::Workspace.new, name = nil) click to toggle source

@param workspace [Solargraph::Workspace] @param name [String, nil]

# File lib/solargraph/library.rb, line 20
def initialize workspace = Solargraph::Workspace.new, name = nil
  @workspace = workspace
  @name = name
  api_map.catalog bench
  @synchronized = true
  @catalog_mutex = Mutex.new
end

Public Instance Methods

attach(source) click to toggle source

Attach a source to the library.

The attached source does not need to be a part of the workspace. The library will include it in the ApiMap while it's attached. Only one source can be attached to the library at a time.

@param source [Source, nil] @return [void]

# File lib/solargraph/library.rb, line 49
def attach source
  mutex.synchronize do
    @synchronized = (@current == source) if synchronized?
    @current = source
    catalog
  end
end
attached?(filename) click to toggle source

True if the specified file is currently attached.

@param filename [String] @return [Boolean]

# File lib/solargraph/library.rb, line 61
def attached? filename
  !@current.nil? && @current.filename == filename
end
Also aliased as: open?
catalog() click to toggle source

Update the ApiMap from the library's workspace and open files.

@return [void]

# File lib/solargraph/library.rb, line 348
def catalog
  @catalog_mutex.synchronize do
    break if synchronized?
    logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
    api_map.catalog bench
    @synchronized = true
    logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
  end
end
close(filename) click to toggle source

Close a file in the library. Closing a file will make it unavailable for checkout although it may still exist in the workspace.

@param filename [String] @return [void]

# File lib/solargraph/library.rb, line 142
def close filename
  mutex.synchronize do
    @synchronized = false
    @current = nil if @current && @current.filename == filename
    catalog
  end
end
completions_at(filename, line, column) click to toggle source

Get completion suggestions at the specified file and location.

@param filename [String] The file to analyze @param line [Integer] The zero-based line number @param column [Integer] The zero-based column number @return [SourceMap::Completion] @todo Take a Location instead of filename/line/column

# File lib/solargraph/library.rb, line 157
def completions_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(read(filename), position)
  api_map.clip(cursor).complete
end
contain?(filename) click to toggle source

True if the specified file is included in the workspace (but not necessarily open).

@param filename [String] @return [Boolean]

# File lib/solargraph/library.rb, line 81
def contain? filename
  workspace.has_file?(filename)
end
create(filename, text) click to toggle source

Create a source to be added to the workspace. The file is ignored if it is neither open in the library nor included in the workspace.

@param filename [String] @param text [String] The contents of the file @return [Boolean] True if the file was added to the workspace.

# File lib/solargraph/library.rb, line 91
def create filename, text
  result = false
  mutex.synchronize do
    next unless contain?(filename) || open?(filename) || workspace.would_merge?(filename)
    @synchronized = false
    source = Solargraph::Source.load_string(text, filename)
    workspace.merge(source)
    result = true
  end
  result
end
create_from_disk(filename) click to toggle source

Create a file source from a file on disk. The file is ignored if it is neither open in the library nor included in the workspace.

@param filename [String] @return [Boolean] True if the file was added to the workspace.

# File lib/solargraph/library.rb, line 108
def create_from_disk filename
  result = false
  mutex.synchronize do
    next if File.directory?(filename) || !File.exist?(filename)
    next unless contain?(filename) || open?(filename) || workspace.would_merge?(filename)
    @synchronized = false
    source = Solargraph::Source.load_string(File.read(filename), filename)
    workspace.merge(source)
    result = true
  end
  result
end
definitions_at(filename, line, column) click to toggle source

Get definition suggestions for the expression at the specified file and location.

@param filename [String] The file to analyze @param line [Integer] The zero-based line number @param column [Integer] The zero-based column number @return [Array<Solargraph::Pin::Base>] @todo Take filename/position instead of filename/line/column

# File lib/solargraph/library.rb, line 171
def definitions_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(read(filename), position)
  if cursor.comment?
    source = read(filename)
    offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column))
    lft = source.code[0..offset-1].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i)
    rgt = source.code[offset..-1].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i)
    if lft && rgt
      tag = (lft[1] + rgt[1]).sub(/:+$/, '')
      clip = api_map.clip(cursor)
      clip.translate tag
    else
      []
    end
  else
    api_map.clip(cursor).define.map { |pin| pin.realize(api_map) }
  end
end
delete(filename) click to toggle source

Delete a file from the library. Deleting a file will make it unavailable for checkout and optionally remove it from the workspace unless the workspace configuration determines that it should still exist.

@param filename [String] @return [Boolean] True if the file was deleted

# File lib/solargraph/library.rb, line 127
def delete filename
  detach filename
  result = false
  mutex.synchronize do
    result = workspace.remove(filename)
    @synchronized = !result if synchronized?
  end
  result
end
detach(filename) click to toggle source

Detach the specified file if it is currently attached to the library.

@param filename [String] @return [Boolean] True if the specified file was detached

# File lib/solargraph/library.rb, line 70
def detach filename
  return false if @current.nil? || @current.filename != filename
  attach nil
  true
end
diagnose(filename) click to toggle source

Get diagnostics about a file.

@param filename [String] @return [Array<Hash>]

# File lib/solargraph/library.rb, line 315
def diagnose filename
  # @todo Only open files get diagnosed. Determine whether anything or
  #   everything in the workspace should get diagnosed, or if there should
  #   be an option to do so.
  #
  return [] unless open?(filename)
  catalog
  result = []
  source = read(filename)
  repargs = {}
  workspace.config.reporters.each do |line|
    if line == 'all!'
      Diagnostics.reporters.each do |reporter|
        repargs[reporter] ||= []
      end
    else
      args = line.split(':').map(&:strip)
      name = args.shift
        reporter = Diagnostics.reporter(name)
      raise DiagnosticsError, "Diagnostics reporter #{name} does not exist" if reporter.nil?
      repargs[reporter] ||= []
      repargs[reporter].concat args
    end
  end
  repargs.each_pair do |reporter, args|
    result.concat reporter.new(*args.uniq).diagnose(source, api_map)
  end
  result
end
document(query) click to toggle source

@param query [String] @return [Array<YARD::CodeObjects::Base>]

# File lib/solargraph/library.rb, line 262
def document query
  catalog
  api_map.document query
end
document_symbols(filename) click to toggle source

Get an array of document symbols.

Document symbols are composed of namespace, method, and constant pins. The results of this query are appropriate for building the response to a textDocument/documentSymbol message in the language server protocol.

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

# File lib/solargraph/library.rb, line 291
def document_symbols filename
  api_map.document_symbols(filename)
end
folding_ranges(filename) click to toggle source

Get an array of foldable ranges for the specified file.

@deprecated The library should not need to handle folding ranges. The

source itself has all the information it needs.

@param filename [String] @return [Array<Range>]

# File lib/solargraph/library.rb, line 365
def folding_ranges filename
  read(filename).folding_ranges
end
get_path_pins(path) click to toggle source

Get an array of pins that match a path.

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

# File lib/solargraph/library.rb, line 256
def get_path_pins path
  api_map.get_path_suggestions(path)
end
inspect() click to toggle source
# File lib/solargraph/library.rb, line 28
def inspect
  # Let's not deal with insane data dumps in spec failures
  to_s
end
locate_pins(location) click to toggle source

Get the pins at the specified location or nil if the pin does not exist.

@param location [Location] @return [Array<Solargraph::Pin::Base>]

# File lib/solargraph/library.rb, line 244
def locate_pins location
  api_map.locate_pins(location).map { |pin| pin.realize(api_map) }
end
locate_ref(location) click to toggle source
# File lib/solargraph/library.rb, line 248
def locate_ref location
  api_map.require_reference_at location
end
merge(source) click to toggle source

Try to merge a source into the library's workspace. If the workspace is not configured to include the source, it gets ignored.

@param source [Source] @return [Boolean] True if the source was merged into the workspace.

# File lib/solargraph/library.rb, line 383
def merge source
  result = false
  mutex.synchronize do
    result = workspace.merge(source)
    @synchronized = !result if synchronized?
  end
  result
end
open?(filename)
Alias for: attached?
path_pins(path) click to toggle source

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

# File lib/solargraph/library.rb, line 297
def path_pins path
  catalog
  api_map.get_path_suggestions(path)
end
query_symbols(query) click to toggle source

Get an array of all symbols in the workspace that match the query.

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

# File lib/solargraph/library.rb, line 278
def query_symbols query
  catalog
  api_map.query_symbols query
end
read_text(filename) click to toggle source

Get the current text of a file in the library.

@param filename [String] @return [String]

# File lib/solargraph/library.rb, line 306
def read_text filename
  source = read(filename)
  source.code
end
references_from(filename, line, column, strip: false) click to toggle source

@param filename [String] @param line [Integer] @param column [Integer] @param strip [Boolean] Strip special characters from variable names @return [Array<Solargraph::Range>] @todo Take a Location instead of filename/line/column

# File lib/solargraph/library.rb, line 211
def references_from filename, line, column, strip: false
  cursor = api_map.cursor_at(filename, Position.new(line, column))
  clip = api_map.clip(cursor)
  pins = clip.define
  return [] if pins.empty?
  result = []
  pins.uniq.each do |pin|
    (workspace.sources + (@current ? [@current] : [])).uniq(&:filename).each do |source|
      found = source.references(pin.name)
      found.select! do |loc|
        referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character)
        # HACK: The additional location comparison is necessary because
        # Clip#define can return proxies for parameter pins
        referenced.any? { |r| r == pin || r.location == pin.location }
      end
      # HACK: for language clients that exclude special characters from the start of variable names
      if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
        found.map! do |loc|
          Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column))
        end
      end
      result.concat(found.sort do |a, b|
        a.range.start.line <=> b.range.start.line
      end)
    end
  end
  result.uniq
end
signatures_at(filename, line, column) click to toggle source

Get signature suggestions for the method at the specified file and location.

@param filename [String] The file to analyze @param line [Integer] The zero-based line number @param column [Integer] The zero-based column number @return [Array<Solargraph::Pin::Base>] @todo Take filename/position instead of filename/line/column

# File lib/solargraph/library.rb, line 199
def signatures_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(read(filename), position)
  api_map.clip(cursor).signify
end
synchronized?() click to toggle source

True if the ApiMap is up to date with the library's workspace and open files.

@return [Boolean]

# File lib/solargraph/library.rb, line 37
def synchronized?
  @synchronized
end

Private Instance Methods

api_map() click to toggle source

@return [ApiMap]

# File lib/solargraph/library.rb, line 400
def api_map
  @api_map ||= Solargraph::ApiMap.new
end
bench() click to toggle source

@return [Bench]

# File lib/solargraph/library.rb, line 405
def bench
  Bench.new(
    workspace: workspace,
    opened: @current ? [@current] : []
  )
end
mutex() click to toggle source

@return [Mutex]

# File lib/solargraph/library.rb, line 395
def mutex
  @mutex ||= Mutex.new
end
read(filename) click to toggle source

Get the source for an open file or create a new source if the file exists on disk. Sources created from disk are not added to the open workspace files, i.e., the version on disk remains the authoritative version.

@raise [FileNotFoundError] if the file does not exist @param filename [String] @return [Solargraph::Source]

# File lib/solargraph/library.rb, line 420
def read filename
  return @current if @current && @current.filename == filename
  raise FileNotFoundError, "File not found: #{filename}" unless workspace.has_file?(filename)
  workspace.source(filename)
end