solargraph 0.58.1 → 0.58.2
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +7 -1
- data/lib/solargraph/api_map/cache.rb +110 -110
- data/lib/solargraph/api_map/constants.rb +279 -279
- data/lib/solargraph/api_map/index.rb +193 -193
- data/lib/solargraph/api_map/source_to_yard.rb +97 -97
- data/lib/solargraph/api_map/store.rb +384 -384
- data/lib/solargraph/api_map.rb +945 -945
- data/lib/solargraph/complex_type/type_methods.rb +228 -228
- data/lib/solargraph/complex_type/unique_type.rb +482 -482
- data/lib/solargraph/complex_type.rb +444 -444
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
- data/lib/solargraph/convention/data_definition.rb +105 -105
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
- data/lib/solargraph/convention/struct_definition.rb +164 -164
- data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
- data/lib/solargraph/diagnostics/rubocop.rb +118 -118
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -68
- data/lib/solargraph/diagnostics/type_check.rb +55 -55
- data/lib/solargraph/doc_map.rb +439 -439
- data/lib/solargraph/equality.rb +34 -34
- data/lib/solargraph/gem_pins.rb +98 -98
- data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
- data/lib/solargraph/language_server/host/dispatch.rb +130 -130
- data/lib/solargraph/language_server/host/message_worker.rb +112 -112
- data/lib/solargraph/language_server/host/sources.rb +99 -99
- data/lib/solargraph/language_server/host.rb +878 -878
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
- data/lib/solargraph/language_server/message/extended/document.rb +23 -23
- data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
- data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
- data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -148
- data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
- data/lib/solargraph/library.rb +683 -683
- data/lib/solargraph/location.rb +82 -82
- data/lib/solargraph/logging.rb +37 -37
- data/lib/solargraph/parser/comment_ripper.rb +69 -69
- data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
- data/lib/solargraph/parser/node_processor/base.rb +92 -92
- data/lib/solargraph/parser/node_processor.rb +62 -62
- data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -149
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
- data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -486
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -52
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -291
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
- data/lib/solargraph/parser/region.rb +69 -69
- data/lib/solargraph/parser/snippet.rb +17 -17
- data/lib/solargraph/pin/base.rb +729 -729
- data/lib/solargraph/pin/base_variable.rb +126 -126
- data/lib/solargraph/pin/block.rb +104 -104
- data/lib/solargraph/pin/breakable.rb +9 -9
- data/lib/solargraph/pin/callable.rb +231 -231
- data/lib/solargraph/pin/closure.rb +72 -72
- data/lib/solargraph/pin/common.rb +79 -79
- data/lib/solargraph/pin/conversions.rb +123 -123
- data/lib/solargraph/pin/delegated_method.rb +120 -120
- data/lib/solargraph/pin/documenting.rb +114 -114
- data/lib/solargraph/pin/instance_variable.rb +34 -34
- data/lib/solargraph/pin/keyword.rb +20 -20
- data/lib/solargraph/pin/local_variable.rb +75 -75
- data/lib/solargraph/pin/method.rb +672 -672
- data/lib/solargraph/pin/method_alias.rb +34 -34
- data/lib/solargraph/pin/namespace.rb +115 -115
- data/lib/solargraph/pin/parameter.rb +275 -275
- data/lib/solargraph/pin/proxy_type.rb +39 -39
- data/lib/solargraph/pin/reference/override.rb +47 -47
- data/lib/solargraph/pin/reference/superclass.rb +15 -15
- data/lib/solargraph/pin/reference.rb +39 -39
- data/lib/solargraph/pin/search.rb +61 -61
- data/lib/solargraph/pin/signature.rb +61 -61
- data/lib/solargraph/pin/symbol.rb +53 -53
- data/lib/solargraph/pin/until.rb +18 -18
- data/lib/solargraph/pin/while.rb +18 -18
- data/lib/solargraph/pin.rb +44 -44
- data/lib/solargraph/pin_cache.rb +245 -245
- data/lib/solargraph/position.rb +132 -119
- data/lib/solargraph/range.rb +112 -112
- data/lib/solargraph/rbs_map/conversions.rb +823 -823
- data/lib/solargraph/rbs_map/core_map.rb +58 -58
- data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
- data/lib/solargraph/rbs_map.rb +163 -163
- data/lib/solargraph/shell.rb +352 -352
- data/lib/solargraph/source/chain/call.rb +337 -337
- data/lib/solargraph/source/chain/constant.rb +26 -26
- data/lib/solargraph/source/chain/hash.rb +34 -34
- data/lib/solargraph/source/chain/if.rb +28 -28
- data/lib/solargraph/source/chain/instance_variable.rb +13 -13
- data/lib/solargraph/source/chain/literal.rb +48 -48
- data/lib/solargraph/source/chain/or.rb +23 -23
- data/lib/solargraph/source/chain.rb +291 -291
- data/lib/solargraph/source/change.rb +82 -82
- data/lib/solargraph/source/cursor.rb +166 -166
- data/lib/solargraph/source/source_chainer.rb +194 -194
- data/lib/solargraph/source/updater.rb +55 -55
- data/lib/solargraph/source.rb +498 -498
- data/lib/solargraph/source_map/clip.rb +226 -226
- data/lib/solargraph/source_map/data.rb +34 -34
- data/lib/solargraph/source_map/mapper.rb +259 -259
- data/lib/solargraph/source_map.rb +212 -212
- data/lib/solargraph/type_checker/checks.rb +124 -124
- data/lib/solargraph/type_checker/param_def.rb +37 -37
- data/lib/solargraph/type_checker/problem.rb +32 -32
- data/lib/solargraph/type_checker/rules.rb +84 -84
- data/lib/solargraph/type_checker.rb +814 -814
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +255 -255
- data/lib/solargraph/workspace/require_paths.rb +97 -97
- data/lib/solargraph/workspace.rb +220 -220
- data/lib/solargraph/yard_map/helpers.rb +44 -44
- data/lib/solargraph/yard_map/mapper/to_method.rb +130 -130
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -31
- data/lib/solargraph/yard_map/mapper.rb +79 -79
- data/lib/solargraph/yard_map/to_method.rb +89 -89
- data/lib/solargraph/yardoc.rb +87 -87
- data/lib/solargraph.rb +105 -105
- data/rbs_collection.yaml +1 -1
- metadata +12 -12
- /data/{sig → rbs}/shims/ast/0/node.rbs +0 -0
- /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
|
@@ -1,259 +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
|
-
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)/.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
|