Class: Solargraph::Library

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

Overview

A Library handles coordination between a Workspace and an ApiMap.

Constant Summary

Constants included from Logging

Solargraph::Logging::DEFAULT_LOG_LEVEL, Solargraph::Logging::LOG_LEVELS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

logger

Constructor Details

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

Returns a new instance of Library

Parameters:

  • workspace (Solargraph::Workspace) (defaults to: Solargraph::Workspace.new)
  • name (String, nil) (defaults to: nil)

18
19
20
21
22
23
24
# File 'lib/solargraph/library.rb', line 18

def initialize workspace = Solargraph::Workspace.new, name = nil
  @workspace = workspace
  @name = name
  api_map.catalog bundle
  @synchronized = true
  @catalog_mutex = Mutex.new
end

Instance Attribute Details

#currentSource? (readonly)

Returns:


14
15
16
# File 'lib/solargraph/library.rb', line 14

def current
  @current
end

#nameString? (readonly)

Returns:

  • (String, nil)

11
12
13
# File 'lib/solargraph/library.rb', line 11

def name
  @name
end

#workspaceSolargraph::Workspace (readonly)


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

def workspace
  @workspace
end

Class Method Details

.load(directory = '', name = nil) ⇒ Solargraph::Library

Create a library from a directory.

Parameters:

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

    The path to be used for the workspace

  • name (String, nil) (defaults to: nil)

Returns:


360
361
362
# File 'lib/solargraph/library.rb', line 360

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

Instance Method Details

#attach(source) ⇒ void

This method returns an undefined value.

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.

Parameters:


47
48
49
50
51
52
53
# File 'lib/solargraph/library.rb', line 47

def attach source
  mutex.synchronize do
    @synchronized = (@current == source) if synchronized?
    @current = source
    catalog
  end
end

#attached?(filename) ⇒ Boolean Also known as: open?

True if the specified file is currently attached.

Parameters:

  • filename (String)

Returns:

  • (Boolean)

59
60
61
# File 'lib/solargraph/library.rb', line 59

def attached? filename
  !@current.nil? && @current.filename == filename
end

#catalogvoid

This method returns an undefined value.

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


334
335
336
337
338
339
340
341
342
# File 'lib/solargraph/library.rb', line 334

def catalog
  @catalog_mutex.synchronize do
    break if synchronized?
    logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
    api_map.catalog bundle
    @synchronized = true
    logger.info "Catalog complete (#{api_map.pins.length} pins)"
  end
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)

140
141
142
143
144
145
146
# File 'lib/solargraph/library.rb', line 140

def close filename
  mutex.synchronize do
    @synchronized = false
    @current = nil if @current && @current.filename == 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:


155
156
157
158
159
# File 'lib/solargraph/library.rb', line 155

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) ⇒ Boolean

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

Parameters:

  • filename (String)

Returns:

  • (Boolean)

79
80
81
# File 'lib/solargraph/library.rb', line 79

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 it is neither open in the library nor included in the workspace.

Parameters:

  • filename (String)
  • text (String)

    The contents of the file

Returns:

  • (Boolean)

    True if the file was added to the workspace.


89
90
91
92
93
94
95
96
97
98
99
# File 'lib/solargraph/library.rb', line 89

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) ⇒ Boolean

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.

Parameters:

  • filename (String)

Returns:

  • (Boolean)

    True if the file was added to the workspace.


106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/solargraph/library.rb', line 106

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) ⇒ 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:


169
170
171
172
173
# File 'lib/solargraph/library.rb', line 169

def definitions_at filename, line, column
  position = Position.new(line, column)
  cursor = Source::Cursor.new(read(filename), position)
  api_map.clip(cursor).define.map { |pin| pin.realize(api_map) }
end

#delete(filename) ⇒ Boolean

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)

Returns:

  • (Boolean)

    True if the file was deleted


125
126
127
128
129
130
131
132
133
# File 'lib/solargraph/library.rb', line 125

def delete filename
  detach filename
  result = false
  mutex.synchronize do
    result = workspace.remove(filename)
    @synchronized = !result if synchronized?
  end
  result
end

#detach(filename) ⇒ Boolean

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

Parameters:

  • filename (String)

Returns:

  • (Boolean)

    True if the specified file was detached


68
69
70
71
72
# File 'lib/solargraph/library.rb', line 68

def detach filename
  return false if @current.nil? || @current.filename != filename
  attach nil
  true
end

#diagnose(filename) ⇒ Array<Hash>

Get diagnostics about a file.

Parameters:

  • filename (String)

Returns:

  • (Array<Hash>)

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/solargraph/library.rb', line 301

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) ⇒ Array<YARD::CodeObject::Base>

Parameters:

  • query (String)

Returns:

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

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

def document query
  catalog
  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:


276
277
278
279
# File 'lib/solargraph/library.rb', line 276

def document_symbols filename
  # checkout filename
  api_map.document_symbols(filename)
end

#folding_ranges(filename) ⇒ Array<Range>

Deprecated.

The library should not need to handle folding ranges. The source itself has all the information it needs.

Get an array of foldable ranges for the specified file.

Parameters:

  • filename (String)

Returns:


351
352
353
# File 'lib/solargraph/library.rb', line 351

def folding_ranges filename
  read(filename).folding_ranges
end

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

Get an array of pins that match a path.

Parameters:

  • path (String)

Returns:


241
242
243
# File 'lib/solargraph/library.rb', line 241

def get_path_pins path
  api_map.get_path_suggestions(path)
end

#inspectObject


26
27
28
29
# File 'lib/solargraph/library.rb', line 26

def inspect
  # Let's not deal with insane data dumps in spec failures
  to_s
end

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

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

Parameters:

Returns:


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

def locate_pins location
  api_map.locate_pins(location).map { |pin| pin.realize(api_map) }
end

#locate_ref(location) ⇒ Object


233
234
235
# File 'lib/solargraph/library.rb', line 233

def locate_ref location
  api_map.require_reference_at location
end

#merge(source) ⇒ Boolean

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

Parameters:

Returns:

  • (Boolean)

    True if the source was merged into the workspace.


369
370
371
372
373
374
375
376
# File 'lib/solargraph/library.rb', line 369

def merge source
  result = nil
  mutex.synchronize do
    result = workspace.merge(source)
    @synchronized = !result if synchronized?
  end
  result
end

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

Parameters:

  • path (String)

Returns:


283
284
285
286
# File 'lib/solargraph/library.rb', line 283

def path_pins path
  catalog
  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:


263
264
265
266
# File 'lib/solargraph/library.rb', line 263

def query_symbols query
  catalog
  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)

292
293
294
295
# File 'lib/solargraph/library.rb', line 292

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)

    Strip special characters from variable names

Returns:


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/solargraph/library.rb', line 195

def references_from filename, line, column, strip: false
  # checkout filename
  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

#search(query) ⇒ Array<String>

Parameters:

  • query (String)

Returns:

  • (Array<String>)

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

def search query
  catalog
  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:


183
184
185
186
187
# File 'lib/solargraph/library.rb', line 183

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?Boolean

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

Returns:

  • (Boolean)

35
36
37
# File 'lib/solargraph/library.rb', line 35

def synchronized?
  @synchronized
end