cosmicgraph 0.49.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 (259) hide show
  1. checksums.yaml +7 -0
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/rspec.yml +41 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +1150 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE +21 -0
  10. data/README.md +136 -0
  11. data/Rakefile +25 -0
  12. data/SPONSORS.md +15 -0
  13. data/bin/solargraph +5 -0
  14. data/cosmicgraph.gemspec +44 -0
  15. data/lib/.rubocop.yml +22 -0
  16. data/lib/solargraph/api_map/bundler_methods.rb +22 -0
  17. data/lib/solargraph/api_map/cache.rb +70 -0
  18. data/lib/solargraph/api_map/source_to_yard.rb +81 -0
  19. data/lib/solargraph/api_map/store.rb +268 -0
  20. data/lib/solargraph/api_map.rb +704 -0
  21. data/lib/solargraph/bench.rb +27 -0
  22. data/lib/solargraph/cache.rb +51 -0
  23. data/lib/solargraph/complex_type/type_methods.rb +134 -0
  24. data/lib/solargraph/complex_type/unique_type.rb +132 -0
  25. data/lib/solargraph/complex_type.rb +254 -0
  26. data/lib/solargraph/convention/base.rb +33 -0
  27. data/lib/solargraph/convention/gemfile.rb +15 -0
  28. data/lib/solargraph/convention/gemspec.rb +22 -0
  29. data/lib/solargraph/convention/rakefile.rb +17 -0
  30. data/lib/solargraph/convention/rspec.rb +30 -0
  31. data/lib/solargraph/convention.rb +49 -0
  32. data/lib/solargraph/converters/dd.rb +12 -0
  33. data/lib/solargraph/converters/dl.rb +12 -0
  34. data/lib/solargraph/converters/dt.rb +12 -0
  35. data/lib/solargraph/converters/misc.rb +1 -0
  36. data/lib/solargraph/diagnostics/base.rb +29 -0
  37. data/lib/solargraph/diagnostics/require_not_found.rb +53 -0
  38. data/lib/solargraph/diagnostics/rubocop.rb +112 -0
  39. data/lib/solargraph/diagnostics/rubocop_helpers.rb +65 -0
  40. data/lib/solargraph/diagnostics/severities.rb +15 -0
  41. data/lib/solargraph/diagnostics/type_check.rb +54 -0
  42. data/lib/solargraph/diagnostics/update_errors.rb +41 -0
  43. data/lib/solargraph/diagnostics.rb +55 -0
  44. data/lib/solargraph/documentor.rb +76 -0
  45. data/lib/solargraph/environ.rb +45 -0
  46. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
  47. data/lib/solargraph/language_server/error_codes.rb +20 -0
  48. data/lib/solargraph/language_server/host/cataloger.rb +56 -0
  49. data/lib/solargraph/language_server/host/diagnoser.rb +89 -0
  50. data/lib/solargraph/language_server/host/dispatch.rb +111 -0
  51. data/lib/solargraph/language_server/host/message_worker.rb +59 -0
  52. data/lib/solargraph/language_server/host/sources.rb +156 -0
  53. data/lib/solargraph/language_server/host.rb +869 -0
  54. data/lib/solargraph/language_server/message/base.rb +89 -0
  55. data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
  57. data/lib/solargraph/language_server/message/client.rb +11 -0
  58. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -0
  59. data/lib/solargraph/language_server/message/completion_item.rb +11 -0
  60. data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
  61. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -0
  62. data/lib/solargraph/language_server/message/extended/document.rb +20 -0
  63. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
  64. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -0
  65. data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
  66. data/lib/solargraph/language_server/message/extended/search.rb +20 -0
  67. data/lib/solargraph/language_server/message/extended.rb +21 -0
  68. data/lib/solargraph/language_server/message/initialize.rb +164 -0
  69. data/lib/solargraph/language_server/message/initialized.rb +27 -0
  70. data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
  71. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
  72. data/lib/solargraph/language_server/message/shutdown.rb +13 -0
  73. data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
  74. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
  75. data/lib/solargraph/language_server/message/text_document/completion.rb +59 -0
  76. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  77. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  78. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
  79. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
  80. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
  81. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -0
  82. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
  83. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
  84. data/lib/solargraph/language_server/message/text_document/formatting.rb +126 -0
  85. data/lib/solargraph/language_server/message/text_document/hover.rb +56 -0
  86. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
  87. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
  88. data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
  89. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
  90. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -0
  91. data/lib/solargraph/language_server/message/text_document.rb +28 -0
  92. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +30 -0
  93. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -0
  94. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
  95. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
  96. data/lib/solargraph/language_server/message/workspace.rb +14 -0
  97. data/lib/solargraph/language_server/message.rb +93 -0
  98. data/lib/solargraph/language_server/message_types.rb +14 -0
  99. data/lib/solargraph/language_server/request.rb +24 -0
  100. data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
  101. data/lib/solargraph/language_server/transport/adapter.rb +53 -0
  102. data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
  103. data/lib/solargraph/language_server/transport.rb +13 -0
  104. data/lib/solargraph/language_server/uri_helpers.rb +49 -0
  105. data/lib/solargraph/language_server.rb +19 -0
  106. data/lib/solargraph/library.rb +547 -0
  107. data/lib/solargraph/location.rb +37 -0
  108. data/lib/solargraph/logging.rb +27 -0
  109. data/lib/solargraph/page.rb +83 -0
  110. data/lib/solargraph/parser/comment_ripper.rb +52 -0
  111. data/lib/solargraph/parser/legacy/class_methods.rb +135 -0
  112. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
  113. data/lib/solargraph/parser/legacy/node_chainer.rb +148 -0
  114. data/lib/solargraph/parser/legacy/node_methods.rb +325 -0
  115. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
  116. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
  117. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
  118. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +42 -0
  119. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +35 -0
  120. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
  121. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
  122. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
  123. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
  124. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
  125. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
  126. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
  127. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
  128. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
  129. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +42 -0
  130. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -0
  131. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
  132. data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
  133. data/lib/solargraph/parser/legacy.rb +12 -0
  134. data/lib/solargraph/parser/node_methods.rb +43 -0
  135. data/lib/solargraph/parser/node_processor/base.rb +77 -0
  136. data/lib/solargraph/parser/node_processor.rb +43 -0
  137. data/lib/solargraph/parser/region.rb +66 -0
  138. data/lib/solargraph/parser/rubyvm/class_methods.rb +149 -0
  139. data/lib/solargraph/parser/rubyvm/node_chainer.rb +160 -0
  140. data/lib/solargraph/parser/rubyvm/node_methods.rb +315 -0
  141. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
  142. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +85 -0
  143. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
  144. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +42 -0
  145. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +33 -0
  146. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
  147. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +75 -0
  148. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +68 -0
  149. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
  150. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
  151. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
  152. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
  153. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
  154. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
  155. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -0
  156. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
  157. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
  158. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +32 -0
  159. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
  160. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +279 -0
  161. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
  162. data/lib/solargraph/parser/rubyvm/node_processors.rb +63 -0
  163. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  164. data/lib/solargraph/parser/rubyvm.rb +40 -0
  165. data/lib/solargraph/parser/snippet.rb +13 -0
  166. data/lib/solargraph/parser.rb +26 -0
  167. data/lib/solargraph/pin/base.rb +299 -0
  168. data/lib/solargraph/pin/base_variable.rb +84 -0
  169. data/lib/solargraph/pin/block.rb +73 -0
  170. data/lib/solargraph/pin/class_variable.rb +8 -0
  171. data/lib/solargraph/pin/closure.rb +37 -0
  172. data/lib/solargraph/pin/common.rb +70 -0
  173. data/lib/solargraph/pin/constant.rb +43 -0
  174. data/lib/solargraph/pin/conversions.rb +92 -0
  175. data/lib/solargraph/pin/documenting.rb +105 -0
  176. data/lib/solargraph/pin/duck_method.rb +16 -0
  177. data/lib/solargraph/pin/global_variable.rb +8 -0
  178. data/lib/solargraph/pin/instance_variable.rb +30 -0
  179. data/lib/solargraph/pin/keyword.rb +15 -0
  180. data/lib/solargraph/pin/keyword_param.rb +8 -0
  181. data/lib/solargraph/pin/local_variable.rb +55 -0
  182. data/lib/solargraph/pin/method.rb +335 -0
  183. data/lib/solargraph/pin/method_alias.rb +31 -0
  184. data/lib/solargraph/pin/namespace.rb +94 -0
  185. data/lib/solargraph/pin/parameter.rb +206 -0
  186. data/lib/solargraph/pin/proxy_type.rb +29 -0
  187. data/lib/solargraph/pin/reference/extend.rb +10 -0
  188. data/lib/solargraph/pin/reference/include.rb +10 -0
  189. data/lib/solargraph/pin/reference/override.rb +29 -0
  190. data/lib/solargraph/pin/reference/prepend.rb +10 -0
  191. data/lib/solargraph/pin/reference/require.rb +14 -0
  192. data/lib/solargraph/pin/reference/superclass.rb +10 -0
  193. data/lib/solargraph/pin/reference.rb +14 -0
  194. data/lib/solargraph/pin/search.rb +56 -0
  195. data/lib/solargraph/pin/signature.rb +23 -0
  196. data/lib/solargraph/pin/singleton.rb +11 -0
  197. data/lib/solargraph/pin/symbol.rb +47 -0
  198. data/lib/solargraph/pin.rb +38 -0
  199. data/lib/solargraph/position.rb +100 -0
  200. data/lib/solargraph/range.rb +95 -0
  201. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  202. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  203. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  204. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  205. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  206. data/lib/solargraph/rbs_map.rb +73 -0
  207. data/lib/solargraph/server_methods.rb +16 -0
  208. data/lib/solargraph/shell.rb +234 -0
  209. data/lib/solargraph/source/chain/block_variable.rb +13 -0
  210. data/lib/solargraph/source/chain/call.rb +215 -0
  211. data/lib/solargraph/source/chain/class_variable.rb +13 -0
  212. data/lib/solargraph/source/chain/constant.rb +75 -0
  213. data/lib/solargraph/source/chain/global_variable.rb +13 -0
  214. data/lib/solargraph/source/chain/hash.rb +28 -0
  215. data/lib/solargraph/source/chain/head.rb +19 -0
  216. data/lib/solargraph/source/chain/instance_variable.rb +13 -0
  217. data/lib/solargraph/source/chain/link.rb +71 -0
  218. data/lib/solargraph/source/chain/literal.rb +23 -0
  219. data/lib/solargraph/source/chain/or.rb +23 -0
  220. data/lib/solargraph/source/chain/q_call.rb +11 -0
  221. data/lib/solargraph/source/chain/variable.rb +13 -0
  222. data/lib/solargraph/source/chain/z_super.rb +30 -0
  223. data/lib/solargraph/source/chain.rb +179 -0
  224. data/lib/solargraph/source/change.rb +79 -0
  225. data/lib/solargraph/source/cursor.rb +164 -0
  226. data/lib/solargraph/source/encoding_fixes.rb +23 -0
  227. data/lib/solargraph/source/source_chainer.rb +191 -0
  228. data/lib/solargraph/source/updater.rb +54 -0
  229. data/lib/solargraph/source.rb +522 -0
  230. data/lib/solargraph/source_map/clip.rb +229 -0
  231. data/lib/solargraph/source_map/completion.rb +23 -0
  232. data/lib/solargraph/source_map/mapper.rb +241 -0
  233. data/lib/solargraph/source_map.rb +180 -0
  234. data/lib/solargraph/type_checker/checks.rb +112 -0
  235. data/lib/solargraph/type_checker/param_def.rb +35 -0
  236. data/lib/solargraph/type_checker/problem.rb +32 -0
  237. data/lib/solargraph/type_checker/rules.rb +57 -0
  238. data/lib/solargraph/type_checker.rb +549 -0
  239. data/lib/solargraph/version.rb +5 -0
  240. data/lib/solargraph/views/_method.erb +62 -0
  241. data/lib/solargraph/views/_name_type_tag.erb +10 -0
  242. data/lib/solargraph/views/_namespace.erb +24 -0
  243. data/lib/solargraph/views/document.erb +23 -0
  244. data/lib/solargraph/views/environment.erb +58 -0
  245. data/lib/solargraph/views/layout.erb +44 -0
  246. data/lib/solargraph/views/search.erb +11 -0
  247. data/lib/solargraph/workspace/config.rb +231 -0
  248. data/lib/solargraph/workspace.rb +212 -0
  249. data/lib/solargraph/yard_map/cache.rb +19 -0
  250. data/lib/solargraph/yard_map/helpers.rb +16 -0
  251. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -0
  252. data/lib/solargraph/yard_map/mapper/to_method.rb +81 -0
  253. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -0
  254. data/lib/solargraph/yard_map/mapper.rb +77 -0
  255. data/lib/solargraph/yard_map/to_method.rb +79 -0
  256. data/lib/solargraph/yard_map.rb +301 -0
  257. data/lib/solargraph.rb +69 -0
  258. data/lib/yard-solargraph.rb +33 -0
  259. metadata +587 -0
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class SourceMap
5
+ # A static analysis tool for obtaining definitions, completions,
6
+ # signatures, and type inferences from a cursor.
7
+ #
8
+ class Clip
9
+ # @param api_map [ApiMap]
10
+ # @param cursor [Source::Cursor]
11
+ def initialize api_map, cursor
12
+ @api_map = api_map
13
+ @cursor = cursor
14
+ end
15
+
16
+ # @return [Array<Pin::Base>]
17
+ def define
18
+ return [] if cursor.comment? || cursor.chain.literal?
19
+ result = cursor.chain.define(api_map, block, locals)
20
+ result.concat((source_map.pins + source_map.locals).select{ |p| p.name == cursor.word && p.location.range.contain?(cursor.position) }) if result.empty?
21
+ result
22
+ end
23
+
24
+ # @return [Completion]
25
+ def complete
26
+ return package_completions([]) if !source_map.source.parsed? || cursor.string?
27
+ return package_completions(api_map.get_symbols) if cursor.chain.literal? && cursor.chain.links.last.word == '<Symbol>'
28
+ return Completion.new([], cursor.range) if cursor.chain.literal?
29
+ if cursor.comment?
30
+ tag_complete
31
+ else
32
+ code_complete
33
+ end
34
+ end
35
+
36
+ # @return [Array<Pin::Base>]
37
+ def signify
38
+ return [] unless cursor.argument?
39
+ chain = Parser.chain(cursor.recipient_node, cursor.filename)
40
+ chain.define(api_map, context_pin, locals).select { |pin| pin.is_a?(Pin::Method) }
41
+ end
42
+
43
+ # @return [ComplexType]
44
+ def infer
45
+ result = cursor.chain.infer(api_map, block, locals)
46
+ if result.tag == 'Class'
47
+ # HACK: Exception to return Object from Class#new
48
+ dfn = cursor.chain.define(api_map, block, locals).first
49
+ return ComplexType.try_parse('Object') if dfn && dfn.path == 'Class#new'
50
+ end
51
+ return result unless result.tag == 'self'
52
+ ComplexType.try_parse(cursor.chain.base.infer(api_map, block, locals).namespace)
53
+ end
54
+
55
+ # Get an array of all the locals that are visible from the cursors's
56
+ # position. Locals can be local variables, method parameters, or block
57
+ # parameters. The array starts with the nearest local pin.
58
+ #
59
+ # @return [Array<Solargraph::Pin::Base>]
60
+ def locals
61
+ @locals ||= source_map.locals_at(location)
62
+ end
63
+
64
+ def gates
65
+ block.gates
66
+ end
67
+
68
+ def in_block?
69
+ return @in_block unless @in_block.nil?
70
+ @in_block = begin
71
+ tree = cursor.source.tree_at(cursor.position.line, cursor.position.column)
72
+ Parser.is_ast_node?(tree[1]) && [:block, :ITER].include?(tree[1].type)
73
+ end
74
+ end
75
+
76
+ # @param phrase [String]
77
+ # @return [Array<Solargraph::Pin::Base>]
78
+ def translate phrase
79
+ chain = Parser.chain(Parser.parse(phrase))
80
+ chain.define(api_map, block, locals)
81
+ end
82
+
83
+ private
84
+
85
+ # @return [ApiMap]
86
+ attr_reader :api_map
87
+
88
+ # @return [Source::Cursor]
89
+ attr_reader :cursor
90
+
91
+ # @return [SourceMap]
92
+ def source_map
93
+ @source_map ||= api_map.source_map(cursor.filename)
94
+ end
95
+
96
+ def location
97
+ Location.new(source_map.filename, Solargraph::Range.new(cursor.position, cursor.position))
98
+ end
99
+
100
+ # @return [Solargraph::Pin::Base]
101
+ def block
102
+ @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character)
103
+ end
104
+
105
+ # The context at the current position.
106
+ #
107
+ # @return [Pin::Base]
108
+ def context_pin
109
+ @context_pin ||= source_map.locate_named_path_pin(cursor.node_position.line, cursor.node_position.character)
110
+ end
111
+
112
+ # @return [Array<Pin::Base>]
113
+ def yielded_self_pins
114
+ return [] unless block.is_a?(Pin::Block) && block.receiver
115
+ chain = Parser.chain(block.receiver, source_map.source.filename)
116
+ receiver_pin = chain.define(api_map, context_pin, locals).first
117
+ return [] if receiver_pin.nil?
118
+ result = []
119
+ ys = receiver_pin.docstring.tag(:yieldpublic)
120
+ unless ys.nil? || ys.types.empty?
121
+ ysct = ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
122
+ result.concat api_map.get_complex_type_methods(ysct, '', false)
123
+ end
124
+ result
125
+ end
126
+
127
+ # @return [Array<Pin::KeywordParam]
128
+ def complete_keyword_parameters
129
+ return [] unless cursor.argument? && cursor.chain.links.one? && cursor.word =~ /^[a-z0-9_]*:?$/
130
+ pins = signify
131
+ result = []
132
+ done = []
133
+ pins.each do |pin|
134
+ pin.parameters.each do |param|
135
+ next if done.include?(param.name)
136
+ done.push param.name
137
+ next unless param.keyword?
138
+ result.push Pin::KeywordParam.new(pin.location, "#{param.name}:")
139
+ end
140
+ if !pin.parameters.empty? && pin.parameters.last.kwrestarg?
141
+ pin.docstring.tags(:param).each do |tag|
142
+ next if done.include?(tag.name)
143
+ done.push tag.name
144
+ result.push Pin::KeywordParam.new(pin.location, "#{tag.name}:")
145
+ end
146
+ end
147
+ end
148
+ result
149
+ end
150
+
151
+ # @param result [Array<Pin::Base>]
152
+ # @return [Completion]
153
+ def package_completions result
154
+ frag_start = cursor.start_of_word.to_s.downcase
155
+ filtered = result.uniq(&:name).select { |s|
156
+ s.name.downcase.start_with?(frag_start) &&
157
+ (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
158
+ }
159
+ Completion.new(filtered, cursor.range)
160
+ end
161
+
162
+ def tag_complete
163
+ result = []
164
+ match = source_map.code[0..cursor.offset-1].match(/[\[<, ]([a-z0-9_:]*)\z/i)
165
+ if match
166
+ full = match[1]
167
+ if full.include?('::')
168
+ if full.end_with?('::')
169
+ result.concat api_map.get_constants(full[0..-3], *gates)
170
+ else
171
+ result.concat api_map.get_constants(full.split('::')[0..-2].join('::'), *gates)
172
+ end
173
+ else
174
+ result.concat api_map.get_constants('', full.end_with?('::') ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
175
+ end
176
+ end
177
+ package_completions(result)
178
+ end
179
+
180
+ def code_complete
181
+ result = []
182
+ result.concat complete_keyword_parameters
183
+ if cursor.chain.constant? || cursor.start_of_constant?
184
+ full = cursor.chain.links.first.word
185
+ type = if cursor.chain.undefined?
186
+ cursor.chain.base.infer(api_map, context_pin, locals)
187
+ else
188
+ if full.include?('::') && cursor.chain.links.length == 1
189
+ ComplexType.try_parse(full.split('::')[0..-2].join('::'))
190
+ elsif cursor.chain.links.length > 1
191
+ ComplexType.try_parse(full)
192
+ else
193
+ ComplexType::UNDEFINED
194
+ end
195
+ end
196
+ if type.undefined?
197
+ if full.include?('::')
198
+ result.concat api_map.get_constants(full, *gates)
199
+ else
200
+ result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
201
+ end
202
+ else
203
+ result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
204
+ end
205
+ else
206
+ type = cursor.chain.base.infer(api_map, block, locals)
207
+ result.concat api_map.get_complex_type_methods(type, block.binder.namespace, cursor.chain.links.length == 1)
208
+ if cursor.chain.links.length == 1
209
+ if cursor.word.start_with?('@@')
210
+ return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace))
211
+ elsif cursor.word.start_with?('@')
212
+ return package_completions(api_map.get_instance_variable_pins(block.binder.namespace, block.binder.scope))
213
+ elsif cursor.word.start_with?('$')
214
+ return package_completions(api_map.get_global_variable_pins)
215
+ end
216
+ result.concat locals
217
+ result.concat api_map.get_constants(context_pin.context.namespace, *gates)
218
+ result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
219
+ result.concat api_map.get_methods('Kernel')
220
+ # result.concat ApiMap.keywords
221
+ result.concat api_map.keyword_pins.to_a
222
+ result.concat yielded_self_pins
223
+ end
224
+ end
225
+ package_completions(result)
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class SourceMap
5
+ # The result of a completion request containing the pins that describe
6
+ # completion options and the range to be replaced.
7
+ #
8
+ class Completion
9
+ # @return [Array<Solargraph::Pin::Base>]
10
+ attr_reader :pins
11
+
12
+ # @return [Solargraph::Range]
13
+ attr_reader :range
14
+
15
+ # @param pins [Array<Solargraph::Pin::Base>]
16
+ # @param range [Solargraph::Range]
17
+ def initialize pins, range
18
+ @pins = pins
19
+ @range = range
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class SourceMap
5
+ # The Mapper generates pins and other data for SourceMaps.
6
+ #
7
+ # This class is used internally by the SourceMap class. Users should not
8
+ # normally need to call it directly.
9
+ #
10
+ class Mapper
11
+ # include Source::NodeMethods
12
+
13
+ private_class_method :new
14
+
15
+ MACRO_REGEXP = /(@\!method|@\!attribute|@\!visibility|@\!domain|@\!macro|@\!parse|@\!override)/.freeze
16
+
17
+ # Generate the data.
18
+ #
19
+ # @param source [Source]
20
+ # @return [Array]
21
+ def map source
22
+ @source = source
23
+ @filename = source.filename
24
+ @code = source.code
25
+ @comments = source.comments
26
+ @pins, @locals = Parser.map(source)
27
+ @pins.each { |p| p.source = :code }
28
+ @locals.each { |l| l.source = :code }
29
+ process_comment_directives
30
+ [@pins, @locals]
31
+ # rescue Exception => e
32
+ # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}"
33
+ # Solargraph.logger.warn e.backtrace.join("\n")
34
+ # [[], []]
35
+ end
36
+
37
+ # @param filename [String]
38
+ # @param code [String]
39
+ # @return [Array]
40
+ def unmap filename, code
41
+ s = Position.new(0, 0)
42
+ e = Position.from_offset(code, code.length)
43
+ location = Location.new(filename, Range.new(s, e))
44
+ [[Pin::Namespace.new(location: location, name: '')], []]
45
+ end
46
+
47
+ class << self
48
+ # @param source [Source]
49
+ # @return [Array]
50
+ def map source
51
+ return new.unmap(source.filename, source.code) unless source.parsed?
52
+ new.map source
53
+ end
54
+ end
55
+
56
+ # @return [Array<Solargraph::Pin::Base>]
57
+ def pins
58
+ @pins ||= []
59
+ end
60
+
61
+ # @param position [Solargraph::Position]
62
+ # @return [Solargraph::Pin::Closure]
63
+ def closure_at(position)
64
+ pins.select{|pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position)}.last
65
+ end
66
+
67
+ def process_comment source_position, comment_position, comment
68
+ return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ MACRO_REGEXP
69
+ cmnt = remove_inline_comment_hashes(comment)
70
+ parse = Solargraph::Source.parse_docstring(cmnt)
71
+ last_line = 0
72
+ # @param d [YARD::Tags::Directive]
73
+ parse.directives.each do |d|
74
+ line_num = find_directive_line_number(cmnt, d.tag.tag_name, last_line)
75
+ pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column)
76
+ process_directive(source_position, pos, d)
77
+ last_line = line_num + 1
78
+ end
79
+ end
80
+
81
+ # @param comment [String]
82
+ # @return [Integer]
83
+ def find_directive_line_number comment, tag, start
84
+ # Avoid overruning the index
85
+ return start unless start < comment.lines.length
86
+ num = comment.lines[start..-1].find_index do |line|
87
+ # Legacy method directives might be `@method` instead of `@!method`
88
+ # @todo Legacy syntax should probably emit a warning
89
+ line.include?("@!#{tag}") || (tag == 'method' && line.include?("@#{tag}"))
90
+ end
91
+ num.to_i + start
92
+ end
93
+
94
+ # @param source_position [Position]
95
+ # @param comment_position [Position]
96
+ # @param directive [YARD::Tags::Directive]
97
+ # @return [void]
98
+ def process_directive source_position, comment_position, directive
99
+ docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring
100
+ location = Location.new(@filename, Range.new(comment_position, comment_position))
101
+ case directive.tag.tag_name
102
+ when 'method'
103
+ namespace = closure_at(source_position) || @pins.first
104
+ if namespace.location.range.start.line < comment_position.line
105
+ namespace = closure_at(comment_position)
106
+ end
107
+ begin
108
+ src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
109
+ region = Parser::Region.new(source: src, closure: namespace)
110
+ gen_pin = Parser.process_node(src.node, region).first.last
111
+ return if gen_pin.nil?
112
+ # Move the location to the end of the line so it gets recognized
113
+ # as originating from a comment
114
+ shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].to_s.chomp.length)
115
+ # @todo: Smelly instance variable access
116
+ gen_pin.instance_variable_set(:@comments, docstring.all.to_s)
117
+ gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted)))
118
+ gen_pin.instance_variable_set(:@explicit, false)
119
+ @pins.push gen_pin
120
+ rescue Parser::SyntaxError => e
121
+ # @todo Handle error in directive
122
+ end
123
+ when 'attribute'
124
+ return if directive.tag.name.nil?
125
+ namespace = closure_at(source_position)
126
+ t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
127
+ if t.nil? || t.include?('r')
128
+ pins.push Solargraph::Pin::Method.new(
129
+ location: location,
130
+ closure: namespace,
131
+ name: directive.tag.name,
132
+ comments: docstring.all.to_s,
133
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
134
+ visibility: :public,
135
+ explicit: false,
136
+ attribute: true
137
+ )
138
+ end
139
+ if t.nil? || t.include?('w')
140
+ pins.push Solargraph::Pin::Method.new(
141
+ location: location,
142
+ closure: namespace,
143
+ name: "#{directive.tag.name}=",
144
+ comments: docstring.all.to_s,
145
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
146
+ visibility: :public,
147
+ attribute: true
148
+ )
149
+ pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
150
+ if pins.last.return_type.defined?
151
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
152
+ end
153
+ end
154
+ when 'visibility'
155
+ begin
156
+ kind = directive.tag.text&.to_sym
157
+ return unless [:private, :protected, :public].include?(kind)
158
+
159
+ name = directive.tag.name
160
+ closure = closure_at(source_position) || @pins.first
161
+ if closure.location.range.start.line < comment_position.line
162
+ closure = closure_at(comment_position)
163
+ end
164
+ if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line)
165
+ # @todo Smelly instance variable access
166
+ closure.instance_variable_set(:@visibility, kind)
167
+ else
168
+ matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance }
169
+ matches.each do |pin|
170
+ # @todo Smelly instance variable access
171
+ pin.instance_variable_set(:@visibility, kind)
172
+ end
173
+ end
174
+ end
175
+ when 'parse'
176
+ begin
177
+ ns = closure_at(source_position)
178
+ src = Solargraph::Source.load_string(directive.tag.text, @source.filename)
179
+ region = Parser::Region.new(source: src, closure: ns)
180
+ # @todo These pins may need to be marked not explicit
181
+ index = @pins.length
182
+ loff = if @code.lines[comment_position.line].strip.end_with?('@!parse')
183
+ comment_position.line + 1
184
+ else
185
+ comment_position.line
186
+ end
187
+ Parser.process_node(src.node, region, @pins)
188
+ @pins[index..-1].each do |p|
189
+ # @todo Smelly instance variable access
190
+ p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
191
+ p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
192
+ end
193
+ rescue Parser::SyntaxError => e
194
+ # @todo Handle parser errors in !parse directives
195
+ end
196
+ when 'domain'
197
+ namespace = closure_at(source_position) || Pin::ROOT_PIN
198
+ namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
199
+ when 'override'
200
+ pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags)
201
+ end
202
+ end
203
+
204
+ def no_empty_lines?(line1, line2)
205
+ @code.lines[line1..line2].none? { |line| line.strip.empty? }
206
+ end
207
+
208
+ def remove_inline_comment_hashes comment
209
+ ctxt = ''
210
+ num = nil
211
+ started = false
212
+ comment.lines.each { |l|
213
+ # Trim the comment and minimum leading whitespace
214
+ p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#/, '')
215
+ if num.nil? && !p.strip.empty?
216
+ num = p.index(/[^ ]/)
217
+ started = true
218
+ elsif started && !p.strip.empty?
219
+ cur = p.index(/[^ ]/)
220
+ num = cur if cur < num
221
+ end
222
+ ctxt += "#{p[num..-1]}" if started
223
+ }
224
+ ctxt
225
+ end
226
+
227
+ # @return [void]
228
+ def process_comment_directives
229
+ return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ MACRO_REGEXP
230
+ code_lines = @code.lines
231
+ @source.associated_comments.each do |line, comments|
232
+ src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0)
233
+ com_pos = Position.new(line + 1 - comments.lines.length, 0)
234
+ process_comment(src_pos, com_pos, comments)
235
+ end
236
+ rescue StandardError => e
237
+ raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yard'
4
+ require 'yard-solargraph'
5
+ require 'set'
6
+
7
+ module Solargraph
8
+ # An index of pins and other ApiMap-related data for a Source.
9
+ #
10
+ class SourceMap
11
+ autoload :Mapper, 'solargraph/source_map/mapper'
12
+ autoload :Clip, 'solargraph/source_map/clip'
13
+ autoload :Completion, 'solargraph/source_map/completion'
14
+
15
+ # @return [Source]
16
+ attr_reader :source
17
+
18
+ # @return [Array<Pin::Base>]
19
+ attr_reader :pins
20
+
21
+ # @return [Array<Pin::Base>]
22
+ attr_reader :locals
23
+
24
+ # @param source [Source]
25
+ # @param pins [Array<Pin::Base>]
26
+ # @param locals [Array<Pin::Base>]
27
+ def initialize source, pins, locals
28
+ # HACK: Keep the library from changing this
29
+ @source = source.dup
30
+ @pins = pins
31
+ @locals = locals
32
+ environ.merge Convention.for_local(self) unless filename.nil?
33
+ @pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
34
+ @pin_select_cache = {}
35
+ end
36
+
37
+ def pins_by_class klass
38
+ @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
39
+ end
40
+
41
+ def rebindable_method_names
42
+ @rebindable_method_names ||= pins_by_class(Pin::Method)
43
+ .select { |pin| pin.comments && pin.comments.include?('@yieldself') }
44
+ .map(&:name)
45
+ .to_set
46
+ end
47
+
48
+ # @return [String]
49
+ def filename
50
+ source.filename
51
+ end
52
+
53
+ # @return [String]
54
+ def code
55
+ source.code
56
+ end
57
+
58
+ # @return [Array<Pin::Reference::Require>]
59
+ def requires
60
+ pins_by_class(Pin::Reference::Require)
61
+ end
62
+
63
+ # @return [Environ]
64
+ def environ
65
+ @environ ||= Environ.new
66
+ end
67
+
68
+ # @return [Array<Pin::Base>]
69
+ def document_symbols
70
+ @document_symbols ||= pins.select { |pin|
71
+ pin.path && !pin.path.empty?
72
+ }
73
+ end
74
+
75
+ # @param query [String]
76
+ # @return [Array<Pin::Base>]
77
+ def query_symbols query
78
+ Pin::Search.new(document_symbols, query).results
79
+ end
80
+
81
+ # @param position [Position]
82
+ # @return [Source::Cursor]
83
+ def cursor_at position
84
+ Source::Cursor.new(source, position)
85
+ end
86
+
87
+ # @param path [String]
88
+ # @return [Pin::Base]
89
+ def first_pin path
90
+ pins.select { |p| p.path == path }.first
91
+ end
92
+
93
+ # @param location [Solargraph::Location]
94
+ # @return [Array<Solargraph::Pin::Base>]
95
+ def locate_pins location
96
+ # return nil unless location.start_with?("#{filename}:")
97
+ (pins + locals).select { |pin| pin.location == location }
98
+ end
99
+
100
+ def locate_named_path_pin line, character
101
+ _locate_pin line, character, Pin::Namespace, Pin::Method
102
+ end
103
+
104
+ def locate_block_pin line, character
105
+ _locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
106
+ end
107
+
108
+ # @param other_map [SourceMap]
109
+ # @return [Boolean]
110
+ def try_merge! other_map
111
+ return false if pins.length != other_map.pins.length || locals.length != other_map.locals.length || requires.map(&:name).uniq.sort != other_map.requires.map(&:name).uniq.sort
112
+ pins.each_index do |i|
113
+ return false unless pins[i].try_merge!(other_map.pins[i])
114
+ end
115
+ locals.each_index do |i|
116
+ return false unless locals[i].try_merge!(other_map.locals[i])
117
+ end
118
+ @source = other_map.source
119
+ true
120
+ end
121
+
122
+ # @param name [String]
123
+ # @return [Array<Location>]
124
+ def references name
125
+ source.references name
126
+ end
127
+
128
+ # @param location [Location]
129
+ # @return [Array<Pin::LocalVariable>]
130
+ def locals_at(location)
131
+ return [] if location.filename != filename
132
+ closure = locate_named_path_pin(location.range.start.line, location.range.start.character)
133
+ locals.select { |pin| pin.visible_at?(closure, location) }
134
+ end
135
+
136
+ class << self
137
+ # @param filename [String]
138
+ # @return [SourceMap]
139
+ def load filename
140
+ source = Solargraph::Source.load(filename)
141
+ SourceMap.map(source)
142
+ end
143
+
144
+ # @param code [String]
145
+ # @param filename [String, nil]
146
+ # @return [SourceMap]
147
+ def load_string code, filename = nil
148
+ source = Solargraph::Source.load_string(code, filename)
149
+ SourceMap.map(source)
150
+ end
151
+
152
+ # @param source [Source]
153
+ # @return [SourceMap]
154
+ def map source
155
+ result = SourceMap::Mapper.map(source)
156
+ new(source, *result)
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ # @param line [Integer]
163
+ # @param character [Integer]
164
+ # @param klasses [Array<Class>]
165
+ # @return [Pin::Base]
166
+ def _locate_pin line, character, *klasses
167
+ position = Position.new(line, character)
168
+ found = nil
169
+ pins.each do |pin|
170
+ # @todo Attribute pins should not be treated like closures, but
171
+ # there's probably a better way to handle it
172
+ next if pin.is_a?(Pin::Method) && pin.attribute?
173
+ found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position)
174
+ break if pin.location.range.start.line > line
175
+ end
176
+ # Assuming the root pin is always valid
177
+ found || pins.first
178
+ end
179
+ end
180
+ end