module Starscope::Lang::Golang
Constants
- BUILTIN_FUNCS
- CONTROL_KEYS
- END_OF_BLOCK
- END_OF_GROUP
- FUNC_CALL
- STRING_LITERAL
- VERSION
Public Class Methods
end_block(line_no, scope, stack) { |:end, scope + ['}'], line_no: line_no, type: :class| ... }
click to toggle source
# File lib/starscope/langs/golang.rb, line 186 def self.end_block(line_no, scope, stack) yield :end, scope + ['}'], line_no: line_no, type: :class stack.pop scope.pop end
extract(_path, contents) { |:defs, scope + [last_match], line_no: line_no| ... }
click to toggle source
# File lib/starscope/langs/golang.rb, line 18 def self.extract(_path, contents, &block) stack = [] scope = [] contents.lines.each_with_index do |line, line_no| line_no += 1 # zero-index to one-index # strip single-line comments like // foo match = %r{//}.match(line) line = match.pre_match if match # strip single-line comments like foo /* foo */ foo match = %r{/\*.*\*/}.match(line) line = match.pre_match + match.post_match if match # strip end-of-line comment starters like foo /* foo \n match = %r{/\*}.match(line) line = match.pre_match if match ends_with_comment = !match.nil? # if we're in a block comment, wait for it to end if stack[-1] == :comment match = %r{\*/(.*)}.match(line) next unless match line = match[1] stack.pop end if stack[-1] != :import && !line.start_with?('import') # strip string literals like "foo" unless they're part of an import pos = 0 while (match = STRING_LITERAL.match(line, pos)) eos = find_end_of_string(line, match.begin(0)) line = line[0..match.begin(0)] + line[eos..-1] pos = match.begin(0) + 2 end end # poor-man's parser case stack[-1] when :struct case line when END_OF_BLOCK end_block(line_no, scope, stack, &block) when /(.+)\s+\w+/ parse_def(Regexp.last_match(1), line_no, scope, &block) end when :interface case line when END_OF_BLOCK end_block(line_no, scope, stack, &block) when /(\w+)\(.*\)\s+/ yield :defs, scope + [Regexp.last_match(1)], line_no: line_no end when :def case line when END_OF_GROUP stack.pop when /(.+)\s*=.*/ parse_def(Regexp.last_match(1), line_no, scope, &block) parse_call(line, line_no, scope, &block) else parse_def(line, line_no, scope, &block) end when :import case line when END_OF_GROUP stack.pop when /"(.+)"/ name = Regexp.last_match(1).split('/') yield :imports, name, line_no: line_no end when :func case line when /^\}/ yield :end, '}', line_no: line_no, type: :func stack.pop else parse_new_line(line, line_no, scope, stack, &block) end else parse_new_line(line, line_no, scope, stack, &block) end # if the line looks like "foo /* foo" then we enter the comment state # after parsing the usable part of the line stack.push(:comment) if ends_with_comment end end
find_end_of_string(line, start)
click to toggle source
# File lib/starscope/langs/golang.rb, line 192 def self.find_end_of_string(line, start) escape = false (start + 1...line.length).each do |i| if escape escape = false elsif line[i] == '\\' escape = true elsif line[i] == '"' return i end end line.length end
match_file(name)
click to toggle source
# File lib/starscope/langs/golang.rb, line 14 def self.match_file(name) name.end_with?('.go') end
parse_call(line, line_no, scope) { |:calls, name, line_no: line_no| ... }
click to toggle source
# File lib/starscope/langs/golang.rb, line 159 def self.parse_call(line, line_no, scope) line.scan(FUNC_CALL) do |match| name = match[0].split('.').select { |chunk| !chunk.empty? } if name.length == 1 next if name[0] == 'func' if BUILTIN_FUNCS.include?(name[0]) yield :calls, name[0], line_no: line_no else yield :calls, scope + [name[0]], line_no: line_no end else yield :calls, name, line_no: line_no end end end
parse_def(line, line_no, scope) { |:defs, scope + [delete(',')], line_no: line_no| ... }
click to toggle source
# File lib/starscope/langs/golang.rb, line 175 def self.parse_def(line, line_no, scope) # if it doesn't start with a valid identifier character, it's probably # part of a multi-line literal and we should skip it return unless line =~ /^\s*[[:alpha:]_]/ line.split.each do |var| yield :defs, scope + [var.delete(',')], line_no: line_no break unless var.end_with?(',') end end
parse_new_line(line, line_no, scope, stack) { |:defs, scope + [last_match], line_no: line_no, type: :func| ... }
click to toggle source
handles new lines (when not in the middle of an existing definition)
# File lib/starscope/langs/golang.rb, line 106 def self.parse_new_line(line, line_no, scope, stack, &block) case line when /^func\s+(\w+)\(/ yield :defs, scope + [Regexp.last_match(1)], line_no: line_no, type: :func stack.push(:func) when /^func\s+\(\w+\s+\*?(\w+)\)\s*(\w+)\(/ yield :defs, scope + [Regexp.last_match(1), Regexp.last_match(2)], line_no: line_no, type: :func stack.push(:func) when /^package\s+(\w+)/ scope.push(Regexp.last_match(1)) yield :defs, scope, line_no: line_no, type: :package when /^type\s+(\w+)\s+struct\s*\{/ scope.push(Regexp.last_match(1)) stack.push(:struct) yield :defs, scope, line_no: line_no, type: :class when /^type\s+(\w+)\s+interface\s*\{/ scope.push(Regexp.last_match(1)) stack.push(:interface) yield :defs, scope, line_no: line_no, type: :class when /^type\s+(\w+)/ yield :defs, scope + [Regexp.last_match(1)], line_no: line_no, type: :type when /^import\s+"(.+)"/ name = Regexp.last_match(1).split('/') yield :imports, name, line_no: line_no when /^import\s+\(/ stack.push(:import) when /^var\s+\(/ stack.push(:def) when /^var\s+(\w+)\s/ yield :defs, scope + [Regexp.last_match(1)], line_no: line_no parse_call(line, line_no, scope, &block) when /^const\s+\(/ stack.push(:def) when /^const\s+(\w+)\s/ yield :defs, scope + [Regexp.last_match(1)], line_no: line_no parse_call(line, line_no, scope, &block) when /^\s+(.*?) :?=[^=]/ Regexp.last_match(1).split(' ').each do |var| next if CONTROL_KEYS.include?(var) name = var.delete(',').split('.') next if name[0] == '_' # assigning to _ is a discard in golang if name.length == 1 yield :assigns, scope + [name[0]], line_no: line_no else yield :assigns, name, line_no: line_no end end parse_call(line, line_no, scope, &block) else parse_call(line, line_no, scope, &block) end end