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


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)


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

def current
  @current
end

#nameString? (readonly)


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.


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.


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.


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.


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.


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).


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.


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.


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.


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.


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.


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.


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>


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.


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.


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.


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.


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.


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>


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.


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.


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>


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>


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.


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.


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

def synchronized?
  @synchronized
end