Class: Solargraph::SourceMap::Mapper
- Inherits:
-
Object
- Object
- Solargraph::SourceMap::Mapper
- Defined in:
- lib/solargraph/source_map/mapper.rb
Overview
The Mapper generates pins and other data for SourceMaps.
This class is used internally by the SourceMap class. Users should not normally need to call it directly.
Constant Summary collapse
- DIRECTIVE_REGEXP =
/(@!method|@!attribute|@!visibility|@!domain|@!macro|@!parse|@!override)/
Class Method Summary collapse
Instance Method Summary collapse
- #closure_at(position) ⇒ Solargraph::Pin::Closure
- #find_directive_line_number(comment, tag, start) ⇒ Integer
-
#map(source) ⇒ Array
Generate the data.
-
#no_empty_lines?(line1, line2) ⇒ Boolean
@sg-ignore Need to add nil check here.
- #pins ⇒ Array<Solargraph::Pin::Base>
- #process_comment(source_position, comment_position, comment) ⇒ void
- #process_comment_directives ⇒ void
- #process_directive(source_position, comment_position, directive) ⇒ void
- #remove_inline_comment_hashes(comment) ⇒ String
- #unmap(filename, code) ⇒ Array
Class Method Details
.map(source) ⇒ Array
51 52 53 54 55 |
# File 'lib/solargraph/source_map/mapper.rb', line 51 def map source # @sg-ignore Need to add nil check here return new.unmap(source.filename, source.code) unless source.parsed? new.map source end |
Instance Method Details
#closure_at(position) ⇒ Solargraph::Pin::Closure
66 67 68 69 |
# File 'lib/solargraph/source_map/mapper.rb', line 66 def closure_at position # @sg-ignore Need to add nil check here pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position) }.last end |
#find_directive_line_number(comment, tag, start) ⇒ Integer
93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/solargraph/source_map/mapper.rb', line 93 def find_directive_line_number comment, tag, start # Avoid overruning the index return start unless start < comment.lines.length # @sg-ignore Need to add nil check here num = comment.lines[start..].find_index do |line| # Legacy method directives might be `@method` instead of `@!method` # @todo Legacy syntax should probably emit a warning line.include?("@!#{tag}") || (tag == 'method' && line.include?("@#{tag}")) end # @sg-ignore Need to add nil check here num.to_i + start end |
#map(source) ⇒ Array
Generate the data.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/solargraph/source_map/mapper.rb', line 21 def map source @source = source @filename = source.filename @code = source.code @comments = source.comments @pins, @locals = Parser.map(source) # @param p [Solargraph::Pin::Base] @pins.each { |p| p.source = :code } @locals.each { |l| l.source = :code } process_comment_directives [@pins, @locals] # rescue Exception => e # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}" # Solargraph.logger.warn e.backtrace.join("\n") # [[], []] end |
#no_empty_lines?(line1, line2) ⇒ Boolean
@sg-ignore Need to add nil check here
237 238 239 240 |
# File 'lib/solargraph/source_map/mapper.rb', line 237 def no_empty_lines? line1, line2 # @sg-ignore Need to add nil check here @code.lines[line1..line2].none? { |line| line.strip.empty? } end |
#pins ⇒ Array<Solargraph::Pin::Base>
59 60 61 62 |
# File 'lib/solargraph/source_map/mapper.rb', line 59 def pins # @type [Array<Solargraph::Pin::Base>] @pins ||= [] end |
#process_comment(source_position, comment_position, comment) ⇒ void
This method returns an undefined value.
75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/solargraph/source_map/mapper.rb', line 75 def process_comment source_position, comment_position, comment return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP cmnt = remove_inline_comment_hashes(comment) parse = Solargraph::Source.parse_docstring(cmnt) last_line = 0 # @param d [YARD::Tags::Directive] parse.directives.each do |d| line_num = find_directive_line_number(cmnt, d.tag.tag_name, last_line) pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column) process_directive(source_position, pos, d) last_line = line_num + 1 end end |
#process_comment_directives ⇒ void
This method returns an undefined value.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/solargraph/source_map/mapper.rb', line 265 def process_comment_directives return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP code_lines = @code.lines @source.associated_comments.each do |line, comments| src_pos = if line Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) else Position.new( code_lines.length, 0 ) end # @sg-ignore Need to add nil check here com_pos = Position.new(line + 1 - comments.lines.length, 0) process_comment(src_pos, com_pos, comments) end rescue StandardError => e raise e.class, "Error processing comment directives in #{@filename}: #{e.}" end |
#process_directive(source_position, comment_position, directive) ⇒ void
This method returns an undefined value.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/solargraph/source_map/mapper.rb', line 110 def process_directive source_position, comment_position, directive # @sg-ignore Need to add nil check here docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring location = Location.new(@filename, Range.new(comment_position, comment_position)) case directive.tag.tag_name when 'method' namespace = closure_at(source_position) || @pins.first # @sg-ignore Need to add nil check here namespace = closure_at(comment_position) if namespace.location.range.start.line < comment_position.line begin src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename) region = Parser::Region.new(source: src, closure: namespace) # @type [Array<Pin::Method>] method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) } gen_pin = method_gen_pins.last return if gen_pin.nil? # Move the location to the end of the line so it gets recognized # as originating from a comment shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].to_s.chomp.length) # @todo: Smelly instance variable access gen_pin.instance_variable_set(:@comments, docstring.all.to_s) gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted))) gen_pin.instance_variable_set(:@explicit, false) @pins.push gen_pin rescue Parser::SyntaxError # @todo Handle error in directive end when 'attribute' return if directive.tag.name.nil? namespace = closure_at(source_position) t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.join if t.nil? || t.include?('r') pins.push Solargraph::Pin::Method.new( location: location, closure: namespace, name: directive.tag.name, comments: docstring.all.to_s, scope: namespace.is_a?(Pin::Singleton) ? :class : :instance, visibility: :public, explicit: false, attribute: true, source: :source_map ) end if t.nil? || t.include?('w') method_pin = Solargraph::Pin::Method.new( location: location, closure: namespace, name: "#{directive.tag.name}=", comments: docstring.all.to_s, scope: namespace.is_a?(Pin::Singleton) ? :class : :instance, visibility: :public, attribute: true, source: :source_map ) pins.push method_pin method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map) if pins.last.return_type.defined? pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value') end end when 'visibility' kind = directive.tag.text&.to_sym return unless %i[private protected public].include?(kind) name = directive.tag.name closure = closure_at(source_position) || @pins.first # @sg-ignore Need to add nil check here closure = closure_at(comment_position) if closure.location.range.start.line < comment_position.line if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line) # @todo Smelly instance variable access closure.instance_variable_set(:@visibility, kind) else matches = pins.select do |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance end matches.each do |pin| # @todo Smelly instance variable access pin.instance_variable_set(:@visibility, kind) end end when 'parse' begin ns = closure_at(source_position) # @sg-ignore Need to add nil check here src = Solargraph::Source.load_string(directive.tag.text, @source.filename) region = Parser::Region.new(source: src, closure: ns) # @todo These pins may need to be marked not explicit index = @pins.length loff = if @code.lines[comment_position.line].strip.end_with?('@!parse') comment_position.line + 1 else comment_position.line end locals = [] ivars = [] Parser.process_node(src.node, region, @pins, locals, ivars) @pins.concat ivars # @sg-ignore Need to add nil check here @pins[index..].each do |p| # @todo Smelly instance variable access p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff) p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff) end rescue Parser::SyntaxError # @todo Handle parser errors in !parse directives end when 'domain' namespace = closure_at(source_position) || Pin::ROOT_PIN # @sg-ignore flow sensitive typing should be able to handle redefinition namespace.domains.concat directive.tag.types unless directive.tag.types.nil? when 'override' pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring., source: :source_map) when 'macro' # @todo Handle macros end end |
#remove_inline_comment_hashes(comment) ⇒ String
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/solargraph/source_map/mapper.rb', line 244 def remove_inline_comment_hashes comment ctxt = '' num = nil started = false comment.lines.each do |l| # Trim the comment and minimum leading whitespace p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '') if num.nil? && !p.strip.empty? num = p.index(/[^ ]/) started = true elsif started && !p.strip.empty? cur = p.index(/[^ ]/) # @sg-ignore Need to add nil check here num = cur if cur < num end ctxt += p[num..].to_s if started end ctxt end |
#unmap(filename, code) ⇒ Array
41 42 43 44 45 46 |
# File 'lib/solargraph/source_map/mapper.rb', line 41 def unmap filename, code s = Position.new(0, 0) e = Position.from_offset(code, code.length) location = Location.new(filename, Range.new(s, e)) [[Pin::Namespace.new(location: location, name: '', source: :source_map)], []] end |