class Solargraph::Workspace

A workspace consists of the files in a project’s directory and the project’s configuration. It provides a Source for each file to be used in an associated Library or ApiMap.

Attributes

directory[R]

@return [String]

gemnames[R]

@return [Array<String>]

require_paths[R]

The require paths associated with the workspace.

@return [Array<String>]

source_gems[R]

@return [Array<String>]

Public Class Methods

new(directory = '', config = nil) click to toggle source

@param directory [String] @param config [Config, nil]

# File lib/solargraph/workspace.rb, line 28
def initialize directory = '', config = nil
  @directory = directory
  @config = config
  load_sources
  @gemnames = []
  @require_paths = generate_require_paths
  require_plugins
end

Public Instance Methods

config() click to toggle source

@return [Solargraph::Workspace::Config]

# File lib/solargraph/workspace.rb, line 38
def config
  @config ||= Solargraph::Workspace::Config.new(directory)
end
filenames() click to toggle source

@return [Array<String>]

# File lib/solargraph/workspace.rb, line 80
def filenames
  source_hash.keys
end
gemspec?() click to toggle source

True if the workspace contains at least one gemspec file.

@return [Boolean]

# File lib/solargraph/workspace.rb, line 117
def gemspec?
  !gemspecs.empty?
end
gemspecs() click to toggle source

Get an array of all gemspec files in the workspace.

@return [Array<String>]

# File lib/solargraph/workspace.rb, line 124
def gemspecs
  return [] if directory.empty? || directory == '*'
  @gemspecs ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs|
    config.allow? gs
  end
end
has_file?(filename) click to toggle source

@param filename [String] @return [Boolean]

# File lib/solargraph/workspace.rb, line 91
def has_file? filename
  source_hash.key?(filename)
end
merge(source) click to toggle source

Merge the source. A merge will update the existing source for the file or add it to the sources if the workspace is configured to include it. The source is ignored if the configuration excludes it.

@param source [Solargraph::Source] @return [Boolean] True if the source was added to the workspace

# File lib/solargraph/workspace.rb, line 48
def merge source
  unless directory == '*' || source_hash.key?(source.filename)
    # Reload the config to determine if a new source should be included
    @config = Solargraph::Workspace::Config.new(directory)
    return false unless config.calculated.include?(source.filename)
  end
  source_hash[source.filename] = source
  true
end
remove(filename) click to toggle source

Remove a source from the workspace. The source will not be removed if its file exists and the workspace is configured to include it.

@param filename [String] @return [Boolean] True if the source was removed from the workspace

# File lib/solargraph/workspace.rb, line 73
def remove filename
  return false unless source_hash.key?(filename)
  source_hash.delete filename
  true
end
source(filename) click to toggle source

Get a source by its filename.

@param filename [String] @return [Solargraph::Source]

# File lib/solargraph/workspace.rb, line 99
def source filename
  source_hash[filename]
end
sources() click to toggle source

@return [Array<Solargraph::Source>]

# File lib/solargraph/workspace.rb, line 85
def sources
  source_hash.values
end
synchronize!(updater) click to toggle source

Synchronize the workspace from the provided updater.

@param updater [Source::Updater] @return [void]

# File lib/solargraph/workspace.rb, line 135
def synchronize! updater
  source_hash[updater.filename] = source_hash[updater.filename].synchronize(updater)
end
would_merge?(filename) click to toggle source

Determine whether a file would be merged into the workspace.

@param filename [String] @return [Boolean]

# File lib/solargraph/workspace.rb, line 62
def would_merge? filename
  return true if directory == '*' || source_hash.include?(filename)
  @config = Solargraph::Workspace::Config.new(directory)
  config.calculated.include?(filename)
end
would_require?(path) click to toggle source

True if the path resolves to a file in the workspace’s require paths.

@param path [String] @return [Boolean]

# File lib/solargraph/workspace.rb, line 107
def would_require? path
  require_paths.each do |rp|
    return true if File.exist?(File.join(rp, "#{path}.rb"))
  end
  false
end

Private Instance Methods

configured_require_paths() click to toggle source

Get additional require paths defined in the configuration.

@return [Array<String>]

# File lib/solargraph/workspace.rb, line 198
def configured_require_paths
  return ['lib'] if directory.empty?
  return [File.join(directory, 'lib')] if config.require_paths.empty?
  config.require_paths.map{|p| File.join(directory, p)}
end
generate_require_paths() click to toggle source

Generate require paths from gemspecs if they exist or assume the default lib directory.

@return [Array<String>]

# File lib/solargraph/workspace.rb, line 166
def generate_require_paths
  return configured_require_paths unless gemspec?
  result = []
  gemspecs.each do |file|
    base = File.dirname(file)
    # HACK: Evaluating gemspec files violates the goal of not running
    #   workspace code, but this is how Gem::Specification.load does it
    #   anyway.
    cmd = ['ruby', '-e', "require 'rubygems'; require 'json'; spec = eval(File.read('#{file}'), TOPLEVEL_BINDING, '#{file}'); return unless Gem::Specification === spec; puts({name: spec.name, paths: spec.require_paths}.to_json)"]
    o, e, s = Open3.capture3(*cmd)
    if s.success?
      begin
        hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
        next if hash.empty?
        @gemnames.push hash['name']
        result.concat(hash['paths'].map { |path| File.join(base, path) })
      rescue StandardError => e
        Solargraph.logger.warn "Error reading #{file}: [#{e.class}] #{e.message}"
      end
    else
      Solargraph.logger.warn "Error reading #{file}"
      Solargraph.logger.warn e
    end
  end
  result.concat(config.require_paths.map { |p| File.join(directory, p) })
  result.push File.join(directory, 'lib') if result.empty?
  result
end
load_sources() click to toggle source

@return [void]

# File lib/solargraph/workspace.rb, line 147
def load_sources
  source_hash.clear
  unless directory.empty? || directory == '*'
    size = config.calculated.length
    raise WorkspaceTooLargeError, "The workspace is too large to index (#{size} files, #{config.max_files} max)" if config.max_files > 0 and size > config.max_files
    config.calculated.each do |filename|
      begin
        source_hash[filename] = Solargraph::Source.load(filename)
      rescue Errno::ENOENT => e
        Solargraph.logger.warn("Error loading #{filename}: [#{e.class}] #{e.message}")
      end
    end
  end
end
require_plugins() click to toggle source
# File lib/solargraph/workspace.rb, line 204
def require_plugins
  config.plugins.each do |plugin|
    begin
      require plugin
    rescue LoadError
      Solargraph.logger.warn "Failed to load plugin '#{plugin}'"
    end
  end
end
source_hash() click to toggle source

@return [Hash{String => Solargraph::Source}]

# File lib/solargraph/workspace.rb, line 142
def source_hash
  @source_hash ||= {}
end