solargraph 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +33 -28
  3. data/lib/solargraph/api_map.rb +997 -1044
  4. data/lib/solargraph/api_map/source_to_yard.rb +4 -3
  5. data/lib/solargraph/diagnostics/rubocop.rb +4 -3
  6. data/lib/solargraph/language_server/host.rb +140 -70
  7. data/lib/solargraph/language_server/message/base.rb +1 -0
  8. data/lib/solargraph/language_server/message/client.rb +6 -2
  9. data/lib/solargraph/language_server/message/text_document/completion.rb +34 -39
  10. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  11. data/lib/solargraph/language_server/message/text_document/did_close.rb +1 -0
  12. data/lib/solargraph/language_server/message/text_document/did_save.rb +1 -3
  13. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
  14. data/lib/solargraph/language_server/message/text_document/hover.rb +25 -30
  15. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +1 -1
  16. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +8 -7
  17. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +1 -1
  18. data/lib/solargraph/language_server/transport/socket.rb +15 -17
  19. data/lib/solargraph/library.rb +34 -16
  20. data/lib/solargraph/node_methods.rb +96 -96
  21. data/lib/solargraph/pin.rb +1 -0
  22. data/lib/solargraph/pin/base.rb +2 -1
  23. data/lib/solargraph/pin/base_variable.rb +45 -5
  24. data/lib/solargraph/pin/block_parameter.rb +5 -2
  25. data/lib/solargraph/pin/method.rb +22 -0
  26. data/lib/solargraph/pin/namespace.rb +32 -2
  27. data/lib/solargraph/pin/reference.rb +21 -0
  28. data/lib/solargraph/pin/yard_object.rb +9 -0
  29. data/lib/solargraph/shell.rb +136 -136
  30. data/lib/solargraph/source.rb +134 -188
  31. data/lib/solargraph/source/change.rb +70 -0
  32. data/lib/solargraph/source/fragment.rb +120 -66
  33. data/lib/solargraph/source/position.rb +41 -0
  34. data/lib/solargraph/source/updater.rb +48 -0
  35. data/lib/solargraph/version.rb +3 -3
  36. data/lib/solargraph/workspace/config.rb +4 -9
  37. data/lib/solargraph/yard_map/core_docs.rb +0 -1
  38. metadata +5 -2
@@ -10,7 +10,7 @@ module Solargraph::LanguageServer::Message::TextDocument
10
10
  filename = uri_to_file(params['textDocument']['uri'])
11
11
  line = params['position']['line']
12
12
  col = params['position']['character']
13
- suggestions = host.library.definitions_at(filename, line, col)
13
+ suggestions = host.definitions_at(filename, line, col)
14
14
  locations = suggestions.map do |pin|
15
15
  unless pin.location.nil?
16
16
  parts = pin.location.split(':')
@@ -4,6 +4,7 @@ module Solargraph
4
4
  module TextDocument
5
5
  class DidClose < Base
6
6
  def process
7
+ host.close params['textDocument']['uri']
7
8
  end
8
9
  end
9
10
  end
@@ -4,9 +4,7 @@ module Solargraph
4
4
  module TextDocument
5
5
  class DidSave < Base
6
6
  def process
7
- STDERR.puts "TODO: TextDocument saved"
8
- # host.open params['textDocument']
9
- # publish_diagnostics
7
+ host.save params
10
8
  end
11
9
  end
12
10
  end
@@ -2,7 +2,7 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
2
2
  include Solargraph::LanguageServer::UriHelpers
3
3
 
4
4
  def process
5
- pins = host.library.file_symbols(uri_to_file(params['textDocument']['uri']))
5
+ pins = host.file_symbols params['textDocument']['uri']
6
6
  info = pins.map do |pin|
7
7
  parts = pin.location.split(':')
8
8
  char = parts.pop.to_i
@@ -3,38 +3,33 @@ require 'uri'
3
3
  module Solargraph::LanguageServer::Message::TextDocument
4
4
  class Hover < Base
5
5
  def process
6
- begin
7
- filename = uri_to_file(params['textDocument']['uri'])
8
- line = params['position']['line']
9
- col = params['position']['character']
10
- suggestions = host.definitions_at(filename, line, col)
11
- # contents = suggestions.map(&:hover)
12
- contents = []
13
- last_path = nil
14
- suggestions.each do |pin|
15
- parts = []
16
- this_path = nil
17
- if pin.kind_of?(Solargraph::Pin::BaseVariable)
18
- this_path = pin.return_type
19
- else
20
- this_path = pin.path
21
- end
22
- if !this_path.nil? and this_path != last_path
23
- parts.push link_documentation(this_path)
24
- end
25
- parts.push pin.documentation unless pin.documentation.nil? or pin.documentation.empty?
26
- contents.push parts.join("\n\n") unless parts.empty?
27
- last_path = this_path unless this_path.nil?
6
+ filename = uri_to_file(params['textDocument']['uri'])
7
+ line = params['position']['line']
8
+ col = params['position']['character']
9
+ contents = []
10
+ suggestions = host.definitions_at(filename, line, col)
11
+ last_path = nil
12
+ suggestions.each do |pin|
13
+ parts = []
14
+ this_path = nil
15
+ if pin.kind_of?(Solargraph::Pin::BaseVariable)
16
+ this_path = pin.return_type
17
+ else
18
+ this_path = pin.path
28
19
  end
29
- set_result(
30
- contents: {
31
- kind: 'markdown',
32
- value: contents.join("\n\n")
33
- }
34
- )
35
- rescue Exception => e
36
- set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, e.message
20
+ if !this_path.nil? and this_path != last_path
21
+ parts.push link_documentation(this_path)
22
+ end
23
+ parts.push pin.documentation unless pin.documentation.nil? or pin.documentation.empty?
24
+ contents.push parts.join("\n\n") unless parts.empty?
25
+ last_path = this_path unless this_path.nil?
37
26
  end
27
+ set_result(
28
+ contents: {
29
+ kind: 'markdown',
30
+ value: contents.join("\n\n")
31
+ }
32
+ )
38
33
  end
39
34
 
40
35
  private
@@ -5,7 +5,7 @@ module Solargraph
5
5
  class OnTypeFormatting < Base
6
6
  def process
7
7
  # @todo Temporarily disabled
8
- set_result []
8
+ set_result nil
9
9
  return
10
10
  src = host.library.checkout(uri_to_file(params['textDocument']['uri']))
11
11
  offset = src.get_offset(params['position']['line'], params['position']['character'])
@@ -9,20 +9,21 @@ module Solargraph::LanguageServer::Message::Workspace
9
9
  include Solargraph::LanguageServer::UriHelpers
10
10
 
11
11
  def process
12
+ # @param change [Hash]
12
13
  params['changes'].each do |change|
13
14
  if change['type'] == CREATED
14
- STDERR.puts "TODO: Need to handle a created file?"
15
- # host.create change['uri']
15
+ # It's only necessary to create the file from if the file isn't open
16
+ # in the client
17
+ host.create change['uri'] unless host.open?(change['uri'])
16
18
  elsif change['type'] == CHANGED
17
- # @todo Should this check if the source is already loaded in the source?
18
- # Possibly out of sync with the disk?
19
- # host.workspace.handle_changed filename
20
- # host.api_map.refresh
21
- STDERR.puts "TODO: Workspace changed"
19
+ # It's only necessary to update from here if the file isn't open in
20
+ # the client
21
+ host.create change['uri'] unless host.open?(change['uri'])
22
22
  elsif change['type'] == DELETED
23
23
  host.delete change['uri']
24
24
  else
25
25
  # @todo Handle error
26
+ set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}"
26
27
  end
27
28
  end
28
29
  end
@@ -2,7 +2,7 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra
2
2
  include Solargraph::LanguageServer::UriHelpers
3
3
 
4
4
  def process
5
- pins = host.library.query_symbols(params['query'])
5
+ pins = host.query_symbols(params['query'])
6
6
  info = pins.map do |pin|
7
7
  parts = pin.location.split(':')
8
8
  char = parts.pop.to_i
@@ -11,30 +11,19 @@ module Solargraph
11
11
  @content_length = 0
12
12
  @buffer = ''
13
13
  @host = Solargraph::LanguageServer::Host.new
14
- EventMachine.add_periodic_timer 0.1 do
15
- tmp = @host.flush
16
- send_data tmp unless tmp.empty?
17
- EventMachine.stop if @host.stopped?
18
- end
19
- @message_semaphore = Mutex.new
20
- @message_stack = 0
14
+ start_timers
21
15
  end
22
-
16
+
23
17
  def process request
24
18
  Thread.new do
25
- @message_semaphore.synchronize do
26
- @message_stack += 1
27
- end
28
19
  message = @host.start(request)
29
20
  message.send
30
21
  tmp = @host.flush
31
22
  send_data tmp unless tmp.empty?
32
- @message_semaphore.synchronize do
33
- @message_stack -= 1
34
- end
23
+ GC.start unless request['method'] == 'textDocument/didChange'
35
24
  end
36
25
  end
37
-
26
+
38
27
  # @param data [String]
39
28
  def receive_data data
40
29
  data.each_char do |char|
@@ -55,9 +44,8 @@ module Solargraph
55
44
  if @buffer.bytesize == @content_length
56
45
  begin
57
46
  process JSON.parse(@buffer)
58
- rescue Exception => e
47
+ rescue JSON::ParserError => e
59
48
  STDERR.puts "Failed to parse request: #{e.message}"
60
- STDERR.puts e.backtrace.inspect
61
49
  STDERR.puts "Buffer: #{@buffer}"
62
50
  ensure
63
51
  @buffer.clear
@@ -68,6 +56,16 @@ module Solargraph
68
56
  end
69
57
  end
70
58
  end
59
+
60
+ private
61
+
62
+ def start_timers
63
+ EventMachine.add_periodic_timer 0.1 do
64
+ tmp = @host.flush
65
+ send_data tmp unless tmp.empty?
66
+ EventMachine.stop if @host.stopped?
67
+ end
68
+ end
71
69
  end
72
70
  end
73
71
  end
@@ -2,7 +2,6 @@ module Solargraph
2
2
  # A library handles coordination between a Workspace and an ApiMap.
3
3
  #
4
4
  class Library
5
- class FileNotFoundError < Exception; end
6
5
 
7
6
  # @param workspace [Solargraph::Workspace]
8
7
  def initialize workspace = Solargraph::Workspace.new(nil)
@@ -24,6 +23,14 @@ module Solargraph
24
23
  api_map.refresh
25
24
  end
26
25
 
26
+ # True if the specified file is currently open in the workspace.
27
+ #
28
+ # @param filename [String]
29
+ # @return [Boolean]
30
+ def open? filename
31
+ source_hash.has_key? filename
32
+ end
33
+
27
34
  # Create a file source to be added to the workspace. The source is ignored
28
35
  # if the workspace is not configured to include the file.
29
36
  #
@@ -31,16 +38,11 @@ module Solargraph
31
38
  # @param text [String] The contents of the file
32
39
  # @return [Boolean] True if the file was added to the workspace.
33
40
  def create filename, text
34
- result = false
35
- if workspace.would_merge?(filename)
36
- source = Solargraph::Source.load_string(text, filename)
37
- if workspace.merge(source)
38
- source_hash[filename] = source
39
- api_map.refresh
40
- result = true
41
- end
42
- end
43
- result
41
+ return false unless workspace.would_merge?(filename)
42
+ source = Solargraph::Source.load_string(text, filename)
43
+ workspace.merge(source)
44
+ api_map.refresh
45
+ true
44
46
  end
45
47
 
46
48
  # Delete a file from the library. Deleting a file will make it unavailable
@@ -62,6 +64,17 @@ module Solargraph
62
64
  # @param filename [String]
63
65
  def close filename
64
66
  source_hash.delete filename
67
+ if workspace.has_file?(filename)
68
+ source = Solargraph::Source.load(filename)
69
+ workspace.merge source
70
+ end
71
+ end
72
+
73
+ def overwrite filename, version
74
+ source = source_hash[filename]
75
+ return if source.nil?
76
+ STDERR.puts "Save out of sync for #{filename}" if source.version > version
77
+ open filename, File.read(filename), version
65
78
  end
66
79
 
67
80
  # Get completion suggestions at the specified file and location.
@@ -71,9 +84,8 @@ module Solargraph
71
84
  # @param column [Integer] The zero-based column number
72
85
  # @return [ApiMap::Completion]
73
86
  def completions_at filename, line, column
74
- # @type [Solargraph::Source]
75
- source = nil
76
87
  source = read(filename)
88
+ api_map.virtualize source
77
89
  fragment = source.fragment_at(line, column)
78
90
  api_map.complete(fragment)
79
91
  end
@@ -87,9 +99,9 @@ module Solargraph
87
99
  # @return [Array<Solargraph::Pin::Base>]
88
100
  def definitions_at filename, line, column
89
101
  source = read(filename)
102
+ api_map.virtualize source
90
103
  fragment = source.fragment_at(line, column)
91
- result = api_map.define(fragment)
92
- result
104
+ api_map.define(fragment)
93
105
  end
94
106
 
95
107
  # Get signature suggestions for the method at the specified file and
@@ -101,6 +113,7 @@ module Solargraph
101
113
  # @return [Array<Solargraph::Pin::Base>]
102
114
  def signatures_at filename, line, column
103
115
  source = read(filename)
116
+ api_map.virtualize source
104
117
  fragment = source.fragment_at(line, column)
105
118
  api_map.signify(fragment)
106
119
  end
@@ -163,6 +176,11 @@ module Solargraph
163
176
  api_map.get_path_suggestions(path)
164
177
  end
165
178
 
179
+ def synchronize updater
180
+ source = read(updater.filename)
181
+ source.synchronize updater
182
+ end
183
+
166
184
  # Get the current text of a file in the library.
167
185
  #
168
186
  # @param filename [String]
@@ -202,7 +220,7 @@ module Solargraph
202
220
  def read filename
203
221
  source = source_hash[filename]
204
222
  raise FileNotFoundError, "Source not found for #{filename}" if source.nil?
205
- api_map.virtualize source
223
+ # api_map.virtualize source
206
224
  source
207
225
  end
208
226
  end
@@ -1,96 +1,96 @@
1
- module Solargraph
2
- module NodeMethods
3
- # @return [String]
4
- def unpack_name(node)
5
- pack_name(node).join("::")
6
- end
7
-
8
- # @return [Array<String>]
9
- def pack_name(node)
10
- parts = []
11
- if node.kind_of?(AST::Node)
12
- node.children.each { |n|
13
- if n.kind_of?(AST::Node)
14
- if n.type == :cbase
15
- parts = pack_name(n)
16
- else
17
- parts += pack_name(n)
18
- end
19
- else
20
- parts.push n unless n.nil?
21
- end
22
- }
23
- end
24
- parts
25
- end
26
-
27
- # @return [String]
28
- def const_from node
29
- if node.kind_of?(AST::Node) and node.type == :const
30
- result = ''
31
- unless node.children[0].nil?
32
- result = const_from(node.children[0])
33
- end
34
- if result == ''
35
- result = node.children[1].to_s
36
- else
37
- result = result + '::' + node.children[1].to_s
38
- end
39
- result
40
- else
41
- nil
42
- end
43
- end
44
-
45
- # @return [String]
46
- def infer_literal_node_type node
47
- return nil unless node.kind_of?(AST::Node)
48
- if node.type == :str or node.type == :dstr
49
- return 'String'
50
- elsif node.type == :array
51
- return 'Array'
52
- elsif node.type == :hash
53
- return 'Hash'
54
- elsif node.type == :int
55
- return 'Integer'
56
- elsif node.type == :float
57
- return 'Float'
58
- elsif node.type == :sym
59
- return 'Symbol'
60
- end
61
- nil
62
- end
63
-
64
- # Get a call signature from a node.
65
- # The result should be a string in the form of a method path, e.g.,
66
- # String.new or variable.method.
67
- #
68
- # @return [String]
69
- def resolve_node_signature node
70
- drill_signature node, ''
71
- end
72
-
73
- private
74
-
75
- def drill_signature node, signature
76
- return signature unless node.kind_of?(AST::Node)
77
- if node.type == :const or node.type == :cbase
78
- unless node.children[0].nil?
79
- signature += drill_signature(node.children[0], signature)
80
- end
81
- signature += '::' unless signature.empty?
82
- signature += node.children[1].to_s
83
- elsif node.type == :lvar or node.type == :ivar or node.type == :cvar
84
- signature += '.' unless signature.empty?
85
- signature += node.children[0].to_s
86
- elsif node.type == :send
87
- unless node.children[0].nil?
88
- signature += drill_signature(node.children[0], signature)
89
- end
90
- signature += '.' unless signature.empty?
91
- signature += node.children[1].to_s
92
- end
93
- signature
94
- end
95
- end
96
- end
1
+ module Solargraph
2
+ module NodeMethods
3
+ # @return [String]
4
+ def unpack_name(node)
5
+ pack_name(node).join("::")
6
+ end
7
+
8
+ # @return [Array<String>]
9
+ def pack_name(node)
10
+ parts = []
11
+ if node.kind_of?(AST::Node)
12
+ node.children.each { |n|
13
+ if n.kind_of?(AST::Node)
14
+ if n.type == :cbase
15
+ parts = pack_name(n)
16
+ else
17
+ parts += pack_name(n)
18
+ end
19
+ else
20
+ parts.push n unless n.nil?
21
+ end
22
+ }
23
+ end
24
+ parts
25
+ end
26
+
27
+ # @return [String]
28
+ def const_from node
29
+ if node.kind_of?(AST::Node) and node.type == :const
30
+ result = ''
31
+ unless node.children[0].nil?
32
+ result = const_from(node.children[0])
33
+ end
34
+ if result == ''
35
+ result = node.children[1].to_s
36
+ else
37
+ result = result + '::' + node.children[1].to_s
38
+ end
39
+ result
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ # @return [String]
46
+ def infer_literal_node_type node
47
+ return nil unless node.kind_of?(AST::Node)
48
+ if node.type == :str or node.type == :dstr
49
+ return 'String'
50
+ elsif node.type == :array
51
+ return 'Array'
52
+ elsif node.type == :hash
53
+ return 'Hash'
54
+ elsif node.type == :int
55
+ return 'Integer'
56
+ elsif node.type == :float
57
+ return 'Float'
58
+ elsif node.type == :sym
59
+ return 'Symbol'
60
+ end
61
+ nil
62
+ end
63
+
64
+ # Get a call signature from a node.
65
+ # The result should be a string in the form of a method path, e.g.,
66
+ # String.new or variable.method.
67
+ #
68
+ # @return [String]
69
+ def resolve_node_signature node
70
+ drill_signature node, ''
71
+ end
72
+
73
+ private
74
+
75
+ def drill_signature node, signature
76
+ return signature unless node.kind_of?(AST::Node)
77
+ if node.type == :const or node.type == :cbase
78
+ unless node.children[0].nil?
79
+ signature += drill_signature(node.children[0], signature)
80
+ end
81
+ signature += '::' unless signature.empty?
82
+ signature += node.children[1].to_s
83
+ elsif node.type == :lvar or node.type == :ivar or node.type == :cvar
84
+ signature += '.' unless signature.empty?
85
+ signature += node.children[0].to_s
86
+ elsif node.type == :send
87
+ unless node.children[0].nil?
88
+ signature += drill_signature(node.children[0], signature)
89
+ end
90
+ signature += '.' unless signature.empty?
91
+ signature += node.children[1].to_s
92
+ end
93
+ signature
94
+ end
95
+ end
96
+ end