class Solargraph::Library
A Library
handles coordination between a Workspace
and an ApiMap
.
Attributes
@return [Source, nil]
@return [String, nil]
@return [Solargraph::Workspace]
Public Class Methods
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
@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 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
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
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 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
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
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 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 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
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 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 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
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
@param query [String] @return [Array<YARD::CodeObjects::Base>]
# File lib/solargraph/library.rb, line 262 def document query catalog api_map.document query end
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
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 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
# File lib/solargraph/library.rb, line 28 def inspect # Let's not deal with insane data dumps in spec failures to_s end
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
# File lib/solargraph/library.rb, line 248 def locate_ref location api_map.require_reference_at location end
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
@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
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
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
@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
@param query [String] @return [Array<String>]
# File lib/solargraph/library.rb, line 269 def search query catalog api_map.search query end
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
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
@return [ApiMap]
# File lib/solargraph/library.rb, line 400 def api_map @api_map ||= Solargraph::ApiMap.new end
@return [Bench]
# File lib/solargraph/library.rb, line 405 def bench Bench.new( workspace: workspace, opened: @current ? [@current] : [] ) end
@return [Mutex]
# File lib/solargraph/library.rb, line 395 def mutex @mutex ||= Mutex.new end
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