class Solargraph::SourceMap::Mapper
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.
Constants
- MACRO_REGEXP
include Source::NodeMethods
Public Class Methods
map(source)
click to toggle source
@param source [Source] @return [Array]
# File lib/solargraph/source_map/mapper.rb, line 48 def map source return new.unmap(source.filename, source.code) unless source.parsed? new.map source end
Public Instance Methods
closure_at(position)
click to toggle source
# File lib/solargraph/source_map/mapper.rb, line 59 def closure_at(position) @pins.select{|pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position)}.last end
find_directive_line_number(comment, tag, start)
click to toggle source
@param comment [String] @return [Integer]
# File lib/solargraph/source_map/mapper.rb, line 84 def find_directive_line_number comment, tag, start # Avoid overruning the index return start unless start < comment.lines.length num = comment.lines[start..-1].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 num.to_i + start end
map(source)
click to toggle source
Generate the data.
@param source [Source] @return [Array]
# 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) 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
pins()
click to toggle source
@return [Array<Solargraph::Pin::Base>]
# File lib/solargraph/source_map/mapper.rb, line 55 def pins @pins ||= [] end
process_comment(source_position, comment_position, comment)
click to toggle source
# File lib/solargraph/source_map/mapper.rb, line 63 def process_comment source_position, comment_position, comment return unless comment =~ MACRO_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 # @todo The below call assumes the topmost comment line. The above # process occasionally emits incorrect comment positions due to # blank lines in comment blocks, but at least it processes all the # directives. # process_directive(source_position, comment_position, d) end end
process_comment_directives()
click to toggle source
@return [void]
# File lib/solargraph/source_map/mapper.rb, line 204 def process_comment_directives return unless @code =~ MACRO_REGEXP code_lines = @code.lines @source.associated_comments.each do |line, comments| src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0) com_pos = Position.new(line + 1 - comments.lines.length, 0) process_comment(src_pos, com_pos, comments) end end
process_directive(source_position, comment_position, directive)
click to toggle source
@param source_position [Position] @param comment_position [Position] @param directive [YARD::Tags::Directive] @return [void]
# File lib/solargraph/source_map/mapper.rb, line 99 def process_directive source_position, comment_position, directive 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 if namespace.location.range.start.line < comment_position.line namespace = closure_at(comment_position) end begin src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename) region = Parser::Region.new(source: src, closure: namespace) gen_pin = Parser.process_node(src.node, region).first.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 => e # @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.flatten.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 ) end if t.nil? || t.include?('w') 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, attribute: true ) pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last) 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 'parse' begin ns = closure_at(source_position) 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 Parser.process_node(src.node, region, @pins) @pins[index..-1].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 => e # @todo Handle parser errors in !parse directives end when 'domain' namespace = closure_at(source_position) namespace.domains.concat directive.tag.types unless directive.tag.types.nil? when 'override' pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags) end end
remove_inline_comment_hashes(comment)
click to toggle source
# File lib/solargraph/source_map/mapper.rb, line 184 def remove_inline_comment_hashes comment ctxt = '' num = nil started = false comment.lines.each { |l| # Trim the comment and minimum leading whitespace p = l.gsub(/^#/, '') if num.nil? && !p.strip.empty? num = p.index(/[^ ]/) started = true elsif started && !p.strip.empty? cur = p.index(/[^ ]/) num = cur if cur < num end ctxt += "#{p[num..-1]}" if started } ctxt end
unmap(filename, code)
click to toggle source
@param filename [String] @param code [String] @return [Array]
# File lib/solargraph/source_map/mapper.rb, line 38 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: '')], []] end