solargraph 0.17.4 → 0.18.0

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