jimeh-solargraph 0.40.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (252) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +54 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +24 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +1003 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE +21 -0
  10. data/README.md +123 -0
  11. data/Rakefile +25 -0
  12. data/SPONSORS.md +15 -0
  13. data/bin/solargraph +5 -0
  14. data/jimeh-solargraph.gemspec +44 -0
  15. data/lib/.rubocop.yml +21 -0
  16. data/lib/solargraph.rb +67 -0
  17. data/lib/solargraph/api_map.rb +752 -0
  18. data/lib/solargraph/api_map/bundler_methods.rb +27 -0
  19. data/lib/solargraph/api_map/cache.rb +70 -0
  20. data/lib/solargraph/api_map/source_to_yard.rb +81 -0
  21. data/lib/solargraph/api_map/store.rb +256 -0
  22. data/lib/solargraph/bench.rb +30 -0
  23. data/lib/solargraph/compat.rb +23 -0
  24. data/lib/solargraph/complex_type.rb +213 -0
  25. data/lib/solargraph/complex_type/type_methods.rb +127 -0
  26. data/lib/solargraph/complex_type/unique_type.rb +75 -0
  27. data/lib/solargraph/convention.rb +47 -0
  28. data/lib/solargraph/convention/base.rb +33 -0
  29. data/lib/solargraph/convention/gemfile.rb +15 -0
  30. data/lib/solargraph/convention/gemspec.rb +22 -0
  31. data/lib/solargraph/convention/rspec.rb +21 -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.rb +55 -0
  37. data/lib/solargraph/diagnostics/base.rb +29 -0
  38. data/lib/solargraph/diagnostics/require_not_found.rb +37 -0
  39. data/lib/solargraph/diagnostics/rubocop.rb +90 -0
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +45 -0
  41. data/lib/solargraph/diagnostics/severities.rb +15 -0
  42. data/lib/solargraph/diagnostics/type_check.rb +54 -0
  43. data/lib/solargraph/diagnostics/update_errors.rb +41 -0
  44. data/lib/solargraph/documentor.rb +78 -0
  45. data/lib/solargraph/environ.rb +45 -0
  46. data/lib/solargraph/language_server.rb +19 -0
  47. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
  48. data/lib/solargraph/language_server/error_codes.rb +20 -0
  49. data/lib/solargraph/language_server/host.rb +746 -0
  50. data/lib/solargraph/language_server/host/cataloger.rb +56 -0
  51. data/lib/solargraph/language_server/host/diagnoser.rb +81 -0
  52. data/lib/solargraph/language_server/host/dispatch.rb +112 -0
  53. data/lib/solargraph/language_server/host/sources.rb +156 -0
  54. data/lib/solargraph/language_server/message.rb +92 -0
  55. data/lib/solargraph/language_server/message/base.rb +85 -0
  56. data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
  57. data/lib/solargraph/language_server/message/client.rb +11 -0
  58. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
  59. data/lib/solargraph/language_server/message/completion_item.rb +11 -0
  60. data/lib/solargraph/language_server/message/completion_item/resolve.rb +57 -0
  61. data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
  62. data/lib/solargraph/language_server/message/extended.rb +21 -0
  63. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -0
  64. data/lib/solargraph/language_server/message/extended/document.rb +20 -0
  65. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
  66. data/lib/solargraph/language_server/message/extended/download_core.rb +23 -0
  67. data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
  68. data/lib/solargraph/language_server/message/extended/search.rb +20 -0
  69. data/lib/solargraph/language_server/message/initialize.rb +153 -0
  70. data/lib/solargraph/language_server/message/initialized.rb +26 -0
  71. data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
  72. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
  73. data/lib/solargraph/language_server/message/shutdown.rb +13 -0
  74. data/lib/solargraph/language_server/message/text_document.rb +28 -0
  75. data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
  76. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
  77. data/lib/solargraph/language_server/message/text_document/completion.rb +57 -0
  78. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  79. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  80. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
  81. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
  82. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
  83. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
  84. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
  85. data/lib/solargraph/language_server/message/text_document/formatting.rb +105 -0
  86. data/lib/solargraph/language_server/message/text_document/hover.rb +44 -0
  87. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
  88. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
  89. data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
  90. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
  91. data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -0
  92. data/lib/solargraph/language_server/message/workspace.rb +14 -0
  93. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +29 -0
  94. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -0
  95. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
  96. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
  97. data/lib/solargraph/language_server/message_types.rb +14 -0
  98. data/lib/solargraph/language_server/request.rb +24 -0
  99. data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
  100. data/lib/solargraph/language_server/transport.rb +13 -0
  101. data/lib/solargraph/language_server/transport/adapter.rb +56 -0
  102. data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
  103. data/lib/solargraph/language_server/uri_helpers.rb +49 -0
  104. data/lib/solargraph/library.rb +426 -0
  105. data/lib/solargraph/location.rb +37 -0
  106. data/lib/solargraph/logging.rb +27 -0
  107. data/lib/solargraph/page.rb +83 -0
  108. data/lib/solargraph/parser.rb +26 -0
  109. data/lib/solargraph/parser/comment_ripper.rb +52 -0
  110. data/lib/solargraph/parser/legacy.rb +12 -0
  111. data/lib/solargraph/parser/legacy/class_methods.rb +109 -0
  112. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
  113. data/lib/solargraph/parser/legacy/node_chainer.rb +118 -0
  114. data/lib/solargraph/parser/legacy/node_methods.rb +311 -0
  115. data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
  116. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
  117. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
  118. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
  119. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +22 -0
  120. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -0
  121. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
  122. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
  123. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
  124. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
  125. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
  126. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
  127. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
  128. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
  129. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
  130. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -0
  131. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -0
  132. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
  133. data/lib/solargraph/parser/node_methods.rb +43 -0
  134. data/lib/solargraph/parser/node_processor.rb +43 -0
  135. data/lib/solargraph/parser/node_processor/base.rb +80 -0
  136. data/lib/solargraph/parser/region.rb +66 -0
  137. data/lib/solargraph/parser/rubyvm.rb +40 -0
  138. data/lib/solargraph/parser/rubyvm/class_methods.rb +150 -0
  139. data/lib/solargraph/parser/rubyvm/node_chainer.rb +135 -0
  140. data/lib/solargraph/parser/rubyvm/node_methods.rb +301 -0
  141. data/lib/solargraph/parser/rubyvm/node_processors.rb +62 -0
  142. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
  143. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +86 -0
  144. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
  145. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +22 -0
  146. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -0
  147. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
  148. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +64 -0
  149. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -0
  150. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
  151. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
  152. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
  153. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
  154. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
  155. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
  156. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -0
  157. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
  158. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
  159. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -0
  160. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
  161. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +277 -0
  162. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
  163. data/lib/solargraph/parser/snippet.rb +13 -0
  164. data/lib/solargraph/pin.rb +36 -0
  165. data/lib/solargraph/pin/base.rb +296 -0
  166. data/lib/solargraph/pin/base_variable.rb +84 -0
  167. data/lib/solargraph/pin/block.rb +62 -0
  168. data/lib/solargraph/pin/class_variable.rb +8 -0
  169. data/lib/solargraph/pin/closure.rb +37 -0
  170. data/lib/solargraph/pin/common.rb +70 -0
  171. data/lib/solargraph/pin/constant.rb +43 -0
  172. data/lib/solargraph/pin/conversions.rb +96 -0
  173. data/lib/solargraph/pin/documenting.rb +105 -0
  174. data/lib/solargraph/pin/duck_method.rb +16 -0
  175. data/lib/solargraph/pin/global_variable.rb +8 -0
  176. data/lib/solargraph/pin/instance_variable.rb +30 -0
  177. data/lib/solargraph/pin/keyword.rb +15 -0
  178. data/lib/solargraph/pin/keyword_param.rb +8 -0
  179. data/lib/solargraph/pin/local_variable.rb +21 -0
  180. data/lib/solargraph/pin/localized.rb +43 -0
  181. data/lib/solargraph/pin/method.rb +245 -0
  182. data/lib/solargraph/pin/method_alias.rb +31 -0
  183. data/lib/solargraph/pin/namespace.rb +85 -0
  184. data/lib/solargraph/pin/parameter.rb +206 -0
  185. data/lib/solargraph/pin/proxy_type.rb +29 -0
  186. data/lib/solargraph/pin/reference.rb +14 -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/singleton.rb +11 -0
  194. data/lib/solargraph/pin/symbol.rb +47 -0
  195. data/lib/solargraph/position.rb +100 -0
  196. data/lib/solargraph/range.rb +95 -0
  197. data/lib/solargraph/server_methods.rb +16 -0
  198. data/lib/solargraph/shell.rb +222 -0
  199. data/lib/solargraph/source.rb +537 -0
  200. data/lib/solargraph/source/chain.rb +154 -0
  201. data/lib/solargraph/source/chain/block_variable.rb +13 -0
  202. data/lib/solargraph/source/chain/call.rb +203 -0
  203. data/lib/solargraph/source/chain/class_variable.rb +13 -0
  204. data/lib/solargraph/source/chain/constant.rb +75 -0
  205. data/lib/solargraph/source/chain/global_variable.rb +13 -0
  206. data/lib/solargraph/source/chain/head.rb +35 -0
  207. data/lib/solargraph/source/chain/instance_variable.rb +13 -0
  208. data/lib/solargraph/source/chain/link.rb +67 -0
  209. data/lib/solargraph/source/chain/literal.rb +23 -0
  210. data/lib/solargraph/source/chain/or.rb +23 -0
  211. data/lib/solargraph/source/chain/variable.rb +13 -0
  212. data/lib/solargraph/source/chain/z_super.rb +30 -0
  213. data/lib/solargraph/source/change.rb +79 -0
  214. data/lib/solargraph/source/cursor.rb +164 -0
  215. data/lib/solargraph/source/encoding_fixes.rb +23 -0
  216. data/lib/solargraph/source/source_chainer.rb +190 -0
  217. data/lib/solargraph/source/updater.rb +54 -0
  218. data/lib/solargraph/source_map.rb +188 -0
  219. data/lib/solargraph/source_map/clip.rb +224 -0
  220. data/lib/solargraph/source_map/completion.rb +23 -0
  221. data/lib/solargraph/source_map/mapper.rb +215 -0
  222. data/lib/solargraph/type_checker.rb +503 -0
  223. data/lib/solargraph/type_checker/checks.rb +99 -0
  224. data/lib/solargraph/type_checker/param_def.rb +35 -0
  225. data/lib/solargraph/type_checker/problem.rb +32 -0
  226. data/lib/solargraph/type_checker/rules.rb +57 -0
  227. data/lib/solargraph/version.rb +5 -0
  228. data/lib/solargraph/views/_method.erb +62 -0
  229. data/lib/solargraph/views/_name_type_tag.erb +10 -0
  230. data/lib/solargraph/views/_namespace.erb +24 -0
  231. data/lib/solargraph/views/document.erb +23 -0
  232. data/lib/solargraph/views/environment.erb +58 -0
  233. data/lib/solargraph/views/layout.erb +44 -0
  234. data/lib/solargraph/views/search.erb +11 -0
  235. data/lib/solargraph/workspace.rb +209 -0
  236. data/lib/solargraph/workspace/config.rb +230 -0
  237. data/lib/solargraph/yard_map.rb +435 -0
  238. data/lib/solargraph/yard_map/cache.rb +19 -0
  239. data/lib/solargraph/yard_map/core_docs.rb +170 -0
  240. data/lib/solargraph/yard_map/core_fills.rb +185 -0
  241. data/lib/solargraph/yard_map/core_gen.rb +76 -0
  242. data/lib/solargraph/yard_map/helpers.rb +16 -0
  243. data/lib/solargraph/yard_map/mapper.rb +77 -0
  244. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -0
  245. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
  246. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -0
  247. data/lib/solargraph/yard_map/rdoc_to_yard.rb +140 -0
  248. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
  249. data/lib/solargraph/yard_map/to_method.rb +79 -0
  250. data/lib/yard-solargraph.rb +30 -0
  251. data/yardoc/2.2.2.tar.gz +0 -0
  252. metadata +564 -0
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ # A pair of positions that compose a section of text.
5
+ #
6
+ class Range
7
+ # @return [Position]
8
+ attr_reader :start
9
+
10
+ # @return [Position]
11
+ attr_reader :ending
12
+
13
+ # @param start [Position]
14
+ # @param ending [Position]
15
+ def initialize start, ending
16
+ @start = start
17
+ @ending = ending
18
+ end
19
+
20
+ # Get a hash of the range. This representation is suitable for use in
21
+ # the language server protocol.
22
+ #
23
+ # @return [Hash<Symbol, Position>]
24
+ def to_hash
25
+ {
26
+ start: start.to_hash,
27
+ end: ending.to_hash
28
+ }
29
+ end
30
+
31
+ # True if the specified position is inside the range.
32
+ #
33
+ # @param position [Position, Array(Integer, Integer)]
34
+ # @return [Boolean]
35
+ def contain? position
36
+ position = Position.normalize(position)
37
+ return false if position.line < start.line || position.line > ending.line
38
+ return false if position.line == start.line && position.character < start.character
39
+ return false if position.line == ending.line && position.character > ending.character
40
+ true
41
+ end
42
+
43
+ # True if the range contains the specified position and the position does not precede it.
44
+ #
45
+ # @param position [Position, Array(Integer, Integer)]
46
+ # @return [Boolean]
47
+ def include? position
48
+ position = Position.normalize(position)
49
+ contain?(position) && !(position.line == start.line && position.character == start.character)
50
+ end
51
+
52
+ # Create a range from a pair of lines and characters.
53
+ #
54
+ # @param l1 [Integer] Starting line
55
+ # @param c1 [Integer] Starting character
56
+ # @param l2 [Integer] Ending line
57
+ # @param c2 [Integer] Ending character
58
+ # @return [Range]
59
+ def self.from_to l1, c1, l2, c2
60
+ Range.new(Position.new(l1, c1), Position.new(l2, c2))
61
+ end
62
+
63
+ # Get a range from a node.
64
+ #
65
+ # @param node [RubyVM::AbstractSyntaxTree::Node, Parser::AST::Node]
66
+ # @return [Range]
67
+ def self.from_node node
68
+ if defined?(RubyVM::AbstractSyntaxTree::Node)
69
+ if node.is_a?(RubyVM::AbstractSyntaxTree::Node)
70
+ Solargraph::Range.from_to(node.first_lineno - 1, node.first_column, node.last_lineno - 1, node.last_column)
71
+ end
72
+ elsif node.loc && node.loc.expression
73
+ from_expr(node.loc.expression)
74
+ end
75
+ end
76
+
77
+ # Get a range from a Parser range, usually found in
78
+ # Parser::AST::Node#location#expression.
79
+ #
80
+ # @param expr [Parser::Source::Range]
81
+ # @return [Range]
82
+ def self.from_expr expr
83
+ from_to(expr.line, expr.column, expr.last_line, expr.last_column)
84
+ end
85
+
86
+ def == other
87
+ return false unless other.is_a?(Range)
88
+ start == other.start && ending == other.ending
89
+ end
90
+
91
+ def inspect
92
+ "#<#{self.class} #{start.inspect} to #{ending.inspect}>"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module Solargraph
6
+ module ServerMethods
7
+ # @return [Integer]
8
+ def available_port
9
+ socket = Socket.new(:INET, :STREAM, 0)
10
+ socket.bind(Addrinfo.tcp("127.0.0.1", 0))
11
+ port = socket.local_address.ip_port
12
+ socket.close
13
+ port
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Solargraph
6
+ class Shell < Thor
7
+ include Solargraph::ServerMethods
8
+
9
+ map %w[--version -v] => :version
10
+
11
+ desc "--version, -v", "Print the version"
12
+ def version
13
+ puts Solargraph::VERSION
14
+ end
15
+
16
+ desc 'socket', 'Run a Solargraph socket server'
17
+ option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1'
18
+ option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658
19
+ def socket
20
+ require 'backport'
21
+ port = options[:port]
22
+ port = available_port if port.zero?
23
+ Backport.run do
24
+ Signal.trap("INT") do
25
+ Backport.stop
26
+ end
27
+ Signal.trap("TERM") do
28
+ Backport.stop
29
+ end
30
+ Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter
31
+ STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
32
+ end
33
+ end
34
+
35
+ desc 'stdio', 'Run a Solargraph stdio server'
36
+ def stdio
37
+ require 'backport'
38
+ Backport.run do
39
+ Signal.trap("INT") do
40
+ Backport.stop
41
+ end
42
+ Signal.trap("TERM") do
43
+ Backport.stop
44
+ end
45
+ Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
46
+ STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
47
+ end
48
+ end
49
+
50
+ desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
51
+ option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
52
+ def config(directory = '.')
53
+ matches = []
54
+ if options[:extensions]
55
+ Gem::Specification.each do |g|
56
+ if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
57
+ require g.name
58
+ matches.push g.name
59
+ end
60
+ end
61
+ end
62
+ conf = Solargraph::Workspace::Config.new.raw_data
63
+ unless matches.empty?
64
+ matches.each do |m|
65
+ conf['extensions'].push m
66
+ end
67
+ end
68
+ File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
69
+ file.puts conf.to_yaml
70
+ end
71
+ STDOUT.puts "Configuration file initialized."
72
+ end
73
+
74
+ desc 'download-core [VERSION]', 'Download core documentation'
75
+ def download_core version = nil
76
+ ver = version || Solargraph::YardMap::CoreDocs.best_download
77
+ puts "Downloading docs for #{ver}..."
78
+ Solargraph::YardMap::CoreDocs.download ver
79
+ # Clear cached documentation if it exists
80
+ FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
81
+ rescue ArgumentError => e
82
+ STDERR.puts "ERROR: #{e.message}"
83
+ STDERR.puts "Run `solargraph available-cores` for a list."
84
+ exit 1
85
+ end
86
+
87
+ desc 'list-cores', 'List the local documentation versions'
88
+ def list_cores
89
+ puts Solargraph::YardMap::CoreDocs.versions.join("\n")
90
+ end
91
+
92
+ desc 'available-cores', 'List available documentation versions'
93
+ def available_cores
94
+ puts Solargraph::YardMap::CoreDocs.available.join("\n")
95
+ end
96
+
97
+ desc 'clear', 'Delete all cached documentation'
98
+ long_desc %(
99
+ This command will delete all core and gem documentation from the cache.
100
+ You can also delete specific gem caches with the `uncache` command or
101
+ update documentation for specific Ruby versions with the `download-core`
102
+ command.
103
+ )
104
+ def clear
105
+ puts "Deleting the cached documentation"
106
+ Solargraph::YardMap::CoreDocs.clear
107
+ end
108
+ map 'clear-cache' => :clear
109
+ map 'clear-cores' => :clear
110
+
111
+ desc 'uncache GEM [...GEM]', "Delete cached gem documentation"
112
+ def uncache *gems
113
+ raise ArgumentError, 'No gems specified.' if gems.empty?
114
+ gems.each do |gem|
115
+ Dir[File.join(Solargraph::YardMap::CoreDocs.cache_dir, 'gems', "#{gem}-*")].each do |dir|
116
+ puts "Deleting cache: #{dir}"
117
+ FileUtils.remove_entry_secure dir
118
+ end
119
+ end
120
+ end
121
+
122
+ desc 'reporters', 'Get a list of diagnostics reporters'
123
+ def reporters
124
+ puts Solargraph::Diagnostics.reporters
125
+ end
126
+
127
+ desc 'typecheck [FILE(s)]', 'Run the type checker'
128
+ long_desc %(
129
+ Perform type checking on one or more files in a workspace. Check the
130
+ entire workspace if no files are specified.
131
+
132
+ Type checking levels are normal, typed, strict, and strong.
133
+ )
134
+ option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
135
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
136
+ def typecheck *files
137
+ directory = File.realpath(options[:directory])
138
+ api_map = Solargraph::ApiMap.load(directory)
139
+ if files.empty?
140
+ files = api_map.source_maps.map(&:filename)
141
+ else
142
+ files.map! { |file| File.realpath(file) }
143
+ end
144
+ probcount = 0
145
+ filecount = 0
146
+ files.each do |file|
147
+ checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
148
+ problems = checker.problems
149
+ next if problems.empty?
150
+ problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
151
+ puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
152
+ filecount += 1
153
+ probcount += problems.length
154
+ end
155
+ puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
156
+ exit 1 if probcount > 0
157
+ end
158
+
159
+ desc 'scan', 'Test the workspace for problems'
160
+ long_desc %(
161
+ A scan loads the entire workspace to make sure that the ASTs and
162
+ maps do not raise errors during analysis. It does not perform any type
163
+ checking or validation; it only confirms that the analysis itself is
164
+ error-free.
165
+ )
166
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
167
+ option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
168
+ def scan
169
+ require 'benchmark'
170
+ directory = File.realpath(options[:directory])
171
+ api_map = nil
172
+ time = Benchmark.measure {
173
+ api_map = Solargraph::ApiMap.load(directory)
174
+ api_map.pins.each do |pin|
175
+ begin
176
+ puts pin_description(pin) if options[:verbose]
177
+ pin.typify api_map
178
+ pin.probe api_map
179
+ rescue StandardError => e
180
+ STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
181
+ STDERR.puts "[#{e.class}]: #{e.message}"
182
+ STDERR.puts e.backtrace.join("\n")
183
+ exit 1
184
+ end
185
+ end
186
+ }
187
+ puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
188
+ end
189
+
190
+ desc 'bundle', 'Generate documentation for bundled gems'
191
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
192
+ option :rebuild, type: :boolean, aliases: :r, desc: 'Rebuild existing documentation', default: false
193
+ def bundle
194
+ Documentor.new(options[:directory], rebuild: options[:rebuild], out: STDOUT).document
195
+ end
196
+
197
+ desc 'rdoc GEM [VERSION]', 'Use RDoc to cache documentation'
198
+ def rdoc gem, version = '>= 0'
199
+ spec = Gem::Specification.find_by_name(gem, version)
200
+ puts "Caching #{spec.name} #{spec.version} from RDoc"
201
+ Solargraph::YardMap::RdocToYard.run(spec)
202
+ end
203
+
204
+ private
205
+
206
+ # @param pin [Solargraph::Pin::Base]
207
+ # @return [String]
208
+ def pin_description pin
209
+ desc = if pin.path.nil? || pin.path.empty?
210
+ if pin.closure
211
+ "#{pin.closure.path} | #{pin.name}"
212
+ else
213
+ "#{pin.context.namespace} | #{pin.name}"
214
+ end
215
+ else
216
+ pin.path
217
+ end
218
+ desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
219
+ desc
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,537 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yard'
4
+
5
+ module Solargraph
6
+ # A Ruby file that has been parsed into an AST.
7
+ #
8
+ class Source
9
+ # autoload :FlawedBuilder, 'solargraph/source/flawed_builder'
10
+ autoload :Updater, 'solargraph/source/updater'
11
+ autoload :Change, 'solargraph/source/change'
12
+ autoload :Mapper, 'solargraph/source/mapper'
13
+ # autoload :NodeMethods, 'solargraph/source/node_methods'
14
+ autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
15
+ autoload :Cursor, 'solargraph/source/cursor'
16
+ autoload :Chain, 'solargraph/source/chain'
17
+ autoload :SourceChainer, 'solargraph/source/source_chainer'
18
+ # autoload :NodeChainer, 'solargraph/source/node_chainer'
19
+
20
+ include EncodingFixes
21
+ # include NodeMethods
22
+
23
+ # @return [String]
24
+ attr_reader :filename
25
+
26
+ # @return [String]
27
+ attr_reader :code
28
+
29
+ # @return [Parser::AST::Node]
30
+ attr_reader :node
31
+
32
+ # @return [Hash{Integer => Array<String>}]
33
+ attr_reader :comments
34
+
35
+ # @todo Deprecate?
36
+ # @return [Integer]
37
+ attr_reader :version
38
+
39
+ # @param code [String]
40
+ # @param filename [String]
41
+ # @param version [Integer]
42
+ def initialize code, filename = nil, version = 0
43
+ @code = normalize(code)
44
+ @repaired = code
45
+ @filename = filename
46
+ @version = version
47
+ @domains = []
48
+ begin
49
+ # @node, @comments = Source.parse_with_comments(@code, filename)
50
+ @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
51
+ @parsed = true
52
+ rescue Parser::SyntaxError, EncodingError => e
53
+ # @todo 100% whitespace results in a nil node, so there's no reason to parse it.
54
+ # We still need to determine whether the resulting node should be nil or a dummy
55
+ # node with a location that encompasses the range.
56
+ # @node, @comments = Source.parse_with_comments(@code.gsub(/[^\s]/, ' '), filename)
57
+ @node = nil
58
+ @comments = {}
59
+ @parsed = false
60
+ # rescue Exception => e
61
+ # Solargraph.logger.warn "[#{e.class}] #{e.message}"
62
+ # Solargraph.logger.warn e.backtrace.join("\n")
63
+ # raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
64
+ ensure
65
+ @code.freeze
66
+ end
67
+ end
68
+
69
+ # @param range [Solargraph::Range]
70
+ # @return [String]
71
+ def at range
72
+ from_to range.start.line, range.start.character, range.ending.line, range.ending.character
73
+ end
74
+
75
+ # @param l1 [Integer]
76
+ # @param c1 [Integer]
77
+ # @param l2 [Integer]
78
+ # @param c2 [Integer]
79
+ # @return [String]
80
+ def from_to l1, c1, l2, c2
81
+ b = Solargraph::Position.line_char_to_offset(@code, l1, c1)
82
+ e = Solargraph::Position.line_char_to_offset(@code, l2, c2)
83
+ @code[b..e-1]
84
+ end
85
+
86
+ # Get the nearest node that contains the specified index.
87
+ #
88
+ # @param line [Integer]
89
+ # @param column [Integer]
90
+ # @return [AST::Node]
91
+ def node_at(line, column)
92
+ tree_at(line, column).first
93
+ end
94
+
95
+ # Get an array of nodes containing the specified index, starting with the
96
+ # nearest node and ending with the root.
97
+ #
98
+ # @param line [Integer]
99
+ # @param column [Integer]
100
+ # @return [Array<AST::Node>]
101
+ def tree_at(line, column)
102
+ # offset = Position.line_char_to_offset(@code, line, column)
103
+ position = Position.new(line, column)
104
+ stack = []
105
+ inner_tree_at @node, position, stack
106
+ stack
107
+ end
108
+
109
+ # Start synchronizing the source. This method updates the code without
110
+ # parsing a new AST. The resulting Source object will be marked not
111
+ # synchronized (#synchronized? == false).
112
+ #
113
+ # @param updater [Source::Updater]
114
+ # @return [Source]
115
+ def start_synchronize updater
116
+ raise 'Invalid synchronization' unless updater.filename == filename
117
+ real_code = updater.write(@code)
118
+ src = Source.allocate
119
+ src.filename = filename
120
+ src.code = real_code
121
+ src.version = updater.version
122
+ src.parsed = parsed?
123
+ src.repaired = updater.repair(@repaired)
124
+ src.synchronized = false
125
+ src.node = @node
126
+ src.comments = @comments
127
+ src.error_ranges = error_ranges
128
+ src.last_updater = updater
129
+ return src.finish_synchronize unless real_code.lines.length == @code.lines.length
130
+ src
131
+ end
132
+
133
+ # Finish synchronizing a source that was updated via #start_synchronize.
134
+ # This method returns self if the source is already synchronized. Otherwise
135
+ # it parses the AST and returns a new synchronized Source.
136
+ #
137
+ # @return [Source]
138
+ def finish_synchronize
139
+ return self if synchronized?
140
+ synced = Source.new(@code, filename)
141
+ if synced.parsed?
142
+ synced.version = version
143
+ return synced
144
+ end
145
+ synced = Source.new(@repaired, filename)
146
+ synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
147
+ synced.code = @code
148
+ synced.synchronized = true
149
+ synced.version = version
150
+ synced
151
+ end
152
+
153
+ # Synchronize the Source with an update. This method applies changes to the
154
+ # code, parses the new code's AST, and returns the resulting Source object.
155
+ #
156
+ # @param updater [Source::Updater]
157
+ # @return [Source]
158
+ def synchronize updater
159
+ raise 'Invalid synchronization' unless updater.filename == filename
160
+ real_code = updater.write(@code)
161
+ if real_code == @code
162
+ @version = updater.version
163
+ return self
164
+ end
165
+ synced = Source.new(real_code, filename)
166
+ if synced.parsed?
167
+ synced.version = updater.version
168
+ return synced
169
+ end
170
+ incr_code = updater.repair(@repaired)
171
+ synced = Source.new(incr_code, filename)
172
+ synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
173
+ synced.code = real_code
174
+ synced.version = updater.version
175
+ synced
176
+ end
177
+
178
+ # @param position [Position, Array(Integer, Integer)]
179
+ # @return [Source::Cursor]
180
+ def cursor_at position
181
+ Cursor.new(self, position)
182
+ end
183
+
184
+ # @return [Boolean]
185
+ def parsed?
186
+ @parsed
187
+ end
188
+
189
+ def repaired?
190
+ @is_repaired ||= (@code != @repaired)
191
+ end
192
+
193
+ # @param position [Position]
194
+ # @return [Boolean]
195
+ def string_at? position
196
+ if Parser.rubyvm?
197
+ string_ranges.each do |range|
198
+ if synchronized?
199
+ return true if range.include?(position) || range.ending == position
200
+ else
201
+ return true if last_updater && last_updater.changes.one? && range.contain?(last_updater.changes.first.range.start)
202
+ end
203
+ end
204
+ false
205
+ else
206
+ return false if Position.to_offset(code, position) >= code.length
207
+ string_nodes.each do |node|
208
+ range = Range.from_node(node)
209
+ next if range.ending.line < position.line
210
+ break if range.ending.line > position.line
211
+ return true if node.type == :str && range.include?(position) && range.start != position
212
+ return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
213
+ if node.type == :dstr
214
+ inner = node_at(position.line, position.column)
215
+ next if inner.nil?
216
+ inner_range = Range.from_node(inner)
217
+ next unless range.include?(inner_range.ending)
218
+ return true if inner.type == :str
219
+ inner_code = at(Solargraph::Range.new(inner_range.start, position))
220
+ return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
221
+ (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
222
+ end
223
+ break if range.ending.line > position.line
224
+ end
225
+ false
226
+ end
227
+ end
228
+
229
+ def string_ranges
230
+ @string_ranges ||= Parser.string_ranges(node)
231
+ end
232
+
233
+ # @param position [Position]
234
+ # @return [Boolean]
235
+ def comment_at? position
236
+ comment_ranges.each do |range|
237
+ return true if range.include?(position) ||
238
+ (range.ending.line == position.line && range.ending.column < position.column)
239
+ break if range.ending.line > position.line
240
+ end
241
+ false
242
+ end
243
+
244
+ # @param name [String]
245
+ # @return [Array<Location>]
246
+ def references name
247
+ Parser.references self, name
248
+ end
249
+
250
+ # @return [Array<Range>]
251
+ def error_ranges
252
+ @error_ranges ||= []
253
+ end
254
+
255
+ # @param node [Parser::AST::Node]
256
+ # @return [String]
257
+ def code_for(node)
258
+ rng = Range.from_node(node)
259
+ b = Position.line_char_to_offset(@code, rng.start.line, rng.start.column)
260
+ e = Position.line_char_to_offset(@code, rng.ending.line, rng.ending.column)
261
+ frag = code[b..e-1].to_s
262
+ frag.strip.gsub(/,$/, '')
263
+ end
264
+
265
+ # @param node [Parser::AST::Node]
266
+ # @return [String]
267
+ def comments_for node
268
+ rng = Range.from_node(node)
269
+ stringified_comments[rng.start.line] ||= begin
270
+ buff = associated_comments[rng.start.line]
271
+ buff ? stringify_comment_array(buff) : nil
272
+ end
273
+ end
274
+
275
+ # A location representing the file in its entirety.
276
+ #
277
+ # @return [Location]
278
+ def location
279
+ st = Position.new(0, 0)
280
+ en = Position.from_offset(code, code.length)
281
+ range = Range.new(st, en)
282
+ Location.new(filename, range)
283
+ end
284
+
285
+ FOLDING_NODE_TYPES = if Parser.rubyvm?
286
+ %i[
287
+ CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
288
+ ].freeze
289
+ else
290
+ %i[
291
+ class sclass module def defs if str dstr array while unless kwbegin hash block
292
+ ].freeze
293
+ end
294
+
295
+ # Get an array of ranges that can be folded, e.g., the range of a class
296
+ # definition or an if condition.
297
+ #
298
+ # See FOLDING_NODE_TYPES for the list of node types that can be folded.
299
+ #
300
+ # @return [Array<Range>]
301
+ def folding_ranges
302
+ @folding_ranges ||= begin
303
+ result = []
304
+ inner_folding_ranges node, result
305
+ result.concat foldable_comment_block_ranges
306
+ result
307
+ end
308
+ end
309
+
310
+ def synchronized?
311
+ @synchronized = true if @synchronized.nil?
312
+ @synchronized
313
+ end
314
+
315
+ # Get a hash of comments grouped by the line numbers of the associated code.
316
+ #
317
+ # @return [Hash{Integer => Array<Parser::Source::Comment>}]
318
+ def associated_comments
319
+ @associated_comments ||= begin
320
+ result = {}
321
+ buffer = String.new('')
322
+ last = nil
323
+ @comments.each_pair do |num, snip|
324
+ if !last || num == last + 1
325
+ buffer.concat "#{snip.text}\n"
326
+ else
327
+ result[first_not_empty_from(last + 1)] = buffer.clone
328
+ buffer.replace "#{snip.text}\n"
329
+ end
330
+ last = num
331
+ end
332
+ result[first_not_empty_from(last + 1)] = buffer unless buffer.empty? || last.nil?
333
+ result
334
+ end
335
+ end
336
+
337
+ private
338
+
339
+ def first_not_empty_from line
340
+ cursor = line
341
+ cursor += 1 while cursor < code_lines.length && code_lines[cursor].strip.empty?
342
+ cursor = line if cursor > code_lines.length - 1
343
+ cursor
344
+ end
345
+
346
+ # @param top [Parser::AST::Node]
347
+ # @param result [Array<Range>]
348
+ # @param parent [Symbol]
349
+ # @return [void]
350
+ def inner_folding_ranges top, result = [], parent = nil
351
+ # return unless top.is_a?(::Parser::AST::Node)
352
+ return unless Parser.is_ast_node?(top)
353
+ if FOLDING_NODE_TYPES.include?(top.type)
354
+ # @todo Smelly exception for hash's first-level array in RubyVM
355
+ unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
356
+ range = Range.from_node(top)
357
+ if result.empty? || range.start.line > result.last.start.line
358
+ result.push range unless range.ending.line - range.start.line < 2
359
+ end
360
+ end
361
+ end
362
+ top.children.each do |child|
363
+ inner_folding_ranges(child, result, top.type)
364
+ end
365
+ end
366
+
367
+ # Get a string representation of an array of comments.
368
+ #
369
+ # @param comments [String]
370
+ # @return [String]
371
+ def stringify_comment_array comments
372
+ ctxt = String.new('')
373
+ started = false
374
+ skip = nil
375
+ comments.lines.each { |l|
376
+ # Trim the comment and minimum leading whitespace
377
+ p = l.gsub(/^#+/, '')
378
+ if p.strip.empty?
379
+ next unless started
380
+ ctxt.concat p
381
+ else
382
+ here = p.index(/[^ \t]/)
383
+ skip = here if skip.nil? || here < skip
384
+ ctxt.concat p[skip..-1]
385
+ end
386
+ started = true
387
+ }
388
+ ctxt
389
+ end
390
+
391
+ # A hash of line numbers and their associated comments.
392
+ #
393
+ # @return [Hash{Integer => Array<String>}]
394
+ def stringified_comments
395
+ @stringified_comments ||= {}
396
+ end
397
+
398
+ # @return [Array<Parser::AST::Node>]
399
+ def string_nodes
400
+ @string_nodes ||= string_nodes_in(@node)
401
+ end
402
+
403
+ # @return [Array<Range>]
404
+ def comment_ranges
405
+ @comment_ranges ||= @comments.values.map(&:range)
406
+ end
407
+
408
+ # Get an array of foldable comment block ranges. Blocks are excluded if
409
+ # they are less than 3 lines long.
410
+ #
411
+ # @return [Array<Range>]
412
+ def foldable_comment_block_ranges
413
+ return [] unless synchronized?
414
+ result = []
415
+ grouped = []
416
+ comments.keys.each do |l|
417
+ if grouped.empty? || l == grouped.last + 1
418
+ grouped.push l
419
+ else
420
+ result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
421
+ grouped = [l]
422
+ end
423
+ end
424
+ result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
425
+ result
426
+ end
427
+
428
+ # @param n [Parser::AST::Node]
429
+ # @return [Array<Parser::AST::Node>]
430
+ def string_nodes_in n
431
+ result = []
432
+ if Parser.is_ast_node?(n)
433
+ if n.type == :str || n.type == :dstr || n.type == :STR || n.type == :DSTR
434
+ result.push n
435
+ else
436
+ n.children.each{ |c| result.concat string_nodes_in(c) }
437
+ end
438
+ end
439
+ result
440
+ end
441
+
442
+ # @param node [Parser::AST::Node]
443
+ # @param position [Position]
444
+ # @param stack [Array<Parser::AST::Node>]
445
+ # @return [void]
446
+ def inner_tree_at node, position, stack
447
+ return if node.nil?
448
+ # here = Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
449
+ here = Range.from_node(node)
450
+ if here.contain?(position) || colonized(here, position, node)
451
+ stack.unshift node
452
+ node.children.each do |c|
453
+ next unless Parser.is_ast_node?(c)
454
+ next if !Parser.rubyvm? && c.loc.expression.nil?
455
+ inner_tree_at(c, position, stack)
456
+ end
457
+ end
458
+ end
459
+
460
+ def colonized range, position, node
461
+ node.type == :COLON2 &&
462
+ range.ending.line == position.line &&
463
+ range.ending.character == position.character - 2 &&
464
+ code[Position.to_offset(code, Position.new(position.line, position.character - 2)), 2] == '::'
465
+ end
466
+
467
+ protected
468
+
469
+ # @return [String]
470
+ attr_writer :filename
471
+
472
+ # @return [Integer]
473
+ attr_writer :version
474
+
475
+ # @param val [String]
476
+ # @return [String]
477
+ def code=(val)
478
+ @code_lines= nil
479
+ @code = val
480
+ end
481
+
482
+ # @return [Parser::AST::Node]
483
+ attr_writer :node
484
+
485
+ # @return [Array<Range>]
486
+ attr_writer :error_ranges
487
+
488
+ # @return [String]
489
+ attr_accessor :repaired
490
+
491
+ # @return [Boolean]
492
+ attr_writer :parsed
493
+
494
+ # @return [Array<Parser::Source::Comment>]
495
+ attr_writer :comments
496
+
497
+ # @return [Boolean]
498
+ attr_writer :synchronized
499
+
500
+ # @return [Source::Updater]
501
+ attr_accessor :last_updater
502
+
503
+ private
504
+
505
+ # @return [Array<String>]
506
+ def code_lines
507
+ @code_lines ||= code.lines
508
+ end
509
+
510
+ class << self
511
+ # @param filename [String]
512
+ # @return [Solargraph::Source]
513
+ def load filename
514
+ file = File.open(filename)
515
+ code = file.read
516
+ file.close
517
+ Source.load_string(code, filename)
518
+ end
519
+
520
+ # @param code [String]
521
+ # @param filename [String]
522
+ # @param version [Integer]
523
+ # @return [Solargraph::Source]
524
+ def load_string code, filename = nil, version = 0
525
+ Source.new code, filename, version
526
+ end
527
+
528
+ # @param comments [String]
529
+ # @return [YARD::DocstringParser]
530
+ def parse_docstring comments
531
+ # HACK: Pass a dummy code object to the parser for plugins that
532
+ # expect it not to be nil
533
+ YARD::Docstring.parser.parse(comments, YARD::CodeObjects::Base.new(:root, 'stub'))
534
+ end
535
+ end
536
+ end
537
+ end