solargraph 0.57.0 → 0.58.3

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