solargraph 0.58.0 → 0.59.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.gitattributes +2 -0
  4. data/.github/workflows/linting.yml +6 -5
  5. data/.github/workflows/plugins.yml +48 -35
  6. data/.github/workflows/rspec.yml +15 -28
  7. data/.github/workflows/typecheck.yml +3 -2
  8. data/.gitignore +1 -0
  9. data/.rubocop.yml +38 -6
  10. data/.rubocop_todo.yml +53 -966
  11. data/CHANGELOG.md +24 -0
  12. data/Gemfile +3 -1
  13. data/README.md +3 -3
  14. data/Rakefile +26 -23
  15. data/bin/solargraph +9 -8
  16. data/lib/solargraph/api_map/cache.rb +110 -110
  17. data/lib/solargraph/api_map/constants.rb +288 -279
  18. data/lib/solargraph/api_map/index.rb +204 -193
  19. data/lib/solargraph/api_map/source_to_yard.rb +110 -97
  20. data/lib/solargraph/api_map/store.rb +395 -384
  21. data/lib/solargraph/api_map.rb +1029 -945
  22. data/lib/solargraph/bench.rb +44 -45
  23. data/lib/solargraph/complex_type/conformance.rb +176 -0
  24. data/lib/solargraph/complex_type/type_methods.rb +240 -228
  25. data/lib/solargraph/complex_type/unique_type.rb +641 -482
  26. data/lib/solargraph/complex_type.rb +557 -444
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  29. data/lib/solargraph/convention/data_definition.rb +108 -105
  30. data/lib/solargraph/convention/gemfile.rb +15 -15
  31. data/lib/solargraph/convention/gemspec.rb +23 -23
  32. data/lib/solargraph/convention/rakefile.rb +17 -17
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  35. data/lib/solargraph/convention/struct_definition.rb +168 -164
  36. data/lib/solargraph/convention.rb +78 -78
  37. data/lib/solargraph/converters/dd.rb +19 -17
  38. data/lib/solargraph/converters/dl.rb +17 -15
  39. data/lib/solargraph/converters/dt.rb +17 -15
  40. data/lib/solargraph/converters/misc.rb +3 -1
  41. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  42. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  44. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  45. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  46. data/lib/solargraph/doc_map.rb +438 -439
  47. data/lib/solargraph/equality.rb +34 -34
  48. data/lib/solargraph/gem_pins.rb +98 -98
  49. data/lib/solargraph/language_server/error_codes.rb +20 -20
  50. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  51. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  52. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  53. data/lib/solargraph/language_server/host/sources.rb +100 -99
  54. data/lib/solargraph/language_server/host.rb +886 -878
  55. data/lib/solargraph/language_server/message/base.rb +97 -97
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  57. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  58. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +108 -114
  59. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  60. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  61. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  64. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  65. data/lib/solargraph/language_server/message/text_document/definition.rb +49 -40
  66. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  67. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +36 -26
  68. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  69. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  70. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  71. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  72. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  73. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  74. data/lib/solargraph/language_server/message/text_document/type_definition.rb +33 -25
  75. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  76. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  77. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  78. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +33 -23
  79. data/lib/solargraph/language_server/message.rb +94 -94
  80. data/lib/solargraph/language_server/request.rb +29 -27
  81. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  82. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  83. data/lib/solargraph/library.rb +702 -683
  84. data/lib/solargraph/location.rb +85 -82
  85. data/lib/solargraph/logging.rb +55 -37
  86. data/lib/solargraph/page.rb +92 -92
  87. data/lib/solargraph/parser/comment_ripper.rb +84 -69
  88. data/lib/solargraph/parser/flow_sensitive_typing.rb +471 -255
  89. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  90. data/lib/solargraph/parser/node_processor.rb +63 -62
  91. data/lib/solargraph/parser/parser_gem/class_methods.rb +165 -149
  92. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  93. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  94. data/lib/solargraph/parser/parser_gem/node_methods.rb +507 -486
  95. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  96. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  97. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  98. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +45 -46
  99. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  100. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  101. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  102. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +42 -40
  103. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  104. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  105. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  106. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  108. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  109. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +51 -52
  110. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +302 -291
  111. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  112. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  113. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  114. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  115. data/lib/solargraph/parser/parser_gem.rb +14 -12
  116. data/lib/solargraph/parser/region.rb +75 -69
  117. data/lib/solargraph/parser/snippet.rb +19 -17
  118. data/lib/solargraph/parser.rb +25 -23
  119. data/lib/solargraph/pin/base.rb +773 -729
  120. data/lib/solargraph/pin/base_variable.rb +375 -126
  121. data/lib/solargraph/pin/block.rb +127 -104
  122. data/lib/solargraph/pin/breakable.rb +15 -9
  123. data/lib/solargraph/pin/callable.rb +275 -231
  124. data/lib/solargraph/pin/closure.rb +69 -72
  125. data/lib/solargraph/pin/common.rb +97 -79
  126. data/lib/solargraph/pin/compound_statement.rb +55 -0
  127. data/lib/solargraph/pin/constant.rb +43 -45
  128. data/lib/solargraph/pin/conversions.rb +129 -123
  129. data/lib/solargraph/pin/delegated_method.rb +131 -120
  130. data/lib/solargraph/pin/documenting.rb +116 -114
  131. data/lib/solargraph/pin/instance_variable.rb +38 -34
  132. data/lib/solargraph/pin/keyword.rb +16 -20
  133. data/lib/solargraph/pin/local_variable.rb +31 -75
  134. data/lib/solargraph/pin/method.rb +724 -672
  135. data/lib/solargraph/pin/method_alias.rb +42 -34
  136. data/lib/solargraph/pin/namespace.rb +122 -115
  137. data/lib/solargraph/pin/parameter.rb +339 -275
  138. data/lib/solargraph/pin/proxy_type.rb +42 -39
  139. data/lib/solargraph/pin/reference/override.rb +47 -47
  140. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  141. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  142. data/lib/solargraph/pin/reference.rb +59 -39
  143. data/lib/solargraph/pin/search.rb +62 -61
  144. data/lib/solargraph/pin/signature.rb +64 -61
  145. data/lib/solargraph/pin/symbol.rb +54 -53
  146. data/lib/solargraph/pin/until.rb +16 -18
  147. data/lib/solargraph/pin/while.rb +16 -18
  148. data/lib/solargraph/pin.rb +46 -44
  149. data/lib/solargraph/pin_cache.rb +248 -245
  150. data/lib/solargraph/position.rb +139 -119
  151. data/lib/solargraph/range.rb +113 -112
  152. data/lib/solargraph/rbs_map/conversions.rb +952 -823
  153. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  154. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  155. data/lib/solargraph/rbs_map/stdlib_map.rb +71 -43
  156. data/lib/solargraph/rbs_map.rb +207 -163
  157. data/lib/solargraph/server_methods.rb +16 -16
  158. data/lib/solargraph/shell.rb +555 -352
  159. data/lib/solargraph/source/chain/array.rb +39 -37
  160. data/lib/solargraph/source/chain/call.rb +377 -337
  161. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/constant.rb +30 -26
  163. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  164. data/lib/solargraph/source/chain/hash.rb +37 -34
  165. data/lib/solargraph/source/chain/if.rb +30 -28
  166. data/lib/solargraph/source/chain/instance_variable.rb +36 -13
  167. data/lib/solargraph/source/chain/link.rb +99 -109
  168. data/lib/solargraph/source/chain/literal.rb +51 -48
  169. data/lib/solargraph/source/chain/or.rb +29 -23
  170. data/lib/solargraph/source/chain/q_call.rb +13 -11
  171. data/lib/solargraph/source/chain/variable.rb +15 -13
  172. data/lib/solargraph/source/chain/z_super.rb +28 -30
  173. data/lib/solargraph/source/chain.rb +304 -291
  174. data/lib/solargraph/source/change.rb +89 -82
  175. data/lib/solargraph/source/cursor.rb +172 -166
  176. data/lib/solargraph/source/encoding_fixes.rb +22 -23
  177. data/lib/solargraph/source/source_chainer.rb +218 -194
  178. data/lib/solargraph/source/updater.rb +59 -55
  179. data/lib/solargraph/source.rb +522 -498
  180. data/lib/solargraph/source_map/clip.rb +245 -226
  181. data/lib/solargraph/source_map/data.rb +37 -34
  182. data/lib/solargraph/source_map/mapper.rb +286 -259
  183. data/lib/solargraph/source_map.rb +224 -212
  184. data/lib/solargraph/type_checker/problem.rb +34 -32
  185. data/lib/solargraph/type_checker/rules.rb +157 -84
  186. data/lib/solargraph/type_checker.rb +888 -814
  187. data/lib/solargraph/version.rb +5 -5
  188. data/lib/solargraph/workspace/config.rb +258 -255
  189. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  190. data/lib/solargraph/workspace/require_paths.rb +98 -97
  191. data/lib/solargraph/workspace.rb +242 -220
  192. data/lib/solargraph/yard_map/helpers.rb +49 -44
  193. data/lib/solargraph/yard_map/mapper/to_method.rb +136 -130
  194. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  195. data/lib/solargraph/yard_map/mapper.rb +84 -79
  196. data/lib/solargraph/yard_map.rb +17 -18
  197. data/lib/solargraph/yard_tags.rb +20 -20
  198. data/lib/solargraph/yardoc.rb +90 -87
  199. data/lib/solargraph.rb +128 -105
  200. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  201. data/rbs/fills/tuple/tuple.rbs +28 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  204. data/rbs_collection.yaml +1 -1
  205. data/solargraph.gemspec +37 -35
  206. metadata +52 -51
  207. data/lib/solargraph/type_checker/checks.rb +0 -124
  208. data/lib/solargraph/type_checker/param_def.rb +0 -37
  209. data/lib/solargraph/yard_map/to_method.rb +0 -89
  210. data/sig/shims/ast/0/node.rbs +0 -5
  211. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  212. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  215. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  216. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  217. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  218. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  219. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,259 +1,286 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class SourceMap
5
- # The Mapper generates pins and other data for SourceMaps.
6
- #
7
- # This class is used internally by the SourceMap class. Users should not
8
- # normally need to call it directly.
9
- #
10
- class Mapper
11
- # include Source::NodeMethods
12
-
13
- private_class_method :new
14
-
15
- DIRECTIVE_REGEXP = /(@\!method|@\!attribute|@\!visibility|@\!domain|@\!macro|@\!parse|@\!override)/.freeze
16
-
17
- # Generate the data.
18
- #
19
- # @param source [Source]
20
- # @return [Array]
21
- def map source
22
- @source = source
23
- @filename = source.filename
24
- @code = source.code
25
- @comments = source.comments
26
- @pins, @locals = Parser.map(source)
27
- @pins.each { |p| p.source = :code }
28
- @locals.each { |l| l.source = :code }
29
- process_comment_directives
30
- [@pins, @locals]
31
- # rescue Exception => e
32
- # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}"
33
- # Solargraph.logger.warn e.backtrace.join("\n")
34
- # [[], []]
35
- end
36
-
37
- # @param filename [String]
38
- # @param code [String]
39
- # @return [Array]
40
- def unmap filename, code
41
- s = Position.new(0, 0)
42
- e = Position.from_offset(code, code.length)
43
- location = Location.new(filename, Range.new(s, e))
44
- [[Pin::Namespace.new(location: location, name: '', source: :source_map)], []]
45
- end
46
-
47
- class << self
48
- # @param source [Source]
49
- # @return [Array]
50
- def map source
51
- return new.unmap(source.filename, source.code) unless source.parsed?
52
- new.map source
53
- end
54
- end
55
-
56
- # @return [Array<Solargraph::Pin::Base>]
57
- def pins
58
- # @type [Array<Solargraph::Pin::Base>]
59
- @pins ||= []
60
- end
61
-
62
- # @param position [Solargraph::Position]
63
- # @return [Solargraph::Pin::Closure]
64
- def closure_at(position)
65
- pins.select{|pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position)}.last
66
- end
67
-
68
- # @param source_position [Position]
69
- # @param comment_position [Position]
70
- # @param comment [String]
71
- # @return [void]
72
- def process_comment source_position, comment_position, comment
73
- return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
74
- cmnt = remove_inline_comment_hashes(comment)
75
- parse = Solargraph::Source.parse_docstring(cmnt)
76
- last_line = 0
77
- # @param d [YARD::Tags::Directive]
78
- parse.directives.each do |d|
79
- line_num = find_directive_line_number(cmnt, d.tag.tag_name, last_line)
80
- pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column)
81
- process_directive(source_position, pos, d)
82
- last_line = line_num + 1
83
- end
84
- end
85
-
86
- # @param comment [String]
87
- # @param tag [String]
88
- # @param start [Integer]
89
- # @return [Integer]
90
- def find_directive_line_number comment, tag, start
91
- # Avoid overruning the index
92
- return start unless start < comment.lines.length
93
- num = comment.lines[start..-1].find_index do |line|
94
- # Legacy method directives might be `@method` instead of `@!method`
95
- # @todo Legacy syntax should probably emit a warning
96
- line.include?("@!#{tag}") || (tag == 'method' && line.include?("@#{tag}"))
97
- end
98
- num.to_i + start
99
- end
100
-
101
- # @param source_position [Position]
102
- # @param comment_position [Position]
103
- # @param directive [YARD::Tags::Directive]
104
- # @return [void]
105
- def process_directive source_position, comment_position, directive
106
- docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring
107
- location = Location.new(@filename, Range.new(comment_position, comment_position))
108
- case directive.tag.tag_name
109
- when 'method'
110
- namespace = closure_at(source_position) || @pins.first
111
- if namespace.location.range.start.line < comment_position.line
112
- namespace = closure_at(comment_position)
113
- end
114
- begin
115
- src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
116
- region = Parser::Region.new(source: src, closure: namespace)
117
- method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
118
- gen_pin = method_gen_pins.last
119
- return if gen_pin.nil?
120
- # Move the location to the end of the line so it gets recognized
121
- # as originating from a comment
122
- shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].to_s.chomp.length)
123
- # @todo: Smelly instance variable access
124
- gen_pin.instance_variable_set(:@comments, docstring.all.to_s)
125
- gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted)))
126
- gen_pin.instance_variable_set(:@explicit, false)
127
- @pins.push gen_pin
128
- rescue Parser::SyntaxError => e
129
- # @todo Handle error in directive
130
- end
131
- when 'attribute'
132
- return if directive.tag.name.nil?
133
- namespace = closure_at(source_position)
134
- t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
135
- if t.nil? || t.include?('r')
136
- pins.push Solargraph::Pin::Method.new(
137
- location: location,
138
- closure: namespace,
139
- name: directive.tag.name,
140
- comments: docstring.all.to_s,
141
- scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
142
- visibility: :public,
143
- explicit: false,
144
- attribute: true,
145
- source: :source_map
146
- )
147
- end
148
- if t.nil? || t.include?('w')
149
- method_pin = Solargraph::Pin::Method.new(
150
- location: location,
151
- closure: namespace,
152
- name: "#{directive.tag.name}=",
153
- comments: docstring.all.to_s,
154
- scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
155
- visibility: :public,
156
- attribute: true,
157
- source: :source_map
158
- )
159
- pins.push method_pin
160
- method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map)
161
- if pins.last.return_type.defined?
162
- pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
163
- end
164
- end
165
- when 'visibility'
166
-
167
- kind = directive.tag.text&.to_sym
168
- return unless [:private, :protected, :public].include?(kind)
169
-
170
- name = directive.tag.name
171
- closure = closure_at(source_position) || @pins.first
172
- if closure.location.range.start.line < comment_position.line
173
- closure = closure_at(comment_position)
174
- end
175
- if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line)
176
- # @todo Smelly instance variable access
177
- closure.instance_variable_set(:@visibility, kind)
178
- else
179
- matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance }
180
- matches.each do |pin|
181
- # @todo Smelly instance variable access
182
- pin.instance_variable_set(:@visibility, kind)
183
- end
184
- end
185
-
186
- when 'parse'
187
- begin
188
- ns = closure_at(source_position)
189
- src = Solargraph::Source.load_string(directive.tag.text, @source.filename)
190
- region = Parser::Region.new(source: src, closure: ns)
191
- # @todo These pins may need to be marked not explicit
192
- index = @pins.length
193
- loff = if @code.lines[comment_position.line].strip.end_with?('@!parse')
194
- comment_position.line + 1
195
- else
196
- comment_position.line
197
- end
198
- Parser.process_node(src.node, region, @pins)
199
- @pins[index..-1].each do |p|
200
- # @todo Smelly instance variable access
201
- p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
202
- p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
203
- end
204
- rescue Parser::SyntaxError => e
205
- # @todo Handle parser errors in !parse directives
206
- end
207
- when 'domain'
208
- namespace = closure_at(source_position) || Pin::ROOT_PIN
209
- namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
210
- when 'override'
211
- pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
212
- source: :source_map)
213
- when 'macro'
214
- # @todo Handle macros
215
- end
216
- end
217
-
218
- # @param line1 [Integer]
219
- # @param line2 [Integer]
220
- def no_empty_lines?(line1, line2)
221
- @code.lines[line1..line2].none? { |line| line.strip.empty? }
222
- end
223
-
224
- # @param comment [String]
225
- # @return [String]
226
- def remove_inline_comment_hashes comment
227
- ctxt = ''
228
- num = nil
229
- started = false
230
- comment.lines.each { |l|
231
- # Trim the comment and minimum leading whitespace
232
- p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
233
- if num.nil? && !p.strip.empty?
234
- num = p.index(/[^ ]/)
235
- started = true
236
- elsif started && !p.strip.empty?
237
- cur = p.index(/[^ ]/)
238
- num = cur if cur < num
239
- end
240
- ctxt += "#{p[num..-1]}" if started
241
- }
242
- ctxt
243
- end
244
-
245
- # @return [void]
246
- def process_comment_directives
247
- return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
248
- code_lines = @code.lines
249
- @source.associated_comments.each do |line, comments|
250
- src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0)
251
- com_pos = Position.new(line + 1 - comments.lines.length, 0)
252
- process_comment(src_pos, com_pos, comments)
253
- end
254
- rescue StandardError => e
255
- raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
256
- end
257
- end
258
- end
259
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class SourceMap
5
+ # The Mapper generates pins and other data for SourceMaps.
6
+ #
7
+ # This class is used internally by the SourceMap class. Users should not
8
+ # normally need to call it directly.
9
+ #
10
+ class Mapper
11
+ # include Source::NodeMethods
12
+
13
+ private_class_method :new
14
+
15
+ DIRECTIVE_REGEXP = /(@!method|@!attribute|@!visibility|@!domain|@!macro|@!parse|@!override)/
16
+
17
+ # Generate the data.
18
+ #
19
+ # @param source [Source]
20
+ # @return [Array]
21
+ def map source
22
+ @source = source
23
+ @filename = source.filename
24
+ @code = source.code
25
+ @comments = source.comments
26
+ @pins, @locals = Parser.map(source)
27
+ # @param p [Solargraph::Pin::Base]
28
+ @pins.each { |p| p.source = :code }
29
+ @locals.each { |l| l.source = :code }
30
+ process_comment_directives
31
+ [@pins, @locals]
32
+ # rescue Exception => e
33
+ # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}"
34
+ # Solargraph.logger.warn e.backtrace.join("\n")
35
+ # [[], []]
36
+ end
37
+
38
+ # @param filename [String]
39
+ # @param code [String]
40
+ # @return [Array]
41
+ def unmap filename, code
42
+ s = Position.new(0, 0)
43
+ e = Position.from_offset(code, code.length)
44
+ location = Location.new(filename, Range.new(s, e))
45
+ [[Pin::Namespace.new(location: location, name: '', source: :source_map)], []]
46
+ end
47
+
48
+ class << self
49
+ # @param source [Source]
50
+ # @return [Array]
51
+ def map source
52
+ # @sg-ignore Need to add nil check here
53
+ return new.unmap(source.filename, source.code) unless source.parsed?
54
+ new.map source
55
+ end
56
+ end
57
+
58
+ # @return [Array<Solargraph::Pin::Base>]
59
+ def pins
60
+ # @type [Array<Solargraph::Pin::Base>]
61
+ @pins ||= []
62
+ end
63
+
64
+ # @param position [Solargraph::Position]
65
+ # @return [Solargraph::Pin::Closure]
66
+ def closure_at position
67
+ # @sg-ignore Need to add nil check here
68
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position) }.last
69
+ end
70
+
71
+ # @param source_position [Position]
72
+ # @param comment_position [Position]
73
+ # @param comment [String]
74
+ # @return [void]
75
+ def process_comment source_position, comment_position, comment
76
+ return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
77
+ cmnt = remove_inline_comment_hashes(comment)
78
+ parse = Solargraph::Source.parse_docstring(cmnt)
79
+ last_line = 0
80
+ # @param d [YARD::Tags::Directive]
81
+ parse.directives.each do |d|
82
+ line_num = find_directive_line_number(cmnt, d.tag.tag_name, last_line)
83
+ pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column)
84
+ process_directive(source_position, pos, d)
85
+ last_line = line_num + 1
86
+ end
87
+ end
88
+
89
+ # @param comment [String]
90
+ # @param tag [String]
91
+ # @param start [Integer]
92
+ # @return [Integer]
93
+ def find_directive_line_number comment, tag, start
94
+ # Avoid overruning the index
95
+ return start unless start < comment.lines.length
96
+ # @sg-ignore Need to add nil check here
97
+ num = comment.lines[start..].find_index do |line|
98
+ # Legacy method directives might be `@method` instead of `@!method`
99
+ # @todo Legacy syntax should probably emit a warning
100
+ line.include?("@!#{tag}") || (tag == 'method' && line.include?("@#{tag}"))
101
+ end
102
+ # @sg-ignore Need to add nil check here
103
+ num.to_i + start
104
+ end
105
+
106
+ # @param source_position [Position]
107
+ # @param comment_position [Position]
108
+ # @param directive [YARD::Tags::Directive]
109
+ # @return [void]
110
+ def process_directive source_position, comment_position, directive
111
+ # @sg-ignore Need to add nil check here
112
+ docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring
113
+ location = Location.new(@filename, Range.new(comment_position, comment_position))
114
+ case directive.tag.tag_name
115
+ when 'method'
116
+ namespace = closure_at(source_position) || @pins.first
117
+ # @sg-ignore Need to add nil check here
118
+ namespace = closure_at(comment_position) if namespace.location.range.start.line < comment_position.line
119
+ begin
120
+ src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
121
+ region = Parser::Region.new(source: src, closure: namespace)
122
+ # @type [Array<Pin::Method>]
123
+ method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
124
+ gen_pin = method_gen_pins.last
125
+ return if gen_pin.nil?
126
+ # Move the location to the end of the line so it gets recognized
127
+ # as originating from a comment
128
+ shifted = Solargraph::Position.new(comment_position.line,
129
+ @code.lines[comment_position.line].to_s.chomp.length)
130
+ # @todo: Smelly instance variable access
131
+ gen_pin.instance_variable_set(:@comments, docstring.all.to_s)
132
+ gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted)))
133
+ gen_pin.instance_variable_set(:@explicit, false)
134
+ @pins.push gen_pin
135
+ rescue Parser::SyntaxError
136
+ # @todo Handle error in directive
137
+ end
138
+ when 'attribute'
139
+ return if directive.tag.name.nil?
140
+ namespace = closure_at(source_position)
141
+ t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.join
142
+ if t.nil? || t.include?('r')
143
+ pins.push Solargraph::Pin::Method.new(
144
+ location: location,
145
+ closure: namespace,
146
+ name: directive.tag.name,
147
+ comments: docstring.all.to_s,
148
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
149
+ visibility: :public,
150
+ explicit: false,
151
+ attribute: true,
152
+ source: :source_map
153
+ )
154
+ end
155
+ if t.nil? || t.include?('w')
156
+ method_pin = Solargraph::Pin::Method.new(
157
+ location: location,
158
+ closure: namespace,
159
+ name: "#{directive.tag.name}=",
160
+ comments: docstring.all.to_s,
161
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
162
+ visibility: :public,
163
+ attribute: true,
164
+ source: :source_map
165
+ )
166
+ pins.push method_pin
167
+ method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last,
168
+ source: :source_map)
169
+ if pins.last.return_type.defined?
170
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '),
171
+ 'value')
172
+ end
173
+ end
174
+ when 'visibility'
175
+
176
+ kind = directive.tag.text&.to_sym
177
+ return unless %i[private protected public].include?(kind)
178
+
179
+ name = directive.tag.name
180
+ closure = closure_at(source_position) || @pins.first
181
+ # @sg-ignore Need to add nil check here
182
+ closure = closure_at(comment_position) if closure.location.range.start.line < comment_position.line
183
+ if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line)
184
+ # @todo Smelly instance variable access
185
+ closure.instance_variable_set(:@visibility, kind)
186
+ else
187
+ matches = pins.select do |pin|
188
+ pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance
189
+ end
190
+ matches.each do |pin|
191
+ # @todo Smelly instance variable access
192
+ pin.instance_variable_set(:@visibility, kind)
193
+ end
194
+ end
195
+
196
+ when 'parse'
197
+ begin
198
+ ns = closure_at(source_position)
199
+ # @sg-ignore Need to add nil check here
200
+ src = Solargraph::Source.load_string(directive.tag.text, @source.filename)
201
+ region = Parser::Region.new(source: src, closure: ns)
202
+ # @todo These pins may need to be marked not explicit
203
+ index = @pins.length
204
+ loff = if @code.lines[comment_position.line].strip.end_with?('@!parse')
205
+ comment_position.line + 1
206
+ else
207
+ comment_position.line
208
+ end
209
+ locals = []
210
+ ivars = []
211
+ Parser.process_node(src.node, region, @pins, locals, ivars)
212
+ @pins.concat ivars
213
+ # @sg-ignore Need to add nil check here
214
+ @pins[index..].each do |p|
215
+ # @todo Smelly instance variable access
216
+ p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
217
+ p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
218
+ end
219
+ rescue Parser::SyntaxError
220
+ # @todo Handle parser errors in !parse directives
221
+ end
222
+ when 'domain'
223
+ namespace = closure_at(source_position) || Pin::ROOT_PIN
224
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
225
+ namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
226
+ when 'override'
227
+ pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
228
+ source: :source_map)
229
+ when 'macro'
230
+ # @todo Handle macros
231
+ end
232
+ end
233
+
234
+ # @param line1 [Integer]
235
+ # @param line2 [Integer]
236
+ # @sg-ignore Need to add nil check here
237
+ def no_empty_lines? line1, line2
238
+ # @sg-ignore Need to add nil check here
239
+ @code.lines[line1..line2].none? { |line| line.strip.empty? }
240
+ end
241
+
242
+ # @param comment [String]
243
+ # @return [String]
244
+ def remove_inline_comment_hashes comment
245
+ ctxt = ''
246
+ num = nil
247
+ started = false
248
+ comment.lines.each do |l|
249
+ # Trim the comment and minimum leading whitespace
250
+ p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
251
+ if num.nil? && !p.strip.empty?
252
+ num = p.index(/[^ ]/)
253
+ started = true
254
+ elsif started && !p.strip.empty?
255
+ cur = p.index(/[^ ]/)
256
+ # @sg-ignore Need to add nil check here
257
+ num = cur if cur < num
258
+ end
259
+ ctxt += p[num..].to_s if started
260
+ end
261
+ ctxt
262
+ end
263
+
264
+ # @return [void]
265
+ def process_comment_directives
266
+ return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP
267
+ code_lines = @code.lines
268
+ @source.associated_comments.each do |line, comments|
269
+ src_pos = if line
270
+ Position.new(line,
271
+ code_lines[line].to_s.chomp.index(/[^\s]/) || 0)
272
+ else
273
+ Position.new(
274
+ code_lines.length, 0
275
+ )
276
+ end
277
+ # @sg-ignore Need to add nil check here
278
+ com_pos = Position.new(line + 1 - comments.lines.length, 0)
279
+ process_comment(src_pos, com_pos, comments)
280
+ end
281
+ rescue StandardError => e
282
+ raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
283
+ end
284
+ end
285
+ end
286
+ end