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,547 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module Solargraph
6
+ # A Library handles coordination between a Workspace and an ApiMap.
7
+ #
8
+ class Library
9
+ include Logging
10
+
11
+ # @return [Solargraph::Workspace]
12
+ attr_reader :workspace
13
+
14
+ # @return [String, nil]
15
+ attr_reader :name
16
+
17
+ # @return [Source, nil]
18
+ attr_reader :current
19
+
20
+ # @param workspace [Solargraph::Workspace]
21
+ # @param name [String, nil]
22
+ def initialize workspace = Solargraph::Workspace.new, name = nil
23
+ @workspace = workspace
24
+ @name = name
25
+ @synchronized = false
26
+ end
27
+
28
+ def inspect
29
+ # Let's not deal with insane data dumps in spec failures
30
+ to_s
31
+ end
32
+
33
+ # True if the ApiMap is up to date with the library's workspace and open
34
+ # files.
35
+ #
36
+ # @return [Boolean]
37
+ def synchronized?
38
+ @synchronized
39
+ end
40
+
41
+ # Attach a source to the library.
42
+ #
43
+ # The attached source does not need to be a part of the workspace. The
44
+ # library will include it in the ApiMap while it's attached. Only one
45
+ # source can be attached to the library at a time.
46
+ #
47
+ # @param source [Source, nil]
48
+ # @return [void]
49
+ def attach source
50
+ mutex.synchronize do
51
+ if @current && (!source || @current.filename != source.filename) && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
52
+ source_map_hash.delete @current.filename
53
+ source_map_external_require_hash.delete @current.filename
54
+ @external_requires = nil
55
+ @synchronized = false
56
+ end
57
+ @current = source
58
+ maybe_map @current
59
+ catalog_inlock
60
+ end
61
+ end
62
+
63
+ # True if the specified file is currently attached.
64
+ #
65
+ # @param filename [String]
66
+ # @return [Boolean]
67
+ def attached? filename
68
+ !@current.nil? && @current.filename == filename
69
+ end
70
+ alias open? attached?
71
+
72
+ # Detach the specified file if it is currently attached to the library.
73
+ #
74
+ # @param filename [String]
75
+ # @return [Boolean] True if the specified file was detached
76
+ def detach filename
77
+ return false if @current.nil? || @current.filename != filename
78
+ attach nil
79
+ true
80
+ end
81
+
82
+ # True if the specified file is included in the workspace (but not
83
+ # necessarily open).
84
+ #
85
+ # @param filename [String]
86
+ # @return [Boolean]
87
+ def contain? filename
88
+ workspace.has_file?(filename)
89
+ end
90
+
91
+ # Create a source to be added to the workspace. The file is ignored if it is
92
+ # neither open in the library nor included in the workspace.
93
+ #
94
+ # @param filename [String]
95
+ # @param text [String] The contents of the file
96
+ # @return [Boolean] True if the file was added to the workspace.
97
+ def create filename, text
98
+ result = false
99
+ mutex.synchronize do
100
+ next unless contain?(filename) || open?(filename) || workspace.would_merge?(filename)
101
+ @synchronized = false
102
+ source = Solargraph::Source.load_string(text, filename)
103
+ workspace.merge(source)
104
+ result = true
105
+ end
106
+ result
107
+ end
108
+
109
+ # Create file sources from files on disk. A file is ignored if it is
110
+ # neither open in the library nor included in the workspace.
111
+ #
112
+ # @param filenames [Array<String>]
113
+ # @return [Boolean] True if at least one file was added to the workspace.
114
+ def create_from_disk *filenames
115
+ result = false
116
+ mutex.synchronize do
117
+ sources = filenames
118
+ .reject { |filename| File.directory?(filename) || !File.exist?(filename) }
119
+ .map { |filename| Solargraph::Source.load_string(File.read(filename), filename) }
120
+ result = workspace.merge(*sources)
121
+ sources.each { |source| maybe_map source }
122
+ end
123
+ result
124
+ end
125
+
126
+ # Delete files from the library. Deleting a file will make it unavailable
127
+ # for checkout and optionally remove it from the workspace unless the
128
+ # workspace configuration determines that it should still exist.
129
+ #
130
+ # @param filenames [Array<String>]
131
+ # @return [Boolean] True if any file was deleted
132
+ def delete *filenames
133
+ result = false
134
+ filenames.each do |filename|
135
+ detach filename
136
+ mutex.synchronize do
137
+ result ||= workspace.remove(filename)
138
+ @synchronized = !result if synchronized?
139
+ end
140
+ end
141
+ result
142
+ end
143
+
144
+ # Close a file in the library. Closing a file will make it unavailable for
145
+ # checkout although it may still exist in the workspace.
146
+ #
147
+ # @param filename [String]
148
+ # @return [void]
149
+ def close filename
150
+ mutex.synchronize do
151
+ @synchronized = false
152
+ @current = nil if @current && @current.filename == filename
153
+ catalog
154
+ end
155
+ end
156
+
157
+ # Get completion suggestions at the specified file and location.
158
+ #
159
+ # @param filename [String] The file to analyze
160
+ # @param line [Integer] The zero-based line number
161
+ # @param column [Integer] The zero-based column number
162
+ # @return [SourceMap::Completion]
163
+ # @todo Take a Location instead of filename/line/column
164
+ def completions_at filename, line, column
165
+ position = Position.new(line, column)
166
+ cursor = Source::Cursor.new(read(filename), position)
167
+ api_map.clip(cursor).complete
168
+ rescue FileNotFoundError => e
169
+ handle_file_not_found filename, e
170
+ end
171
+
172
+ # Get definition suggestions for the expression at the specified file and
173
+ # location.
174
+ #
175
+ # @param filename [String] The file to analyze
176
+ # @param line [Integer] The zero-based line number
177
+ # @param column [Integer] The zero-based column number
178
+ # @return [Array<Solargraph::Pin::Base>]
179
+ # @todo Take filename/position instead of filename/line/column
180
+ def definitions_at filename, line, column
181
+ position = Position.new(line, column)
182
+ cursor = Source::Cursor.new(read(filename), position)
183
+ if cursor.comment?
184
+ source = read(filename)
185
+ offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column))
186
+ lft = source.code[0..offset-1].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i)
187
+ rgt = source.code[offset..-1].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i)
188
+ if lft && rgt
189
+ tag = (lft[1] + rgt[1]).sub(/:+$/, '')
190
+ clip = api_map.clip(cursor)
191
+ clip.translate tag
192
+ else
193
+ []
194
+ end
195
+ else
196
+ api_map.clip(cursor).define.map { |pin| pin.realize(api_map) }
197
+ end
198
+ rescue FileNotFoundError => e
199
+ handle_file_not_found(filename, e)
200
+ end
201
+
202
+ # Get signature suggestions for the method at the specified file and
203
+ # location.
204
+ #
205
+ # @param filename [String] The file to analyze
206
+ # @param line [Integer] The zero-based line number
207
+ # @param column [Integer] The zero-based column number
208
+ # @return [Array<Solargraph::Pin::Base>]
209
+ # @todo Take filename/position instead of filename/line/column
210
+ def signatures_at filename, line, column
211
+ position = Position.new(line, column)
212
+ cursor = Source::Cursor.new(read(filename), position)
213
+ api_map.clip(cursor).signify
214
+ end
215
+
216
+ # @param filename [String]
217
+ # @param line [Integer]
218
+ # @param column [Integer]
219
+ # @param strip [Boolean] Strip special characters from variable names
220
+ # @param only [Boolean] Search for references in the current file only
221
+ # @return [Array<Solargraph::Range>]
222
+ # @todo Take a Location instead of filename/line/column
223
+ def references_from filename, line, column, strip: false, only: false
224
+ cursor = api_map.cursor_at(filename, Position.new(line, column))
225
+ clip = api_map.clip(cursor)
226
+ pin = clip.define.first
227
+ return [] unless pin
228
+ result = []
229
+ files = if only
230
+ [api_map.source_map(filename)]
231
+ else
232
+ (workspace.sources + (@current ? [@current] : []))
233
+ end
234
+ files.uniq(&:filename).each do |source|
235
+ found = source.references(pin.name)
236
+ found.select! do |loc|
237
+ referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character).first
238
+ referenced && referenced.path == pin.path
239
+ end
240
+ # HACK: for language clients that exclude special characters from the start of variable names
241
+ if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
242
+ found.map! do |loc|
243
+ Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column))
244
+ end
245
+ end
246
+ result.concat(found.sort do |a, b|
247
+ a.range.start.line <=> b.range.start.line
248
+ end)
249
+ end
250
+ result.uniq
251
+ end
252
+
253
+ # Get the pins at the specified location or nil if the pin does not exist.
254
+ #
255
+ # @param location [Location]
256
+ # @return [Array<Solargraph::Pin::Base>]
257
+ def locate_pins location
258
+ api_map.locate_pins(location).map { |pin| pin.realize(api_map) }
259
+ end
260
+
261
+ def locate_ref location
262
+ map = source_map_hash[location.filename]
263
+ return if map.nil?
264
+ pin = map.requires.select { |p| p.location.range.contain?(location.range.start) }.first
265
+ return nil if pin.nil?
266
+ workspace.require_paths.each do |path|
267
+ full = Pathname.new(path).join("#{pin.name}.rb").to_s
268
+ next unless source_map_hash.key?(full)
269
+ return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0))
270
+ end
271
+ # api_map.yard_map.require_reference(pin.name)
272
+ rescue FileNotFoundError
273
+ nil
274
+ end
275
+
276
+ # Get an array of pins that match a path.
277
+ #
278
+ # @param path [String]
279
+ # @return [Array<Solargraph::Pin::Base>]
280
+ def get_path_pins path
281
+ api_map.get_path_suggestions(path)
282
+ end
283
+
284
+ # @param query [String]
285
+ # @return [Array<YARD::CodeObjects::Base>]
286
+ def document query
287
+ api_map.document query
288
+ end
289
+
290
+ # @param query [String]
291
+ # @return [Array<String>]
292
+ def search query
293
+ api_map.search query
294
+ end
295
+
296
+ # Get an array of all symbols in the workspace that match the query.
297
+ #
298
+ # @param query [String]
299
+ # @return [Array<Pin::Base>]
300
+ def query_symbols query
301
+ api_map.query_symbols query
302
+ end
303
+
304
+ # Get an array of document symbols.
305
+ #
306
+ # Document symbols are composed of namespace, method, and constant pins.
307
+ # The results of this query are appropriate for building the response to a
308
+ # textDocument/documentSymbol message in the language server protocol.
309
+ #
310
+ # @param filename [String]
311
+ # @return [Array<Solargraph::Pin::Base>]
312
+ def document_symbols filename
313
+ api_map.document_symbols(filename)
314
+ end
315
+
316
+ # @param path [String]
317
+ # @return [Array<Solargraph::Pin::Base>]
318
+ def path_pins path
319
+ api_map.get_path_suggestions(path)
320
+ end
321
+
322
+ def source_maps
323
+ source_map_hash.values
324
+ end
325
+
326
+ # Get the current text of a file in the library.
327
+ #
328
+ # @param filename [String]
329
+ # @return [String]
330
+ def read_text filename
331
+ source = read(filename)
332
+ source.code
333
+ end
334
+
335
+ # Get diagnostics about a file.
336
+ #
337
+ # @param filename [String]
338
+ # @return [Array<Hash>]
339
+ def diagnose filename
340
+ # @todo Only open files get diagnosed. Determine whether anything or
341
+ # everything in the workspace should get diagnosed, or if there should
342
+ # be an option to do so.
343
+ #
344
+ return [] unless open?(filename)
345
+ result = []
346
+ source = read(filename)
347
+ catalog
348
+ repargs = {}
349
+ workspace.config.reporters.each do |line|
350
+ if line == 'all!'
351
+ Diagnostics.reporters.each do |reporter|
352
+ repargs[reporter] ||= []
353
+ end
354
+ else
355
+ args = line.split(':').map(&:strip)
356
+ name = args.shift
357
+ reporter = Diagnostics.reporter(name)
358
+ raise DiagnosticsError, "Diagnostics reporter #{name} does not exist" if reporter.nil?
359
+ repargs[reporter] ||= []
360
+ repargs[reporter].concat args
361
+ end
362
+ end
363
+ repargs.each_pair do |reporter, args|
364
+ result.concat reporter.new(*args.uniq).diagnose(source, api_map)
365
+ end
366
+ result
367
+ end
368
+
369
+ # Update the ApiMap from the library's workspace and open files.
370
+ #
371
+ # @return [void]
372
+ def catalog
373
+ mutex.synchronize do
374
+ catalog_inlock
375
+ end
376
+ end
377
+
378
+ private def catalog_inlock
379
+ return if synchronized?
380
+ logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
381
+ api_map.catalog bench
382
+ @synchronized = true
383
+ logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
384
+ end
385
+
386
+ def bench
387
+ Bench.new(
388
+ source_maps: source_map_hash.values,
389
+ workspace: workspace,
390
+ external_requires: external_requires
391
+ )
392
+ end
393
+
394
+ # Get an array of foldable ranges for the specified file.
395
+ #
396
+ # @deprecated The library should not need to handle folding ranges. The
397
+ # source itself has all the information it needs.
398
+ #
399
+ # @param filename [String]
400
+ # @return [Array<Range>]
401
+ def folding_ranges filename
402
+ read(filename).folding_ranges
403
+ end
404
+
405
+ # Create a library from a directory.
406
+ #
407
+ # @param directory [String] The path to be used for the workspace
408
+ # @param name [String, nil]
409
+ # @return [Solargraph::Library]
410
+ def self.load directory = '', name = nil
411
+ Solargraph::Library.new(Solargraph::Workspace.new(directory), name)
412
+ end
413
+
414
+ # Try to merge a source into the library's workspace. If the workspace is
415
+ # not configured to include the source, it gets ignored.
416
+ #
417
+ # @param source [Source]
418
+ # @return [Boolean] True if the source was merged into the workspace.
419
+ def merge source
420
+ Logging.logger.debug "Merging source: #{source.filename}"
421
+ result = false
422
+ mutex.synchronize do
423
+ result = workspace.merge(source)
424
+ maybe_map source
425
+ end
426
+ # catalog
427
+ result
428
+ end
429
+
430
+ def source_map_hash
431
+ @source_map_hash ||= {}
432
+ end
433
+
434
+ def mapped?
435
+ (workspace.filenames - source_map_hash.keys).empty?
436
+ end
437
+
438
+ def next_map
439
+ return false if mapped?
440
+ mutex.synchronize do
441
+ @synchronized = false
442
+ src = workspace.sources.find { |s| !source_map_hash.key?(s.filename) }
443
+ if src
444
+ Logging.logger.debug "Mapping #{src.filename}"
445
+ source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
446
+ find_external_requires(source_map_hash[src.filename])
447
+ source_map_hash[src.filename]
448
+ else
449
+ false
450
+ end
451
+ end
452
+ end
453
+
454
+ def map!
455
+ workspace.sources.each do |src|
456
+ source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
457
+ find_external_requires(source_map_hash[src.filename])
458
+ end
459
+ self
460
+ end
461
+
462
+ def pins
463
+ @pins ||= []
464
+ end
465
+
466
+ def external_requires
467
+ @external_requires ||= source_map_external_require_hash.values.flatten.to_set
468
+ end
469
+
470
+ private
471
+
472
+ def source_map_external_require_hash
473
+ @source_map_external_require_hash ||= {}
474
+ end
475
+
476
+ # @param source_map [SourceMap]
477
+ def find_external_requires source_map
478
+ new_set = source_map.requires.map(&:name).to_set
479
+ # return if new_set == source_map_external_require_hash[source_map.filename]
480
+ source_map_external_require_hash[source_map.filename] = new_set.reject do |path|
481
+ workspace.require_paths.any? do |base|
482
+ full = Pathname.new(base).join("#{path}.rb").to_s
483
+ workspace.filenames.include?(full)
484
+ end
485
+ end
486
+ @external_requires = nil
487
+ end
488
+
489
+ # @return [Mutex]
490
+ def mutex
491
+ @mutex ||= Mutex.new
492
+ end
493
+
494
+ # @return [ApiMap]
495
+ def api_map
496
+ @api_map ||= Solargraph::ApiMap.new
497
+ end
498
+
499
+ # Get the source for an open file or create a new source if the file
500
+ # exists on disk. Sources created from disk are not added to the open
501
+ # workspace files, i.e., the version on disk remains the authoritative
502
+ # version.
503
+ #
504
+ # @raise [FileNotFoundError] if the file does not exist
505
+ # @param filename [String]
506
+ # @return [Solargraph::Source]
507
+ def read filename
508
+ return @current if @current && @current.filename == filename
509
+ raise FileNotFoundError, "File not found: #{filename}" unless workspace.has_file?(filename)
510
+ workspace.source(filename)
511
+ end
512
+
513
+ def handle_file_not_found filename, error
514
+ if workspace.source(filename)
515
+ Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap"
516
+ nil
517
+ else
518
+ raise error
519
+ end
520
+ end
521
+
522
+ def maybe_map source
523
+ return unless source
524
+ return unless @current == source || workspace.has_file?(source.filename)
525
+ if source_map_hash.key?(source.filename)
526
+ return if source_map_hash[source.filename].code == source.code &&
527
+ source_map_hash[source.filename].source.synchronized? &&
528
+ source.synchronized?
529
+ if source.synchronized?
530
+ new_map = Solargraph::SourceMap.map(source)
531
+ unless source_map_hash[source.filename].try_merge!(new_map)
532
+ source_map_hash[source.filename] = new_map
533
+ find_external_requires(source_map_hash[source.filename])
534
+ @synchronized = false
535
+ end
536
+ else
537
+ # @todo Smelly instance variable access
538
+ source_map_hash[source.filename].instance_variable_set(:@source, source)
539
+ end
540
+ else
541
+ source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
542
+ find_external_requires(source_map_hash[source.filename])
543
+ @synchronized = false
544
+ end
545
+ end
546
+ end
547
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ # A section of text identified by its filename and range.
5
+ #
6
+ class Location
7
+ # @return [String]
8
+ attr_reader :filename
9
+
10
+ # @return [Solargraph::Range]
11
+ attr_reader :range
12
+
13
+ # @param filename [String]
14
+ # @param range [Solargraph::Range]
15
+ def initialize filename, range
16
+ @filename = filename
17
+ @range = range
18
+ end
19
+
20
+ # @return [Hash]
21
+ def to_hash
22
+ {
23
+ filename: filename,
24
+ range: range.to_hash
25
+ }
26
+ end
27
+
28
+ def == other
29
+ return false unless other.is_a?(Location)
30
+ filename == other.filename and range == other.range
31
+ end
32
+
33
+ def inspect
34
+ "#<#{self.class} #{filename}, #{range.inspect}>"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Solargraph
6
+ module Logging
7
+ DEFAULT_LOG_LEVEL = Logger::WARN
8
+
9
+ LOG_LEVELS = {
10
+ 'warn' => Logger::WARN,
11
+ 'info' => Logger::INFO,
12
+ 'debug' => Logger::DEBUG
13
+ }
14
+
15
+ @@logger = Logger.new(STDERR, level: DEFAULT_LOG_LEVEL)
16
+ @@logger.formatter = proc do |severity, datetime, progname, msg|
17
+ "[#{severity}] #{msg}\n"
18
+ end
19
+
20
+ module_function
21
+
22
+ # @return [Logger]
23
+ def logger
24
+ @@logger
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ require 'tilt'
5
+ require 'yard'
6
+ require 'cgi'
7
+
8
+ module Solargraph
9
+ class Page
10
+ class Binder < OpenStruct
11
+ # @param locals [Hash]
12
+ # @param render_method [Proc]
13
+ def initialize locals, render_method
14
+ super(locals)
15
+ define_singleton_method :render do |template, layout: false, locals: {}|
16
+ render_method.call(template, layout: layout, locals: locals)
17
+ end
18
+ define_singleton_method :erb do |template, layout: false, locals: {}|
19
+ render_method.call(template, layout: layout, locals: locals)
20
+ end
21
+ end
22
+
23
+ # @param text [String]
24
+ # @return [String]
25
+ def htmlify text
26
+ YARD::Templates::Helpers::Markup::RDocMarkup.new(text).to_html
27
+ end
28
+
29
+ # @param text [String]
30
+ # @return [String]
31
+ def escape text
32
+ CGI.escapeHTML(text)
33
+ end
34
+
35
+ # @param code [String]
36
+ # @return [String]
37
+ def ruby_to_html code
38
+ code
39
+ end
40
+ end
41
+ private_constant :Binder
42
+
43
+ # @param directory [String]
44
+ def initialize directory = VIEWS_PATH
45
+ directory = VIEWS_PATH if directory.nil? or !File.directory?(directory)
46
+ directories = [directory]
47
+ directories.push VIEWS_PATH if directory != VIEWS_PATH
48
+ # @type [Proc]
49
+ # @param template [String]
50
+ # @param layout [Boolean]
51
+ # @param locals [Hash]
52
+ @render_method = proc { |template, layout: false, locals: {}|
53
+ binder = Binder.new(locals, @render_method)
54
+ if layout
55
+ Tilt::ERBTemplate.new(Page.select_template(directories, 'layout')).render(binder) do
56
+ Tilt::ERBTemplate.new(Page.select_template(directories, template)).render(binder)
57
+ end
58
+ else
59
+ Tilt::ERBTemplate.new(Page.select_template(directories, template)).render(binder)
60
+ end
61
+ }
62
+ end
63
+
64
+ # @param template [String]
65
+ # @param layout [Boolean]
66
+ # @param locals [Hash]
67
+ # @return [String]
68
+ def render template, layout: true, locals: {}
69
+ @render_method.call(template, layout: layout, locals: locals)
70
+ end
71
+
72
+ # @param directories [Array<String>]
73
+ # @param name [String]
74
+ # @return [String]
75
+ def self.select_template directories, name
76
+ directories.each do |dir|
77
+ path = File.join(dir, "#{name}.erb")
78
+ return path if File.file?(path)
79
+ end
80
+ raise FileNotFoundError, "Template not found: #{name}"
81
+ end
82
+ end
83
+ end