solargraph 0.46.0 → 0.47.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -41
  3. data/.gitignore +9 -9
  4. data/.rspec +2 -2
  5. data/.travis.yml +19 -19
  6. data/CHANGELOG.md +1123 -1115
  7. data/Gemfile +0 -0
  8. data/LICENSE +0 -0
  9. data/README.md +128 -128
  10. data/Rakefile +0 -0
  11. data/SPONSORS.md +17 -18
  12. data/bin/solargraph +0 -0
  13. data/lib/solargraph/api_map/bundler_methods.rb +22 -22
  14. data/lib/solargraph/api_map/cache.rb +70 -70
  15. data/lib/solargraph/api_map/source_to_yard.rb +81 -81
  16. data/lib/solargraph/api_map/store.rb +256 -256
  17. data/lib/solargraph/api_map.rb +686 -686
  18. data/lib/solargraph/bench.rb +27 -27
  19. data/lib/solargraph/compat.rb +37 -37
  20. data/lib/solargraph/complex_type/type_methods.rb +130 -130
  21. data/lib/solargraph/complex_type/unique_type.rb +75 -75
  22. data/lib/solargraph/complex_type.rb +221 -221
  23. data/lib/solargraph/convention/base.rb +33 -33
  24. data/lib/solargraph/convention/gemfile.rb +15 -15
  25. data/lib/solargraph/convention/gemspec.rb +22 -22
  26. data/lib/solargraph/convention/rspec.rb +30 -30
  27. data/lib/solargraph/convention.rb +47 -47
  28. data/lib/solargraph/converters/dd.rb +12 -12
  29. data/lib/solargraph/converters/dl.rb +12 -12
  30. data/lib/solargraph/converters/dt.rb +12 -12
  31. data/lib/solargraph/converters/misc.rb +1 -1
  32. data/lib/solargraph/diagnostics/base.rb +29 -29
  33. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  34. data/lib/solargraph/diagnostics/rubocop.rb +98 -98
  35. data/lib/solargraph/diagnostics/rubocop_helpers.rb +63 -63
  36. data/lib/solargraph/diagnostics/severities.rb +15 -15
  37. data/lib/solargraph/diagnostics/type_check.rb +54 -54
  38. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  39. data/lib/solargraph/diagnostics.rb +55 -55
  40. data/lib/solargraph/documentor.rb +76 -76
  41. data/lib/solargraph/environ.rb +45 -45
  42. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -35
  43. data/lib/solargraph/language_server/error_codes.rb +20 -20
  44. data/lib/solargraph/language_server/host/cataloger.rb +56 -56
  45. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  46. data/lib/solargraph/language_server/host/dispatch.rb +111 -111
  47. data/lib/solargraph/language_server/host/message_worker.rb +59 -59
  48. data/lib/solargraph/language_server/host/sources.rb +156 -156
  49. data/lib/solargraph/language_server/host.rb +865 -865
  50. data/lib/solargraph/language_server/message/base.rb +89 -89
  51. data/lib/solargraph/language_server/message/cancel_request.rb +13 -13
  52. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  53. data/lib/solargraph/language_server/message/client.rb +11 -11
  54. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -58
  55. data/lib/solargraph/language_server/message/completion_item.rb +11 -11
  56. data/lib/solargraph/language_server/message/exit_notification.rb +13 -13
  57. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -100
  58. data/lib/solargraph/language_server/message/extended/document.rb +20 -20
  59. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  60. data/lib/solargraph/language_server/message/extended/download_core.rb +23 -23
  61. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/extended.rb +21 -21
  64. data/lib/solargraph/language_server/message/initialize.rb +162 -162
  65. data/lib/solargraph/language_server/message/initialized.rb +27 -27
  66. data/lib/solargraph/language_server/message/method_not_found.rb +16 -16
  67. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -14
  68. data/lib/solargraph/language_server/message/shutdown.rb +13 -13
  69. data/lib/solargraph/language_server/message/text_document/base.rb +19 -19
  70. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -17
  71. data/lib/solargraph/language_server/message/text_document/completion.rb +59 -59
  72. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -38
  73. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -15
  74. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -15
  75. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -15
  76. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -17
  77. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  78. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -23
  79. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -26
  80. data/lib/solargraph/language_server/message/text_document/formatting.rb +126 -126
  81. data/lib/solargraph/language_server/message/text_document/hover.rb +56 -54
  82. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -34
  83. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  84. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  85. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  86. data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -29
  87. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  88. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +30 -30
  89. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -33
  90. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -24
  91. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  92. data/lib/solargraph/language_server/message/workspace.rb +14 -14
  93. data/lib/solargraph/language_server/message.rb +93 -93
  94. data/lib/solargraph/language_server/message_types.rb +14 -14
  95. data/lib/solargraph/language_server/request.rb +24 -24
  96. data/lib/solargraph/language_server/symbol_kinds.rb +36 -36
  97. data/lib/solargraph/language_server/transport/adapter.rb +53 -53
  98. data/lib/solargraph/language_server/transport/data_reader.rb +72 -72
  99. data/lib/solargraph/language_server/transport.rb +13 -13
  100. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  101. data/lib/solargraph/language_server.rb +19 -19
  102. data/lib/solargraph/library.rb +546 -546
  103. data/lib/solargraph/location.rb +37 -37
  104. data/lib/solargraph/logging.rb +27 -27
  105. data/lib/solargraph/page.rb +83 -83
  106. data/lib/solargraph/parser/comment_ripper.rb +52 -52
  107. data/lib/solargraph/parser/legacy/class_methods.rb +135 -135
  108. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -16
  109. data/lib/solargraph/parser/legacy/node_chainer.rb +148 -148
  110. data/lib/solargraph/parser/legacy/node_methods.rb +325 -325
  111. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -23
  112. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -35
  113. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -15
  114. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +42 -42
  115. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -25
  116. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -23
  117. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -63
  118. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -36
  119. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -23
  120. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -38
  121. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -28
  122. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -39
  123. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -16
  124. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -36
  125. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -21
  126. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -257
  127. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -18
  128. data/lib/solargraph/parser/legacy/node_processors.rb +54 -54
  129. data/lib/solargraph/parser/legacy.rb +12 -12
  130. data/lib/solargraph/parser/node_methods.rb +43 -43
  131. data/lib/solargraph/parser/node_processor/base.rb +77 -77
  132. data/lib/solargraph/parser/node_processor.rb +43 -43
  133. data/lib/solargraph/parser/region.rb +66 -66
  134. data/lib/solargraph/parser/rubyvm/class_methods.rb +144 -144
  135. data/lib/solargraph/parser/rubyvm/node_chainer.rb +160 -160
  136. data/lib/solargraph/parser/rubyvm/node_methods.rb +315 -315
  137. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -23
  138. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +85 -85
  139. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -15
  140. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +42 -42
  141. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -22
  142. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -23
  143. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +63 -63
  144. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -57
  145. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -23
  146. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -38
  147. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -39
  148. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -20
  149. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -27
  150. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -39
  151. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -26
  152. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -15
  153. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -45
  154. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -21
  155. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -15
  156. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +277 -277
  157. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -18
  158. data/lib/solargraph/parser/rubyvm/node_processors.rb +63 -63
  159. data/lib/solargraph/parser/rubyvm.rb +40 -40
  160. data/lib/solargraph/parser/snippet.rb +13 -13
  161. data/lib/solargraph/parser.rb +26 -26
  162. data/lib/solargraph/pin/base.rb +296 -296
  163. data/lib/solargraph/pin/base_variable.rb +84 -84
  164. data/lib/solargraph/pin/block.rb +73 -72
  165. data/lib/solargraph/pin/class_variable.rb +8 -8
  166. data/lib/solargraph/pin/closure.rb +37 -37
  167. data/lib/solargraph/pin/common.rb +70 -70
  168. data/lib/solargraph/pin/constant.rb +43 -43
  169. data/lib/solargraph/pin/conversions.rb +96 -96
  170. data/lib/solargraph/pin/documenting.rb +105 -105
  171. data/lib/solargraph/pin/duck_method.rb +16 -16
  172. data/lib/solargraph/pin/global_variable.rb +8 -8
  173. data/lib/solargraph/pin/instance_variable.rb +30 -30
  174. data/lib/solargraph/pin/keyword.rb +15 -15
  175. data/lib/solargraph/pin/keyword_param.rb +8 -8
  176. data/lib/solargraph/pin/local_variable.rb +55 -55
  177. data/lib/solargraph/pin/method.rb +245 -245
  178. data/lib/solargraph/pin/method_alias.rb +31 -31
  179. data/lib/solargraph/pin/namespace.rb +91 -91
  180. data/lib/solargraph/pin/parameter.rb +201 -201
  181. data/lib/solargraph/pin/proxy_type.rb +29 -29
  182. data/lib/solargraph/pin/reference/extend.rb +10 -10
  183. data/lib/solargraph/pin/reference/include.rb +10 -10
  184. data/lib/solargraph/pin/reference/override.rb +29 -29
  185. data/lib/solargraph/pin/reference/prepend.rb +10 -10
  186. data/lib/solargraph/pin/reference/require.rb +14 -14
  187. data/lib/solargraph/pin/reference/superclass.rb +10 -10
  188. data/lib/solargraph/pin/reference.rb +14 -14
  189. data/lib/solargraph/pin/search.rb +56 -56
  190. data/lib/solargraph/pin/singleton.rb +11 -11
  191. data/lib/solargraph/pin/symbol.rb +47 -47
  192. data/lib/solargraph/pin.rb +37 -37
  193. data/lib/solargraph/position.rb +100 -100
  194. data/lib/solargraph/range.rb +95 -95
  195. data/lib/solargraph/server_methods.rb +16 -16
  196. data/lib/solargraph/shell.rb +230 -226
  197. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  198. data/lib/solargraph/source/chain/call.rb +204 -204
  199. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  200. data/lib/solargraph/source/chain/constant.rb +75 -75
  201. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  202. data/lib/solargraph/source/chain/hash.rb +28 -28
  203. data/lib/solargraph/source/chain/head.rb +19 -19
  204. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  205. data/lib/solargraph/source/chain/link.rb +71 -71
  206. data/lib/solargraph/source/chain/literal.rb +23 -23
  207. data/lib/solargraph/source/chain/or.rb +23 -23
  208. data/lib/solargraph/source/chain/q_call.rb +11 -11
  209. data/lib/solargraph/source/chain/variable.rb +13 -13
  210. data/lib/solargraph/source/chain/z_super.rb +30 -30
  211. data/lib/solargraph/source/chain.rb +164 -164
  212. data/lib/solargraph/source/change.rb +79 -79
  213. data/lib/solargraph/source/cursor.rb +164 -164
  214. data/lib/solargraph/source/source_chainer.rb +191 -191
  215. data/lib/solargraph/source/updater.rb +54 -54
  216. data/lib/solargraph/source.rb +524 -522
  217. data/lib/solargraph/source_map/clip.rb +224 -224
  218. data/lib/solargraph/source_map/completion.rb +23 -23
  219. data/lib/solargraph/source_map/mapper.rb +239 -212
  220. data/lib/solargraph/source_map.rb +180 -180
  221. data/lib/solargraph/type_checker/checks.rb +99 -99
  222. data/lib/solargraph/type_checker/param_def.rb +35 -35
  223. data/lib/solargraph/type_checker/problem.rb +32 -32
  224. data/lib/solargraph/type_checker/rules.rb +57 -57
  225. data/lib/solargraph/type_checker.rb +543 -543
  226. data/lib/solargraph/version.rb +5 -5
  227. data/lib/solargraph/views/environment.erb +58 -58
  228. data/lib/solargraph/workspace/config.rb +231 -231
  229. data/lib/solargraph/workspace.rb +215 -215
  230. data/lib/solargraph/yard_map/cache.rb +19 -19
  231. data/lib/solargraph/yard_map/core_docs.rb +170 -170
  232. data/lib/solargraph/yard_map/core_fills.rb +208 -208
  233. data/lib/solargraph/yard_map/core_gen.rb +76 -76
  234. data/lib/solargraph/yard_map/helpers.rb +16 -16
  235. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -25
  236. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -78
  237. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -27
  238. data/lib/solargraph/yard_map/mapper.rb +77 -77
  239. data/lib/solargraph/yard_map/rdoc_to_yard.rb +140 -140
  240. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -43
  241. data/lib/solargraph/yard_map/to_method.rb +79 -79
  242. data/lib/solargraph/yard_map.rb +460 -460
  243. data/lib/solargraph.rb +69 -69
  244. data/lib/yard-solargraph.rb +33 -33
  245. data/solargraph.gemspec +0 -0
  246. metadata +12 -12
@@ -1,522 +1,524 @@
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 :Updater, 'solargraph/source/updater'
10
- autoload :Change, 'solargraph/source/change'
11
- autoload :Mapper, 'solargraph/source/mapper'
12
- autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
13
- autoload :Cursor, 'solargraph/source/cursor'
14
- autoload :Chain, 'solargraph/source/chain'
15
- autoload :SourceChainer, 'solargraph/source/source_chainer'
16
-
17
- include EncodingFixes
18
-
19
- # @return [String]
20
- attr_reader :filename
21
-
22
- # @return [String]
23
- attr_reader :code
24
-
25
- # @return [Parser::AST::Node]
26
- attr_reader :node
27
-
28
- # @return [Hash{Integer => Array<String>}]
29
- attr_reader :comments
30
-
31
- # @todo Deprecate?
32
- # @return [Integer]
33
- attr_reader :version
34
-
35
- # @param code [String]
36
- # @param filename [String]
37
- # @param version [Integer]
38
- def initialize code, filename = nil, version = 0
39
- @code = normalize(code)
40
- @repaired = code
41
- @filename = filename
42
- @version = version
43
- @domains = []
44
- begin
45
- @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
46
- @parsed = true
47
- rescue Parser::SyntaxError, EncodingError => e
48
- @node = nil
49
- @comments = {}
50
- @parsed = false
51
- ensure
52
- @code.freeze
53
- end
54
- end
55
-
56
- # @param range [Solargraph::Range]
57
- # @return [String]
58
- def at range
59
- from_to range.start.line, range.start.character, range.ending.line, range.ending.character
60
- end
61
-
62
- # @param l1 [Integer]
63
- # @param c1 [Integer]
64
- # @param l2 [Integer]
65
- # @param c2 [Integer]
66
- # @return [String]
67
- def from_to l1, c1, l2, c2
68
- b = Solargraph::Position.line_char_to_offset(@code, l1, c1)
69
- e = Solargraph::Position.line_char_to_offset(@code, l2, c2)
70
- @code[b..e-1]
71
- end
72
-
73
- # Get the nearest node that contains the specified index.
74
- #
75
- # @param line [Integer]
76
- # @param column [Integer]
77
- # @return [AST::Node]
78
- def node_at(line, column)
79
- tree_at(line, column).first
80
- end
81
-
82
- # Get an array of nodes containing the specified index, starting with the
83
- # nearest node and ending with the root.
84
- #
85
- # @param line [Integer]
86
- # @param column [Integer]
87
- # @return [Array<AST::Node>]
88
- def tree_at(line, column)
89
- # offset = Position.line_char_to_offset(@code, line, column)
90
- position = Position.new(line, column)
91
- stack = []
92
- inner_tree_at @node, position, stack
93
- stack
94
- end
95
-
96
- # Start synchronizing the source. This method updates the code without
97
- # parsing a new AST. The resulting Source object will be marked not
98
- # synchronized (#synchronized? == false).
99
- #
100
- # @param updater [Source::Updater]
101
- # @return [Source]
102
- def start_synchronize updater
103
- raise 'Invalid synchronization' unless updater.filename == filename
104
- real_code = updater.write(@code)
105
- src = Source.allocate
106
- src.filename = filename
107
- src.code = real_code
108
- src.version = updater.version
109
- src.parsed = parsed?
110
- src.repaired = updater.repair(@repaired)
111
- src.synchronized = false
112
- src.node = @node
113
- src.comments = @comments
114
- src.error_ranges = error_ranges
115
- src.last_updater = updater
116
- return src.finish_synchronize unless real_code.lines.length == @code.lines.length
117
- src
118
- end
119
-
120
- # Finish synchronizing a source that was updated via #start_synchronize.
121
- # This method returns self if the source is already synchronized. Otherwise
122
- # it parses the AST and returns a new synchronized Source.
123
- #
124
- # @return [Source]
125
- def finish_synchronize
126
- return self if synchronized?
127
- synced = Source.new(@code, filename)
128
- if synced.parsed?
129
- synced.version = version
130
- return synced
131
- end
132
- synced = Source.new(@repaired, filename)
133
- synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
134
- synced.code = @code
135
- synced.synchronized = true
136
- synced.version = version
137
- synced
138
- end
139
-
140
- # Synchronize the Source with an update. This method applies changes to the
141
- # code, parses the new code's AST, and returns the resulting Source object.
142
- #
143
- # @param updater [Source::Updater]
144
- # @return [Source]
145
- def synchronize updater
146
- raise 'Invalid synchronization' unless updater.filename == filename
147
- real_code = updater.write(@code)
148
- if real_code == @code
149
- @version = updater.version
150
- return self
151
- end
152
- synced = Source.new(real_code, filename)
153
- if synced.parsed?
154
- synced.version = updater.version
155
- return synced
156
- end
157
- incr_code = updater.repair(@repaired)
158
- synced = Source.new(incr_code, filename)
159
- synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
160
- synced.code = real_code
161
- synced.version = updater.version
162
- synced
163
- end
164
-
165
- # @param position [Position, Array(Integer, Integer)]
166
- # @return [Source::Cursor]
167
- def cursor_at position
168
- Cursor.new(self, position)
169
- end
170
-
171
- # @return [Boolean]
172
- def parsed?
173
- @parsed
174
- end
175
-
176
- def repaired?
177
- @is_repaired ||= (@code != @repaired)
178
- end
179
-
180
- # @param position [Position]
181
- # @return [Boolean]
182
- def string_at? position
183
- if Parser.rubyvm?
184
- string_ranges.each do |range|
185
- if synchronized?
186
- return true if range.include?(position) || range.ending == position
187
- else
188
- return true if last_updater && last_updater.changes.one? && range.contain?(last_updater.changes.first.range.start)
189
- end
190
- end
191
- false
192
- else
193
- return false if Position.to_offset(code, position) >= code.length
194
- string_nodes.each do |node|
195
- range = Range.from_node(node)
196
- next if range.ending.line < position.line
197
- break if range.ending.line > position.line
198
- return true if node.type == :str && range.include?(position) && range.start != position
199
- return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
200
- if node.type == :dstr
201
- inner = node_at(position.line, position.column)
202
- next if inner.nil?
203
- inner_range = Range.from_node(inner)
204
- next unless range.include?(inner_range.ending)
205
- return true if inner.type == :str
206
- inner_code = at(Solargraph::Range.new(inner_range.start, position))
207
- return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
208
- (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
209
- end
210
- break if range.ending.line > position.line
211
- end
212
- false
213
- end
214
- end
215
-
216
- def string_ranges
217
- @string_ranges ||= Parser.string_ranges(node)
218
- end
219
-
220
- # @param position [Position]
221
- # @return [Boolean]
222
- def comment_at? position
223
- comment_ranges.each do |range|
224
- return true if range.include?(position) ||
225
- (range.ending.line == position.line && range.ending.column < position.column)
226
- break if range.ending.line > position.line
227
- end
228
- false
229
- end
230
-
231
- # @param name [String]
232
- # @return [Array<Location>]
233
- def references name
234
- Parser.references self, name
235
- end
236
-
237
- # @return [Array<Range>]
238
- def error_ranges
239
- @error_ranges ||= []
240
- end
241
-
242
- # @param node [Parser::AST::Node]
243
- # @return [String]
244
- def code_for(node)
245
- rng = Range.from_node(node)
246
- b = Position.line_char_to_offset(@code, rng.start.line, rng.start.column)
247
- e = Position.line_char_to_offset(@code, rng.ending.line, rng.ending.column)
248
- frag = code[b..e-1].to_s
249
- frag.strip.gsub(/,$/, '')
250
- end
251
-
252
- # @param node [Parser::AST::Node]
253
- # @return [String]
254
- def comments_for node
255
- rng = Range.from_node(node)
256
- stringified_comments[rng.start.line] ||= begin
257
- buff = associated_comments[rng.start.line]
258
- buff ? stringify_comment_array(buff) : nil
259
- end
260
- end
261
-
262
- # A location representing the file in its entirety.
263
- #
264
- # @return [Location]
265
- def location
266
- st = Position.new(0, 0)
267
- en = Position.from_offset(code, code.length)
268
- range = Range.new(st, en)
269
- Location.new(filename, range)
270
- end
271
-
272
- FOLDING_NODE_TYPES = if Parser.rubyvm?
273
- %i[
274
- CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
275
- ].freeze
276
- else
277
- %i[
278
- class sclass module def defs if str dstr array while unless kwbegin hash block
279
- ].freeze
280
- end
281
-
282
- # Get an array of ranges that can be folded, e.g., the range of a class
283
- # definition or an if condition.
284
- #
285
- # See FOLDING_NODE_TYPES for the list of node types that can be folded.
286
- #
287
- # @return [Array<Range>]
288
- def folding_ranges
289
- @folding_ranges ||= begin
290
- result = []
291
- inner_folding_ranges node, result
292
- result.concat foldable_comment_block_ranges
293
- result
294
- end
295
- end
296
-
297
- def synchronized?
298
- @synchronized = true if @synchronized.nil?
299
- @synchronized
300
- end
301
-
302
- # Get a hash of comments grouped by the line numbers of the associated code.
303
- #
304
- # @return [Hash{Integer => Array<Parser::Source::Comment>}]
305
- def associated_comments
306
- @associated_comments ||= begin
307
- result = {}
308
- buffer = String.new('')
309
- last = nil
310
- @comments.each_pair do |num, snip|
311
- if !last || num == last + 1
312
- buffer.concat "#{snip.text}\n"
313
- else
314
- result[first_not_empty_from(last + 1)] = buffer.clone
315
- buffer.replace "#{snip.text}\n"
316
- end
317
- last = num
318
- end
319
- result[first_not_empty_from(last + 1)] = buffer unless buffer.empty? || last.nil?
320
- result
321
- end
322
- end
323
-
324
- private
325
-
326
- def first_not_empty_from line
327
- cursor = line
328
- cursor += 1 while cursor < code_lines.length && code_lines[cursor].strip.empty?
329
- cursor = line if cursor > code_lines.length - 1
330
- cursor
331
- end
332
-
333
- # @param top [Parser::AST::Node]
334
- # @param result [Array<Range>]
335
- # @param parent [Symbol]
336
- # @return [void]
337
- def inner_folding_ranges top, result = [], parent = nil
338
- return unless Parser.is_ast_node?(top)
339
- if FOLDING_NODE_TYPES.include?(top.type)
340
- # @todo Smelly exception for hash's first-level array in RubyVM
341
- unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
342
- range = Range.from_node(top)
343
- if result.empty? || range.start.line > result.last.start.line
344
- result.push range unless range.ending.line - range.start.line < 2
345
- end
346
- end
347
- end
348
- top.children.each do |child|
349
- inner_folding_ranges(child, result, top.type)
350
- end
351
- end
352
-
353
- # Get a string representation of an array of comments.
354
- #
355
- # @param comments [String]
356
- # @return [String]
357
- def stringify_comment_array comments
358
- ctxt = String.new('')
359
- started = false
360
- skip = nil
361
- comments.lines.each { |l|
362
- # Trim the comment and minimum leading whitespace
363
- p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
364
- if p.strip.empty?
365
- next unless started
366
- ctxt.concat p
367
- else
368
- here = p.index(/[^ \t]/)
369
- skip = here if skip.nil? || here < skip
370
- ctxt.concat p[skip..-1]
371
- end
372
- started = true
373
- }
374
- ctxt
375
- end
376
-
377
- # A hash of line numbers and their associated comments.
378
- #
379
- # @return [Hash{Integer => Array<String>}]
380
- def stringified_comments
381
- @stringified_comments ||= {}
382
- end
383
-
384
- # @return [Array<Parser::AST::Node>]
385
- def string_nodes
386
- @string_nodes ||= string_nodes_in(@node)
387
- end
388
-
389
- # @return [Array<Range>]
390
- def comment_ranges
391
- @comment_ranges ||= @comments.values.map(&:range)
392
- end
393
-
394
- # Get an array of foldable comment block ranges. Blocks are excluded if
395
- # they are less than 3 lines long.
396
- #
397
- # @return [Array<Range>]
398
- def foldable_comment_block_ranges
399
- return [] unless synchronized?
400
- result = []
401
- grouped = []
402
- comments.keys.each do |l|
403
- if grouped.empty? || l == grouped.last + 1
404
- grouped.push l
405
- else
406
- result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
407
- grouped = [l]
408
- end
409
- end
410
- result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
411
- result
412
- end
413
-
414
- # @param n [Parser::AST::Node]
415
- # @return [Array<Parser::AST::Node>]
416
- def string_nodes_in n
417
- result = []
418
- if Parser.is_ast_node?(n)
419
- if n.type == :str || n.type == :dstr || n.type == :STR || n.type == :DSTR
420
- result.push n
421
- else
422
- n.children.each{ |c| result.concat string_nodes_in(c) }
423
- end
424
- end
425
- result
426
- end
427
-
428
- # @param node [Parser::AST::Node]
429
- # @param position [Position]
430
- # @param stack [Array<Parser::AST::Node>]
431
- # @return [void]
432
- def inner_tree_at node, position, stack
433
- return if node.nil?
434
- here = Range.from_node(node)
435
- if here.contain?(position) || colonized(here, position, node)
436
- stack.unshift node
437
- node.children.each do |c|
438
- next unless Parser.is_ast_node?(c)
439
- next if !Parser.rubyvm? && c.loc.expression.nil?
440
- inner_tree_at(c, position, stack)
441
- end
442
- end
443
- end
444
-
445
- def colonized range, position, node
446
- node.type == :COLON2 &&
447
- range.ending.line == position.line &&
448
- range.ending.character == position.character - 2 &&
449
- code[Position.to_offset(code, Position.new(position.line, position.character - 2)), 2] == '::'
450
- end
451
-
452
- protected
453
-
454
- # @return [String]
455
- attr_writer :filename
456
-
457
- # @return [Integer]
458
- attr_writer :version
459
-
460
- # @param val [String]
461
- # @return [String]
462
- def code=(val)
463
- @code_lines= nil
464
- @code = val
465
- end
466
-
467
- # @return [Parser::AST::Node]
468
- attr_writer :node
469
-
470
- # @return [Array<Range>]
471
- attr_writer :error_ranges
472
-
473
- # @return [String]
474
- attr_accessor :repaired
475
-
476
- # @return [Boolean]
477
- attr_writer :parsed
478
-
479
- # @return [Array<Parser::Source::Comment>]
480
- attr_writer :comments
481
-
482
- # @return [Boolean]
483
- attr_writer :synchronized
484
-
485
- # @return [Source::Updater]
486
- attr_accessor :last_updater
487
-
488
- private
489
-
490
- # @return [Array<String>]
491
- def code_lines
492
- @code_lines ||= code.lines
493
- end
494
-
495
- class << self
496
- # @param filename [String]
497
- # @return [Solargraph::Source]
498
- def load filename
499
- file = File.open(filename)
500
- code = file.read
501
- file.close
502
- Source.load_string(code, filename)
503
- end
504
-
505
- # @param code [String]
506
- # @param filename [String]
507
- # @param version [Integer]
508
- # @return [Solargraph::Source]
509
- def load_string code, filename = nil, version = 0
510
- Source.new code, filename, version
511
- end
512
-
513
- # @param comments [String]
514
- # @return [YARD::DocstringParser]
515
- def parse_docstring comments
516
- # HACK: Pass a dummy code object to the parser for plugins that
517
- # expect it not to be nil
518
- YARD::Docstring.parser.parse(comments, YARD::CodeObjects::Base.new(:root, 'stub'))
519
- end
520
- end
521
- end
522
- end
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 :Updater, 'solargraph/source/updater'
10
+ autoload :Change, 'solargraph/source/change'
11
+ autoload :Mapper, 'solargraph/source/mapper'
12
+ autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
13
+ autoload :Cursor, 'solargraph/source/cursor'
14
+ autoload :Chain, 'solargraph/source/chain'
15
+ autoload :SourceChainer, 'solargraph/source/source_chainer'
16
+
17
+ include EncodingFixes
18
+
19
+ # @return [String]
20
+ attr_reader :filename
21
+
22
+ # @return [String]
23
+ attr_reader :code
24
+
25
+ # @return [Parser::AST::Node]
26
+ attr_reader :node
27
+
28
+ # @return [Hash{Integer => Array<String>}]
29
+ attr_reader :comments
30
+
31
+ # @todo Deprecate?
32
+ # @return [Integer]
33
+ attr_reader :version
34
+
35
+ # @param code [String]
36
+ # @param filename [String]
37
+ # @param version [Integer]
38
+ def initialize code, filename = nil, version = 0
39
+ @code = normalize(code)
40
+ @repaired = code
41
+ @filename = filename
42
+ @version = version
43
+ @domains = []
44
+ begin
45
+ @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
46
+ @parsed = true
47
+ rescue Parser::SyntaxError, EncodingError => e
48
+ puts "[#{e.class}] #{e.message}"
49
+ puts e.backtrace
50
+ @node = nil
51
+ @comments = {}
52
+ @parsed = false
53
+ ensure
54
+ @code.freeze
55
+ end
56
+ end
57
+
58
+ # @param range [Solargraph::Range]
59
+ # @return [String]
60
+ def at range
61
+ from_to range.start.line, range.start.character, range.ending.line, range.ending.character
62
+ end
63
+
64
+ # @param l1 [Integer]
65
+ # @param c1 [Integer]
66
+ # @param l2 [Integer]
67
+ # @param c2 [Integer]
68
+ # @return [String]
69
+ def from_to l1, c1, l2, c2
70
+ b = Solargraph::Position.line_char_to_offset(@code, l1, c1)
71
+ e = Solargraph::Position.line_char_to_offset(@code, l2, c2)
72
+ @code[b..e-1]
73
+ end
74
+
75
+ # Get the nearest node that contains the specified index.
76
+ #
77
+ # @param line [Integer]
78
+ # @param column [Integer]
79
+ # @return [AST::Node]
80
+ def node_at(line, column)
81
+ tree_at(line, column).first
82
+ end
83
+
84
+ # Get an array of nodes containing the specified index, starting with the
85
+ # nearest node and ending with the root.
86
+ #
87
+ # @param line [Integer]
88
+ # @param column [Integer]
89
+ # @return [Array<AST::Node>]
90
+ def tree_at(line, column)
91
+ # offset = Position.line_char_to_offset(@code, line, column)
92
+ position = Position.new(line, column)
93
+ stack = []
94
+ inner_tree_at @node, position, stack
95
+ stack
96
+ end
97
+
98
+ # Start synchronizing the source. This method updates the code without
99
+ # parsing a new AST. The resulting Source object will be marked not
100
+ # synchronized (#synchronized? == false).
101
+ #
102
+ # @param updater [Source::Updater]
103
+ # @return [Source]
104
+ def start_synchronize updater
105
+ raise 'Invalid synchronization' unless updater.filename == filename
106
+ real_code = updater.write(@code)
107
+ src = Source.allocate
108
+ src.filename = filename
109
+ src.code = real_code
110
+ src.version = updater.version
111
+ src.parsed = parsed?
112
+ src.repaired = updater.repair(@repaired)
113
+ src.synchronized = false
114
+ src.node = @node
115
+ src.comments = @comments
116
+ src.error_ranges = error_ranges
117
+ src.last_updater = updater
118
+ return src.finish_synchronize unless real_code.lines.length == @code.lines.length
119
+ src
120
+ end
121
+
122
+ # Finish synchronizing a source that was updated via #start_synchronize.
123
+ # This method returns self if the source is already synchronized. Otherwise
124
+ # it parses the AST and returns a new synchronized Source.
125
+ #
126
+ # @return [Source]
127
+ def finish_synchronize
128
+ return self if synchronized?
129
+ synced = Source.new(@code, filename)
130
+ if synced.parsed?
131
+ synced.version = version
132
+ return synced
133
+ end
134
+ synced = Source.new(@repaired, filename)
135
+ synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
136
+ synced.code = @code
137
+ synced.synchronized = true
138
+ synced.version = version
139
+ synced
140
+ end
141
+
142
+ # Synchronize the Source with an update. This method applies changes to the
143
+ # code, parses the new code's AST, and returns the resulting Source object.
144
+ #
145
+ # @param updater [Source::Updater]
146
+ # @return [Source]
147
+ def synchronize updater
148
+ raise 'Invalid synchronization' unless updater.filename == filename
149
+ real_code = updater.write(@code)
150
+ if real_code == @code
151
+ @version = updater.version
152
+ return self
153
+ end
154
+ synced = Source.new(real_code, filename)
155
+ if synced.parsed?
156
+ synced.version = updater.version
157
+ return synced
158
+ end
159
+ incr_code = updater.repair(@repaired)
160
+ synced = Source.new(incr_code, filename)
161
+ synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
162
+ synced.code = real_code
163
+ synced.version = updater.version
164
+ synced
165
+ end
166
+
167
+ # @param position [Position, Array(Integer, Integer)]
168
+ # @return [Source::Cursor]
169
+ def cursor_at position
170
+ Cursor.new(self, position)
171
+ end
172
+
173
+ # @return [Boolean]
174
+ def parsed?
175
+ @parsed
176
+ end
177
+
178
+ def repaired?
179
+ @is_repaired ||= (@code != @repaired)
180
+ end
181
+
182
+ # @param position [Position]
183
+ # @return [Boolean]
184
+ def string_at? position
185
+ if Parser.rubyvm?
186
+ string_ranges.each do |range|
187
+ if synchronized?
188
+ return true if range.include?(position) || range.ending == position
189
+ else
190
+ return true if last_updater && last_updater.changes.one? && range.contain?(last_updater.changes.first.range.start)
191
+ end
192
+ end
193
+ false
194
+ else
195
+ return false if Position.to_offset(code, position) >= code.length
196
+ string_nodes.each do |node|
197
+ range = Range.from_node(node)
198
+ next if range.ending.line < position.line
199
+ break if range.ending.line > position.line
200
+ return true if node.type == :str && range.include?(position) && range.start != position
201
+ return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
202
+ if node.type == :dstr
203
+ inner = node_at(position.line, position.column)
204
+ next if inner.nil?
205
+ inner_range = Range.from_node(inner)
206
+ next unless range.include?(inner_range.ending)
207
+ return true if inner.type == :str
208
+ inner_code = at(Solargraph::Range.new(inner_range.start, position))
209
+ return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
210
+ (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
211
+ end
212
+ break if range.ending.line > position.line
213
+ end
214
+ false
215
+ end
216
+ end
217
+
218
+ def string_ranges
219
+ @string_ranges ||= Parser.string_ranges(node)
220
+ end
221
+
222
+ # @param position [Position]
223
+ # @return [Boolean]
224
+ def comment_at? position
225
+ comment_ranges.each do |range|
226
+ return true if range.include?(position) ||
227
+ (range.ending.line == position.line && range.ending.column < position.column)
228
+ break if range.ending.line > position.line
229
+ end
230
+ false
231
+ end
232
+
233
+ # @param name [String]
234
+ # @return [Array<Location>]
235
+ def references name
236
+ Parser.references self, name
237
+ end
238
+
239
+ # @return [Array<Range>]
240
+ def error_ranges
241
+ @error_ranges ||= []
242
+ end
243
+
244
+ # @param node [Parser::AST::Node]
245
+ # @return [String]
246
+ def code_for(node)
247
+ rng = Range.from_node(node)
248
+ b = Position.line_char_to_offset(@code, rng.start.line, rng.start.column)
249
+ e = Position.line_char_to_offset(@code, rng.ending.line, rng.ending.column)
250
+ frag = code[b..e-1].to_s
251
+ frag.strip.gsub(/,$/, '')
252
+ end
253
+
254
+ # @param node [Parser::AST::Node]
255
+ # @return [String]
256
+ def comments_for node
257
+ rng = Range.from_node(node)
258
+ stringified_comments[rng.start.line] ||= begin
259
+ buff = associated_comments[rng.start.line]
260
+ buff ? stringify_comment_array(buff) : nil
261
+ end
262
+ end
263
+
264
+ # A location representing the file in its entirety.
265
+ #
266
+ # @return [Location]
267
+ def location
268
+ st = Position.new(0, 0)
269
+ en = Position.from_offset(code, code.length)
270
+ range = Range.new(st, en)
271
+ Location.new(filename, range)
272
+ end
273
+
274
+ FOLDING_NODE_TYPES = if Parser.rubyvm?
275
+ %i[
276
+ CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
277
+ ].freeze
278
+ else
279
+ %i[
280
+ class sclass module def defs if str dstr array while unless kwbegin hash block
281
+ ].freeze
282
+ end
283
+
284
+ # Get an array of ranges that can be folded, e.g., the range of a class
285
+ # definition or an if condition.
286
+ #
287
+ # See FOLDING_NODE_TYPES for the list of node types that can be folded.
288
+ #
289
+ # @return [Array<Range>]
290
+ def folding_ranges
291
+ @folding_ranges ||= begin
292
+ result = []
293
+ inner_folding_ranges node, result
294
+ result.concat foldable_comment_block_ranges
295
+ result
296
+ end
297
+ end
298
+
299
+ def synchronized?
300
+ @synchronized = true if @synchronized.nil?
301
+ @synchronized
302
+ end
303
+
304
+ # Get a hash of comments grouped by the line numbers of the associated code.
305
+ #
306
+ # @return [Hash{Integer => Array<Parser::Source::Comment>}]
307
+ def associated_comments
308
+ @associated_comments ||= begin
309
+ result = {}
310
+ buffer = String.new('')
311
+ last = nil
312
+ @comments.each_pair do |num, snip|
313
+ if !last || num == last + 1
314
+ buffer.concat "#{snip.text}\n"
315
+ else
316
+ result[first_not_empty_from(last + 1)] = buffer.clone
317
+ buffer.replace "#{snip.text}\n"
318
+ end
319
+ last = num
320
+ end
321
+ result[first_not_empty_from(last + 1)] = buffer unless buffer.empty? || last.nil?
322
+ result
323
+ end
324
+ end
325
+
326
+ private
327
+
328
+ def first_not_empty_from line
329
+ cursor = line
330
+ cursor += 1 while cursor < code_lines.length && code_lines[cursor].strip.empty?
331
+ cursor = line if cursor > code_lines.length - 1
332
+ cursor
333
+ end
334
+
335
+ # @param top [Parser::AST::Node]
336
+ # @param result [Array<Range>]
337
+ # @param parent [Symbol]
338
+ # @return [void]
339
+ def inner_folding_ranges top, result = [], parent = nil
340
+ return unless Parser.is_ast_node?(top)
341
+ if FOLDING_NODE_TYPES.include?(top.type)
342
+ # @todo Smelly exception for hash's first-level array in RubyVM
343
+ unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
344
+ range = Range.from_node(top)
345
+ if result.empty? || range.start.line > result.last.start.line
346
+ result.push range unless range.ending.line - range.start.line < 2
347
+ end
348
+ end
349
+ end
350
+ top.children.each do |child|
351
+ inner_folding_ranges(child, result, top.type)
352
+ end
353
+ end
354
+
355
+ # Get a string representation of an array of comments.
356
+ #
357
+ # @param comments [String]
358
+ # @return [String]
359
+ def stringify_comment_array comments
360
+ ctxt = String.new('')
361
+ started = false
362
+ skip = nil
363
+ comments.lines.each { |l|
364
+ # Trim the comment and minimum leading whitespace
365
+ p = l.force_encoding('UTF-8').encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
366
+ if p.strip.empty?
367
+ next unless started
368
+ ctxt.concat p
369
+ else
370
+ here = p.index(/[^ \t]/)
371
+ skip = here if skip.nil? || here < skip
372
+ ctxt.concat p[skip..-1]
373
+ end
374
+ started = true
375
+ }
376
+ ctxt
377
+ end
378
+
379
+ # A hash of line numbers and their associated comments.
380
+ #
381
+ # @return [Hash{Integer => Array<String>}]
382
+ def stringified_comments
383
+ @stringified_comments ||= {}
384
+ end
385
+
386
+ # @return [Array<Parser::AST::Node>]
387
+ def string_nodes
388
+ @string_nodes ||= string_nodes_in(@node)
389
+ end
390
+
391
+ # @return [Array<Range>]
392
+ def comment_ranges
393
+ @comment_ranges ||= @comments.values.map(&:range)
394
+ end
395
+
396
+ # Get an array of foldable comment block ranges. Blocks are excluded if
397
+ # they are less than 3 lines long.
398
+ #
399
+ # @return [Array<Range>]
400
+ def foldable_comment_block_ranges
401
+ return [] unless synchronized?
402
+ result = []
403
+ grouped = []
404
+ comments.keys.each do |l|
405
+ if grouped.empty? || l == grouped.last + 1
406
+ grouped.push l
407
+ else
408
+ result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
409
+ grouped = [l]
410
+ end
411
+ end
412
+ result.push Range.from_to(grouped.first, 0, grouped.last, 0) unless grouped.length < 3
413
+ result
414
+ end
415
+
416
+ # @param n [Parser::AST::Node]
417
+ # @return [Array<Parser::AST::Node>]
418
+ def string_nodes_in n
419
+ result = []
420
+ if Parser.is_ast_node?(n)
421
+ if n.type == :str || n.type == :dstr || n.type == :STR || n.type == :DSTR
422
+ result.push n
423
+ else
424
+ n.children.each{ |c| result.concat string_nodes_in(c) }
425
+ end
426
+ end
427
+ result
428
+ end
429
+
430
+ # @param node [Parser::AST::Node]
431
+ # @param position [Position]
432
+ # @param stack [Array<Parser::AST::Node>]
433
+ # @return [void]
434
+ def inner_tree_at node, position, stack
435
+ return if node.nil?
436
+ here = Range.from_node(node)
437
+ if here.contain?(position) || colonized(here, position, node)
438
+ stack.unshift node
439
+ node.children.each do |c|
440
+ next unless Parser.is_ast_node?(c)
441
+ next if !Parser.rubyvm? && c.loc.expression.nil?
442
+ inner_tree_at(c, position, stack)
443
+ end
444
+ end
445
+ end
446
+
447
+ def colonized range, position, node
448
+ node.type == :COLON2 &&
449
+ range.ending.line == position.line &&
450
+ range.ending.character == position.character - 2 &&
451
+ code[Position.to_offset(code, Position.new(position.line, position.character - 2)), 2] == '::'
452
+ end
453
+
454
+ protected
455
+
456
+ # @return [String]
457
+ attr_writer :filename
458
+
459
+ # @return [Integer]
460
+ attr_writer :version
461
+
462
+ # @param val [String]
463
+ # @return [String]
464
+ def code=(val)
465
+ @code_lines= nil
466
+ @code = val
467
+ end
468
+
469
+ # @return [Parser::AST::Node]
470
+ attr_writer :node
471
+
472
+ # @return [Array<Range>]
473
+ attr_writer :error_ranges
474
+
475
+ # @return [String]
476
+ attr_accessor :repaired
477
+
478
+ # @return [Boolean]
479
+ attr_writer :parsed
480
+
481
+ # @return [Array<Parser::Source::Comment>]
482
+ attr_writer :comments
483
+
484
+ # @return [Boolean]
485
+ attr_writer :synchronized
486
+
487
+ # @return [Source::Updater]
488
+ attr_accessor :last_updater
489
+
490
+ private
491
+
492
+ # @return [Array<String>]
493
+ def code_lines
494
+ @code_lines ||= code.lines
495
+ end
496
+
497
+ class << self
498
+ # @param filename [String]
499
+ # @return [Solargraph::Source]
500
+ def load filename
501
+ file = File.open(filename)
502
+ code = file.read
503
+ file.close
504
+ Source.load_string(code, filename)
505
+ end
506
+
507
+ # @param code [String]
508
+ # @param filename [String]
509
+ # @param version [Integer]
510
+ # @return [Solargraph::Source]
511
+ def load_string code, filename = nil, version = 0
512
+ Source.new code, filename, version
513
+ end
514
+
515
+ # @param comments [String]
516
+ # @return [YARD::DocstringParser]
517
+ def parse_docstring comments
518
+ # HACK: Pass a dummy code object to the parser for plugins that
519
+ # expect it not to be nil
520
+ YARD::Docstring.parser.parse(comments, YARD::CodeObjects::Base.new(:root, 'stub'))
521
+ end
522
+ end
523
+ end
524
+ end