solargraph 0.17.4 → 0.18.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +16 -12
  3. data/lib/solargraph/api_map.rb +516 -588
  4. data/lib/solargraph/api_map/completion.rb +16 -0
  5. data/lib/solargraph/api_map/source_to_yard.rb +2 -2
  6. data/lib/solargraph/language_server.rb +12 -0
  7. data/lib/solargraph/language_server/completion_item_kinds.rb +31 -0
  8. data/lib/solargraph/language_server/error_codes.rb +16 -0
  9. data/lib/solargraph/language_server/host.rb +305 -0
  10. data/lib/solargraph/language_server/message.rb +70 -0
  11. data/lib/solargraph/language_server/message/base.rb +64 -0
  12. data/lib/solargraph/language_server/message/cancel_request.rb +11 -0
  13. data/lib/solargraph/language_server/message/client.rb +5 -0
  14. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -0
  15. data/lib/solargraph/language_server/message/completion_item.rb +9 -0
  16. data/lib/solargraph/language_server/message/completion_item/resolve.rb +23 -0
  17. data/lib/solargraph/language_server/message/exit_notification.rb +12 -0
  18. data/lib/solargraph/language_server/message/extended.rb +15 -0
  19. data/lib/solargraph/language_server/message/extended/document.rb +18 -0
  20. data/lib/solargraph/language_server/message/extended/search.rb +18 -0
  21. data/lib/solargraph/language_server/message/initialize.rb +39 -0
  22. data/lib/solargraph/language_server/message/initialized.rb +10 -0
  23. data/lib/solargraph/language_server/message/method_not_found.rb +14 -0
  24. data/lib/solargraph/language_server/message/method_not_implemented.rb +12 -0
  25. data/lib/solargraph/language_server/message/shutdown.rb +11 -0
  26. data/lib/solargraph/language_server/message/text_document.rb +21 -0
  27. data/lib/solargraph/language_server/message/text_document/base.rb +17 -0
  28. data/lib/solargraph/language_server/message/text_document/completion.rb +69 -0
  29. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  30. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  31. data/lib/solargraph/language_server/message/text_document/did_close.rb +12 -0
  32. data/lib/solargraph/language_server/message/text_document/did_open.rb +13 -0
  33. data/lib/solargraph/language_server/message/text_document/did_save.rb +15 -0
  34. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +31 -0
  35. data/lib/solargraph/language_server/message/text_document/formatting.rb +36 -0
  36. data/lib/solargraph/language_server/message/text_document/hover.rb +19 -0
  37. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +29 -0
  38. data/lib/solargraph/language_server/message/text_document/signature_help.rb +23 -0
  39. data/lib/solargraph/language_server/message/workspace.rb +11 -0
  40. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +9 -0
  41. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +30 -0
  42. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +31 -0
  43. data/lib/solargraph/language_server/symbol_kinds.rb +32 -0
  44. data/lib/solargraph/language_server/transport.rb +7 -0
  45. data/lib/solargraph/language_server/transport/socket.rb +66 -0
  46. data/lib/solargraph/language_server/uri_helpers.rb +21 -0
  47. data/lib/solargraph/library.rb +225 -0
  48. data/lib/solargraph/live_map.rb +1 -1
  49. data/lib/solargraph/page.rb +61 -0
  50. data/lib/solargraph/pin.rb +7 -0
  51. data/lib/solargraph/pin/attribute.rb +9 -0
  52. data/lib/solargraph/pin/base.rb +76 -6
  53. data/lib/solargraph/pin/base_variable.rb +29 -7
  54. data/lib/solargraph/pin/block_parameter.rb +53 -0
  55. data/lib/solargraph/pin/constant.rb +6 -2
  56. data/lib/solargraph/pin/conversions.rb +65 -0
  57. data/lib/solargraph/pin/directed/attribute.rb +4 -0
  58. data/lib/solargraph/pin/directed/method.rb +6 -1
  59. data/lib/solargraph/pin/helper.rb +35 -0
  60. data/lib/solargraph/pin/keyword.rb +22 -0
  61. data/lib/solargraph/pin/local_variable.rb +0 -1
  62. data/lib/solargraph/pin/method.rb +55 -2
  63. data/lib/solargraph/pin/method_parameter.rb +19 -0
  64. data/lib/solargraph/pin/namespace.rb +7 -2
  65. data/lib/solargraph/pin/parameter.rb +23 -0
  66. data/lib/solargraph/pin/plugin/method.rb +3 -2
  67. data/lib/solargraph/pin/yard_object.rb +101 -0
  68. data/lib/solargraph/server.rb +82 -135
  69. data/lib/solargraph/shell.rb +20 -1
  70. data/lib/solargraph/source.rb +709 -0
  71. data/lib/solargraph/source/flawed_builder.rb +10 -0
  72. data/lib/solargraph/source/fragment.rb +319 -0
  73. data/lib/solargraph/source/position.rb +26 -0
  74. data/lib/solargraph/source/range.rb +39 -0
  75. data/lib/solargraph/suggestion.rb +29 -4
  76. data/lib/solargraph/version.rb +1 -1
  77. data/lib/solargraph/workspace.rb +105 -0
  78. data/lib/solargraph/{api_map → workspace}/config.rb +1 -1
  79. data/lib/solargraph/yard_map.rb +59 -37
  80. metadata +168 -5
  81. data/lib/solargraph/api_map/source.rb +0 -470
  82. data/lib/solargraph/code_map.rb +0 -868
@@ -0,0 +1,10 @@
1
+ module Solargraph
2
+ class Source
3
+ class FlawedBuilder < Parser::Builders::Default
4
+ def string_value(token)
5
+ value(token)
6
+ end
7
+ end
8
+ private_constant :FlawedBuilder
9
+ end
10
+ end
@@ -0,0 +1,319 @@
1
+ module Solargraph
2
+ class Source
3
+ class Fragment
4
+ # @return [Integer]
5
+ attr_reader :offset
6
+
7
+ include NodeMethods
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param offset [Integer]
11
+ def initialize source, offset
12
+ # @todo Split this object from the source. The source can change; if
13
+ # it does, this object's data should not.
14
+ @source = source
15
+ @code = source.code
16
+ @offset = offset
17
+ end
18
+
19
+ # Get the node at the current offset.
20
+ #
21
+ # @return [Parser::AST::Node]
22
+ def node
23
+ @node ||= @source.node_at(@offset)
24
+ end
25
+
26
+ # Get the fully qualified namespace at the current offset.
27
+ #
28
+ # @return [String]
29
+ def namespace
30
+ if @namespace.nil?
31
+ base = @source.parent_node_from(@offset, :class, :module, :def, :defs)
32
+ @namespace ||= @source.namespace_for(base)
33
+ end
34
+ @namespace
35
+ end
36
+
37
+ # Get the scope at the current offset.
38
+ #
39
+ # @return [Symbol] :class or :instance
40
+ def scope
41
+ if @scope.nil?
42
+ @scope = :class
43
+ tree = @source.tree_at(@offset)
44
+ until tree.empty?
45
+ cursor = tree.shift
46
+ break if cursor.type == :class or cursor.type == :module
47
+ if cursor.type == :def
48
+ pin = @source.method_pins.select{|pin| pin.contain?(@offset)}.first
49
+ # @todo The pin should never be nil here, but we're guarding it just in case
50
+ @scope = (pin.nil? ? :instance : pin.scope)
51
+ end
52
+ end
53
+ end
54
+ @scope
55
+ end
56
+
57
+ # Get the signature up to the current offset. Given the text `foo.bar`,
58
+ # the signature at offset 5 is `foo.b`.
59
+ #
60
+ # @return [String]
61
+ def signature
62
+ @signature ||= signature_data[1]
63
+ end
64
+
65
+ # Get the signature before the current word. Given the signature
66
+ # `String.new.split`, the base is `String.new`.
67
+ #
68
+ # @return [String]
69
+ def base
70
+ if @base.nil?
71
+ if signature.include?('.')
72
+ if signature.end_with?('.')
73
+ @base = signature[0..-2]
74
+ else
75
+ @base = signature.split('.')[0..-2].join('.')
76
+ end
77
+ elsif signature.include?('::')
78
+ if signature.end_with?('::')
79
+ @base = signature[0..-3]
80
+ else
81
+ @base = signature.split('::')[0..-2].join('::')
82
+ end
83
+ else
84
+ # @base = signature
85
+ @base = ''
86
+ end
87
+ end
88
+ @base
89
+ end
90
+
91
+ # Get the remainder of the word after the current offset. Given the text
92
+ # `foobar` with an offset of 3, the remainder is `bar`.
93
+ #
94
+ # @return [String]
95
+ def remainder
96
+ @remainder ||= remainder_at(@offset)
97
+ end
98
+
99
+ # Get the whole word at the current offset, including the remainder.
100
+ # Given the text `foobar.baz`, the whole word at any offset from 0 to 6
101
+ # is `foobar`.
102
+ #
103
+ # @return [String]
104
+ def whole_word
105
+ @whole_word ||= word + remainder
106
+ end
107
+
108
+ # Get the whole signature at the current offset, including the final
109
+ # word and its remainder.
110
+ #
111
+ # @return [String]
112
+ def whole_signature
113
+ @whole_signature ||= signature + remainder
114
+ end
115
+
116
+ # Get the entire phrase up to the current offset. Given the text
117
+ # `foo[bar].baz()`, the phrase at offset 10 is `foo[bar].b`.
118
+ #
119
+ # @return [String]
120
+ def phrase
121
+ @phrase ||= @code[signature_data[0]..@offset]
122
+ end
123
+
124
+ # Get the word before the current offset. Given the text `foo.bar`, the
125
+ # word at offset 6 is `ba`.
126
+ def word
127
+ @word ||= word_at(@offset)
128
+ end
129
+
130
+ # True if the current offset is inside a string.
131
+ #
132
+ # @return [Boolean]
133
+ def string?
134
+ @string = @source.string_at?(@offset) if @string.nil?
135
+ @string
136
+ end
137
+
138
+ # True if the current offset is inside a comment.
139
+ #
140
+ # @return [Boolean]
141
+ def comment?
142
+ @comment = get_comment_at(@offset) if @comment.nil?
143
+ @comment
144
+ end
145
+
146
+ # Get the range of the word up to the current offset.
147
+ #
148
+ # @return [Range]
149
+ def word_range
150
+ @word_range ||= word_range_at(@offset, false)
151
+ end
152
+
153
+ # Get the range of the whole word at the current offset, including its
154
+ # remainder.
155
+ #
156
+ # @return [Range]
157
+ def whole_word_range
158
+ @whole_word_range ||= word_range_at(@offset, true)
159
+ end
160
+
161
+ # Get an array of all the local variables in the source that are visible
162
+ # from the current offset.
163
+ #
164
+ # @return [Array<Solargraph::Pin::LocalVariable>]
165
+ def local_variable_pins
166
+ @local_variable_pins ||= @source.local_variable_pins.select{|pin| pin.visible_from?(node)}
167
+ end
168
+
169
+ private
170
+
171
+ def signature_data
172
+ @signature_data ||= get_signature_data_at(@offset)
173
+ end
174
+
175
+ def get_signature_data_at index
176
+ brackets = 0
177
+ squares = 0
178
+ parens = 0
179
+ signature = ''
180
+ index -=1
181
+ in_whitespace = false
182
+ while index >= 0
183
+ break if index > 0 and comment?
184
+ unless !in_whitespace and string?
185
+ break if brackets > 0 or parens > 0 or squares > 0
186
+ char = @code[index, 1]
187
+ if brackets.zero? and parens.zero? and squares.zero? and [' ', "\n", "\t"].include?(char)
188
+ in_whitespace = true
189
+ else
190
+ if brackets.zero? and parens.zero? and squares.zero? and in_whitespace
191
+ unless char == '.' or @code[index+1..-1].strip.start_with?('.')
192
+ old = @code[index+1..-1]
193
+ nxt = @code[index+1..-1].lstrip
194
+ index += (@code[index+1..-1].length - @code[index+1..-1].lstrip.length)
195
+ break
196
+ end
197
+ end
198
+ if char == ')'
199
+ parens -=1
200
+ elsif char == ']'
201
+ squares -=1
202
+ elsif char == '}'
203
+ brackets -= 1
204
+ elsif char == '('
205
+ parens += 1
206
+ elsif char == '{'
207
+ brackets += 1
208
+ elsif char == '['
209
+ squares += 1
210
+ signature = ".[]#{signature}" if squares == 0 and @code[index-2] != '%'
211
+ end
212
+ if brackets.zero? and parens.zero? and squares.zero?
213
+ break if ['"', "'", ',', ';', '%'].include?(char)
214
+ signature = char + signature if char.match(/[a-z0-9:\._@\$]/i) and @code[index - 1] != '%'
215
+ break if char == '$'
216
+ if char == '@'
217
+ signature = "@#{signature}" if @code[index-1, 1] == '@'
218
+ break
219
+ end
220
+ end
221
+ in_whitespace = false
222
+ end
223
+ end
224
+ index -= 1
225
+ end
226
+ if signature.start_with?('.')
227
+ pn = @source.node_at(index)
228
+ unless pn.nil?
229
+ literal = infer_literal_node_type(pn)
230
+ unless literal.nil?
231
+ signature = "#{literal}.new#{signature}"
232
+ # @todo Determine the index from the beginning of the literal node?
233
+ end
234
+ end
235
+ end
236
+ [index + 1, signature]
237
+ end
238
+
239
+ # Determine if the specified index is inside a comment.
240
+ #
241
+ # @return [Boolean]
242
+ def get_comment_at(index)
243
+ return false if string?
244
+ line, col = Solargraph::Source.get_position_at(@source.code, index)
245
+ @source.comments.each do |c|
246
+ return true if index > c.location.expression.begin_pos and index <= c.location.expression.end_pos
247
+ end
248
+ false
249
+ end
250
+
251
+ # Select the word that directly precedes the specified index.
252
+ # A word can only consist of letters, numbers, and underscores.
253
+ #
254
+ # @param index [Integer]
255
+ # @return [String]
256
+ def word_at index
257
+ @code[beginning_of_word_at(index)..index - 1]
258
+ end
259
+
260
+ def beginning_of_word_at index
261
+ cursor = index - 1
262
+ # Words can end with ? or !
263
+ if @code[cursor, 1] == '!' or @code[cursor, 1] == '?'
264
+ cursor -= 1
265
+ end
266
+ while cursor > -1
267
+ char = @code[cursor, 1]
268
+ break if char.nil? or char.strip.empty?
269
+ break unless char.match(/[a-z0-9_]/i)
270
+ cursor -= 1
271
+ end
272
+ # Words can begin with @@, @, $, or :
273
+ if cursor > -1
274
+ if cursor > 0 and @code[cursor - 1, 2] == '@@'
275
+ cursor -= 2
276
+ elsif @code[cursor, 1] == '@' or @code[cursor, 1] == '$'
277
+ cursor -= 1
278
+ elsif @code[cursor, 1] == ':' and (cursor == 0 or @code[cursor - 1, 2] != '::')
279
+ cursor -= 1
280
+ end
281
+ end
282
+ cursor + 1
283
+ end
284
+
285
+ # @return Solargraph::Source::Range
286
+ def word_range_at index, whole
287
+ cursor = beginning_of_word_at(index)
288
+ start_offset = cursor
289
+ start_offset -= 1 if (start_offset > 1 and @code[start_offset - 1] == ':') and (start_offset == 1 or @code[start_offset - 2] != ':')
290
+ cursor = index
291
+ if whole
292
+ while cursor < @code.length
293
+ char = @code[cursor, 1]
294
+ break if char.nil? or char == ''
295
+ break unless char.match(/[a-z0-9_\?\!]/i)
296
+ cursor += 1
297
+ end
298
+ end
299
+ end_offset = cursor
300
+ end_offset = start_offset if end_offset < start_offset
301
+ start_pos = Solargraph::Source.get_position_at(@code, start_offset)
302
+ end_pos = Solargraph::Source.get_position_at(@code, end_offset)
303
+ Solargraph::Source::Range.from_to(start_pos[0], start_pos[1], end_pos[0], end_pos[1])
304
+ end
305
+
306
+ def remainder_at index
307
+ cursor = index
308
+ while cursor < @code.length
309
+ char = @code[cursor, 1]
310
+ break if char.nil? or char == ''
311
+ break unless char.match(/[a-z0-9_\?\!]/i)
312
+ cursor += 1
313
+ end
314
+ @code[index..cursor-1]
315
+ end
316
+
317
+ end
318
+ end
319
+ end
@@ -0,0 +1,26 @@
1
+ module Solargraph
2
+ class Source
3
+ class Position
4
+ # @return [Integer]
5
+ attr_reader :line
6
+
7
+ # @return [Integer]
8
+ attr_reader :character
9
+
10
+ def initialize line, character
11
+ @line = line
12
+ @character = character
13
+ end
14
+
15
+ # Get a hash of the position. This representation is suitable for use in
16
+ # the language server protocol.
17
+ #
18
+ def to_hash
19
+ {
20
+ line: line,
21
+ character: character
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ module Solargraph
2
+ class Source
3
+ class Range
4
+ # @return [Position]
5
+ attr_reader :start
6
+
7
+ # @return [Position]
8
+ attr_reader :end
9
+
10
+ # @param start [Position]
11
+ # @param ending [Position]
12
+ def initialize start, ending
13
+ @start = start
14
+ @end = ending
15
+ end
16
+
17
+ # Get a hash of the range. This representation is suitable for use in
18
+ # the language server protocol.
19
+ #
20
+ def to_hash
21
+ {
22
+ start: start.to_hash,
23
+ end: self.end.to_hash
24
+ }
25
+ end
26
+
27
+ # Create a range from a pair of lines and characters.
28
+ #
29
+ # @param l1 [Integer] Starting line
30
+ # @param c1 [Integer] Starting character
31
+ # @param l2 [Integer] Ending line
32
+ # @param c2 [Integer] Ending character
33
+ # @return [Position]
34
+ def self.from_to l1, c1, l2, c2
35
+ Range.new(Position.new(l1, c1), Position.new(l2, c2))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -17,7 +17,28 @@ module Solargraph
17
17
  attr_reader :label
18
18
 
19
19
  # @return [String]
20
- attr_reader :kind
20
+ def kind
21
+ case @kind
22
+ when Solargraph::LanguageServer::CompletionItemKinds::CLASS
23
+ 'Class'
24
+ when Solargraph::LanguageServer::CompletionItemKinds::CONSTANT
25
+ 'Constant'
26
+ when Solargraph::LanguageServer::CompletionItemKinds::FIELD
27
+ 'Field'
28
+ when Solargraph::LanguageServer::CompletionItemKinds::KEYWORD
29
+ 'Keyword'
30
+ when Solargraph::LanguageServer::CompletionItemKinds::METHOD
31
+ 'Method'
32
+ when Solargraph::LanguageServer::CompletionItemKinds::MODULE
33
+ 'Module'
34
+ when Solargraph::LanguageServer::CompletionItemKinds::PROPERTY
35
+ 'Property'
36
+ when Solargraph::LanguageServer::CompletionItemKinds::VARIABLE
37
+ 'Variable'
38
+ else
39
+ nil
40
+ end
41
+ end
21
42
 
22
43
  # @return [String]
23
44
  attr_reader :insert
@@ -37,7 +58,10 @@ module Solargraph
37
58
  # @return [YARD::CodeObjects::Base]
38
59
  attr_reader :code_object
39
60
 
40
- def initialize label, kind: KEYWORD, insert: nil, detail: nil, docstring: nil, code_object: nil, location: nil, arguments: [], return_type: nil, path: nil
61
+ # @return [Solargraph::Pin::Base]
62
+ attr_reader :pin
63
+
64
+ def initialize label, kind: KEYWORD, insert: nil, detail: nil, docstring: nil, code_object: nil, location: nil, arguments: [], return_type: nil, path: nil, pin: nil
41
65
  @helper = Server::Helpers.new
42
66
  @label = label.to_s
43
67
  @kind = kind
@@ -49,6 +73,7 @@ module Solargraph
49
73
  @arguments = arguments
50
74
  @return_type = return_type
51
75
  @path = path
76
+ @pin = pin
52
77
  end
53
78
 
54
79
  # The full path of the suggestion.
@@ -124,7 +149,7 @@ module Solargraph
124
149
  def as_json args = {}
125
150
  result = {
126
151
  label: @label,
127
- kind: @kind,
152
+ kind: kind,
128
153
  insert: @insert,
129
154
  detail: @detail,
130
155
  path: path,
@@ -147,7 +172,7 @@ module Solargraph
147
172
  # @param pin [Solargraph::Pin::Base]
148
173
  def self.pull pin, return_type = nil
149
174
  return pin if pin.kind_of?(Suggestion)
150
- Suggestion.new(pin.name, insert: pin.name.gsub(/=$/, ' = '), kind: pin.kind, docstring: pin.docstring, detail: pin.namespace, arguments: pin.parameters, path: pin.path, return_type: return_type || pin.return_type, location: pin.location)
175
+ Suggestion.new(pin.name, insert: pin.name.gsub(/=$/, ' = '), kind: pin.kind, docstring: pin.docstring, detail: pin.namespace, arguments: pin.parameters, path: pin.path, return_type: return_type || pin.return_type, location: pin.location, pin: pin)
151
176
  end
152
177
  end
153
178
  end