solargraph 0.18.2 → 0.18.3

Sign up to get free protection for your applications and to get access to all the features.
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