Class: Solargraph::Library

Inherits:
Object
  • Object
show all
Defined in:
lib/solargraph/library.rb

Overview

A Library handles coordination between a Workspace and an ApiMap.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(workspace = Solargraph::Workspace.new) ⇒ Library

Returns a new instance of Library.

Parameters:



6
7
8
9
10
11
# File 'lib/solargraph/library.rb', line 6

def initialize workspace = Solargraph::Workspace.new
  @mutex = Mutex.new
  @workspace = workspace
  api_map.catalog bundle
  @synchronized = true
end

Class Method Details

.load(directory = '') ⇒ Solargraph::Library

Create a library from a directory.

Parameters:

  • directory (String) (defaults to: '')

    The path to be used for the workspace

Returns:



321
322
323
# File 'lib/solargraph/library.rb', line 321

def self.load directory = ''
  Solargraph::Library.new(Solargraph::Workspace.new(directory))
end

Instance Method Details

#catalogvoid

This method returns an undefined value.

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



312
313
314
315
# File 'lib/solargraph/library.rb', line 312

def catalog
  api_map.catalog bundle
  @synchronized = true
end

#checkout(filename) ⇒ Source

Check a file out of the library. If the file is not part of the workspace, the ApiMap will virtualize it for mapping purposes. If filename is nil, any source currently checked out of the library will be removed from the ApiMap. Only one file can be checked out (virtualized) at a time.

Parameters:

  • filename (String)

Returns:

Raises:



216
217
218
# File 'lib/solargraph/library.rb', line 216

def checkout filename
  read filename
end

#close(filename) ⇒ void

This method returns an undefined value.

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

Parameters:

  • filename (String)


109
110
111
112
113
114
# File 'lib/solargraph/library.rb', line 109

def close filename
  mutex.synchronize do
    open_file_hash.delete filename
    catalog
  end
end

#completions_at(filename, line, column) ⇒ SourceMap::Completion

TODO:

Take a Location instead of filename/line/column

Get completion suggestions at the specified file and location.

Parameters:

  • filename (String)

    The file to analyze

  • line (Integer)

    The zero-based line number

  • column (Integer)

    The zero-based column number

Returns:



123
124
125
126
127
# File 'lib/solargraph/library.rb', line 123

def completions_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(checkout(filename), position)
  api_map.clip(cursor).complete
end

#contain?(filename) ⇒ Boolean

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

Parameters:

  • filename (String)

Returns:

  • (Boolean)


50
51
52
# File 'lib/solargraph/library.rb', line 50

def contain? filename
  workspace.has_file?(filename)
end

#create(filename, text) ⇒ Boolean

Create a source to be added to the workspace. The file is ignored if the workspace is not configured to include the file.

Parameters:

  • filename (String)
  • text (String)

    The contents of the file

Returns:

  • (Boolean)

    True if the file was added to the workspace.



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/solargraph/library.rb', line 60

def create filename, text
  result = false
  mutex.synchronize do
    next unless workspace.would_merge?(filename)
    source = Solargraph::Source.load_string(text, filename)
    workspace.merge(source)
    catalog #unless api_map.try_merge!(source)
    result = true
  end
  result
end

#create_from_disk(filename) ⇒ Boolean

Create a file source from a file on disk. The file is ignored if the workspace is not configured to include the file.

Parameters:

  • filename (String)

Returns:

  • (Boolean)

    True if the file was added to the workspace.



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/solargraph/library.rb', line 77

def create_from_disk filename
  result = false
  mutex.synchronize do
    next if File.directory?(filename) or !File.exist?(filename)
    next unless workspace.would_merge?(filename)
    source = Solargraph::Source.load_string(File.read(filename), filename)
    workspace.merge(source)
    catalog #unless api_map.try_merge!(source)
    result = true
  end
  result
end

#definitions_at(filename, line, column) ⇒ Array<Solargraph::Pin::Base>

TODO:

Take filename/position instead of filename/line/column

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

Parameters:

  • filename (String)

    The file to analyze

  • line (Integer)

    The zero-based line number

  • column (Integer)

    The zero-based column number

Returns:



137
138
139
140
141
# File 'lib/solargraph/library.rb', line 137

def definitions_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(checkout(filename), position)
  api_map.clip(cursor).define
end

#delete(filename) ⇒ void

This method returns an undefined value.

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.

Parameters:

  • filename (String)


96
97
98
99
100
101
102
# File 'lib/solargraph/library.rb', line 96

def delete filename
  mutex.synchronize do
    open_file_hash.delete filename
    workspace.remove filename
    catalog
  end
end

#diagnose(filename) ⇒ Array<Hash>

Get diagnostics about a file.

Parameters:

  • filename (String)

Returns:

  • (Array<Hash>)


294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/solargraph/library.rb', line 294

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)
  result = []
  source = read(filename)
  workspace.config.reporters.each do |name|
    reporter = Diagnostics.reporter(name)
    raise DiagnosticsError, "Diagnostics reporter #{name} does not exist" if reporter.nil?
    result.concat reporter.new.diagnose(source, api_map)
  end
  result
end

#document(query) ⇒ Array<YARD::CodeObject::Base>

Parameters:

  • query (String)

Returns:

  • (Array<YARD::CodeObject::Base>)


222
223
224
# File 'lib/solargraph/library.rb', line 222

def document query
  api_map.document query
end

#document_symbols(filename) ⇒ Array<Solargraph::Pin::Base>

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.

Parameters:

  • filename (String)

Returns:



248
249
250
251
# File 'lib/solargraph/library.rb', line 248

def document_symbols filename
  return [] unless open_file_hash.has_key?(filename)
  api_map.document_symbols(filename)
end

#get_path_pins(path) ⇒ Array<Solargraph::Pin::Base>

Get an array of pins that match a path.

Parameters:

  • path (String)

Returns:



202
203
204
# File 'lib/solargraph/library.rb', line 202

def get_path_pins path
  api_map.get_path_suggestions(path)
end

#locate_pin(location) ⇒ Solargraph::Pin::Base

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

Parameters:

Returns:



194
195
196
# File 'lib/solargraph/library.rb', line 194

def locate_pin location
  api_map.locate_pin location
end

#open(filename, text, version) ⇒ void

This method returns an undefined value.

Open a file in the library. Opening a file will make it available for checkout and merge it into the workspace if applicable.

Parameters:

  • filename (String)
  • text (String)
  • version (Integer)


28
29
30
31
32
33
34
35
# File 'lib/solargraph/library.rb', line 28

def open filename, text, version
  mutex.synchronize do
    source = Solargraph::Source.load_string(text, filename, version)
    workspace.merge source
    open_file_hash[filename] = source
    catalog #unless api_map.try_merge!(source)
  end
end

#open?(filename) ⇒ Boolean

True if the specified file is currently open.

Parameters:

  • filename (String)

Returns:

  • (Boolean)


41
42
43
# File 'lib/solargraph/library.rb', line 41

def open? filename
  open_file_hash.has_key? filename
end

#path_pins(path) ⇒ Array<Solargraph::Pin::Base>

Parameters:

  • path (String)

Returns:



255
256
257
# File 'lib/solargraph/library.rb', line 255

def path_pins path
  api_map.get_path_suggestions(path)
end

#query_symbols(query) ⇒ Array<Pin::Base>

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

Parameters:

  • query (String)

Returns:



236
237
238
# File 'lib/solargraph/library.rb', line 236

def query_symbols query
  api_map.query_symbols query
end

#read_text(filename) ⇒ String

Get the current text of a file in the library.

Parameters:

  • filename (String)

Returns:

  • (String)


285
286
287
288
# File 'lib/solargraph/library.rb', line 285

def read_text filename
  source = read(filename)
  source.code
end

#references_from(filename, line, column, strip: false) ⇒ Array<Solargraph::Range>

TODO:

Take a Location instead of filename/line/column

Parameters:

  • filename (String)
  • line (Integer)
  • column (Integer)
  • strip (Boolean) (defaults to: false)

    Strip special characters from variable names

Returns:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/solargraph/library.rb', line 163

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 + open_file_hash.values).uniq.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)
        referenced.any?{|r| r == pin}
      end
      # HACK for language clients that exclude special characters from the start of variable names
      if strip && match = cursor.word.match(/^[^a-z0-9_]+/)
        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{ |a, b|
        a.range.start.line <=> b.range.start.line
      })
    end
  end
  result
end

#search(query) ⇒ Array<String>

Parameters:

  • query (String)

Returns:

  • (Array<String>)


228
229
230
# File 'lib/solargraph/library.rb', line 228

def search query
  api_map.search query
end

#signatures_at(filename, line, column) ⇒ Array<Solargraph::Pin::Base>

TODO:

Take filename/position instead of filename/line/column

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

Parameters:

  • filename (String)

    The file to analyze

  • line (Integer)

    The zero-based line number

  • column (Integer)

    The zero-based column number

Returns:



151
152
153
154
155
# File 'lib/solargraph/library.rb', line 151

def signatures_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(checkout(filename), position)
  api_map.clip(cursor).signify
end

#synchronized?Boolean

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

Returns:

  • (Boolean)


17
18
19
# File 'lib/solargraph/library.rb', line 17

def synchronized?
  @synchronized
end

#update(updater) ⇒ void

Note:

This method will not update the library’s ApiMap. See Library#ynchronized? and Library#catalog for more information.

This method returns an undefined value.

Update a source in the library from the provided updater.

Parameters:

Raises:



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/solargraph/library.rb', line 268

def update updater
  mutex.synchronize do
    if workspace.has_file?(updater.filename)
      workspace.synchronize!(updater)
      open_file_hash[updater.filename] = workspace.source(updater.filename) if open?(updater.filename)
    else
      raise FileNotFoundError, "Unable to update #{updater.filename}" unless open?(updater.filename)
      open_file_hash[updater.filename] = open_file_hash[updater.filename].synchronize(updater)
    end
    @synchronized = false
  end
end