Class: RubyLanguageServer::SEXPProcessor

Overview

This class is responsible for processing the generated sexp from the ScopeParser below. It builds scopes that amount to heirarchical arrays with information about what classes, methods, variables, etc - are in each scope.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RubyLanguageServer::ScopeParserCommands::RubyCommands

#on_attr_accessor_command, #on_attr_command, #on_attr_reader_command, #on_attr_writer_command

Methods included from RubyLanguageServer::ScopeParserCommands::RailsCommands

#rails_add_reference

Methods included from RubyLanguageServer::ScopeParserCommands::RspecCommands

#on_context_command, #on_describe_command, #on_it_command

Methods included from RubyLanguageServer::ScopeParserCommands::RakeCommands

#on_namespace_command, #on_task_command

Constructor Details

#initialize(sexp, lines = 1, shallow = false) ⇒ SEXPProcessor

Returns a new instance of SEXPProcessor.



20
21
22
23
24
25
# File 'lib/ruby_language_server/scope_parser.rb', line 20

def initialize(sexp, lines = 1, shallow = false)
  @sexp = sexp
  @lines = lines
  @shallow = shallow
  @root_scope = nil
end

Instance Attribute Details

#current_scopeObject (readonly)

Returns the value of attribute current_scope.



18
19
20
# File 'lib/ruby_language_server/scope_parser.rb', line 18

def current_scope
  @current_scope
end

#linesObject (readonly)

Returns the value of attribute lines.



18
19
20
# File 'lib/ruby_language_server/scope_parser.rb', line 18

def lines
  @lines
end

#sexpObject (readonly)

Returns the value of attribute sexp.



18
19
20
# File 'lib/ruby_language_server/scope_parser.rb', line 18

def sexp
  @sexp
end

Instance Method Details

#assign_subclass(scope, sexp) ⇒ Object



103
104
105
106
107
108
# File 'lib/ruby_language_server/scope_parser.rb', line 103

def assign_subclass(scope, sexp)
  return unless !sexp[0].nil? && sexp[0][0] == :var_ref

  (_, (_, name)) = sexp[0]
  scope.set_superclass_name(name)
end

#on_assign(args, rest) ⇒ Object



142
143
144
145
# File 'lib/ruby_language_server/scope_parser.rb', line 142

def on_assign(args, rest)
  process(args)
  process(rest)
end

#on_block_var(args, rest) ⇒ Object



130
131
132
133
# File 'lib/ruby_language_server/scope_parser.rb', line 130

def on_block_var(args, rest)
  process(args)
  process(rest)
end

#on_bodystmt(args, _rest) ⇒ Object



89
90
91
# File 'lib/ruby_language_server/scope_parser.rb', line 89

def on_bodystmt(args, _rest)
  process(args)
end

#on_class(args, rest) ⇒ Object



98
99
100
101
# File 'lib/ruby_language_server/scope_parser.rb', line 98

def on_class(args, rest)
  scope = add_scope(args.last, rest, ScopeData::Scope::TYPE_CLASS)
  assign_subclass(scope, rest)
end

#on_command(args, rest) ⇒ Object

The on_command function idea is stolen from RipperTags github.com/tmm1/ripper-tags/blob/master/lib/ripper-tags/parser.rb



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/ruby_language_server/scope_parser.rb', line 175

def on_command(args, rest)
  # [:@ident, "public", [6, 8]]
  (_, name, (line, _column)) = args

  method_name = "on_#{name}_command"
  if respond_to? method_name
    return send(method_name, line, args, rest)
  else
    RubyLanguageServer.logger.debug("We don't have a #{method_name} with #{args}")
  end

  case name
  when 'public', 'private', 'protected'
    # FIXME: access control...
    process(rest)
  when 'delegate'
    # on_delegate(*args[0][1..-1])
  when 'def_delegator', 'def_instance_delegator'
    # on_def_delegator(*args[0][1..-1])
  when 'def_delegators', 'def_instance_delegators'
    # on_def_delegators(*args[0][1..-1])
  end
end

#on_def(args, rest) ⇒ Object



147
148
149
# File 'lib/ruby_language_server/scope_parser.rb', line 147

def on_def(args, rest)
  add_scope(args, rest, ScopeData::Scope::TYPE_METHOD)
end

#on_defs(args, rest) ⇒ Object

def self.something(par)…

:var_ref, [:@kw, “self”, [28, 14]]], [[:@period, “.”, [28, 18]], [:@ident, “something”, [28, 19]], [:paren, [:params, [[:@ident, “par”, [28, 23]]], nil, nil, nil, nil, nil, nil]], [:bodystmt, [[:assign, [:var_field, [:@ident, “pax”, [29, 12]]], [:var_ref, [:@ident, “par”, [29, 18]]]]], nil, nil, nil]


153
154
155
# File 'lib/ruby_language_server/scope_parser.rb', line 153

def on_defs(args, rest)
  on_def(rest[1], rest[2..]) if args[1][1] == 'self' && rest[0][1] == '.'
end

#on_do_block(args, rest) ⇒ Object



122
123
124
125
126
127
128
# File 'lib/ruby_language_server/scope_parser.rb', line 122

def on_do_block(args, rest)
  ((_, ((_, (_, (_, _name, (line, column))))))) = args
  push_scope(ScopeData::Scope::TYPE_BLOCK, 'block', line, column, false)
  process(args)
  process(rest)
  pop_scope
end

#on_ident(name, line, column) ⇒ Object

ident is something that gets processed at parameters to a function or block



165
166
167
# File 'lib/ruby_language_server/scope_parser.rb', line 165

def on_ident(name, ((line, column)))
  add_variable(name, line, column)
end

#on_method_add_arg(call, args) ⇒ Object

The on_method_add_arg function is downright stolen from RipperTags github.com/tmm1/ripper-tags/blob/master/lib/ripper-tags/parser.rb



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/ruby_language_server/scope_parser.rb', line 200

def on_method_add_arg(call, args)
  call_name = call && call[0]
  first_arg = args && args[0] == :args && args[1]

  if call_name == :call && first_arg
    if args.length == 2
      # augment call if a single argument was used
      call = call.dup
      call[3] = args[1]
    end
    call
  elsif call_name == :fcall && first_arg
    name, line = call[1]
    case name
    when 'alias_method' # this is an fcall
      [:alias, args[1][0], args[2][0], line] if args[1] && args[2]
    when 'define_method' # this is an fcall
      [:def, args[1][0], line]
    when 'public_class_method', 'private_class_method', 'private', 'public', 'protected'
      access = name.sub('_class_method', '')

      if args[1][1] == 'self'
        klass = 'self'
        method_name = args[1][2]
      else
        klass = nil
        method_name = args[1][1]
      end

      [:def_with_access, klass, method_name, access, line]
    end
  end
end

#on_method_add_block(args, rest) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ruby_language_server/scope_parser.rb', line 110

def on_method_add_block(args, rest)
  scope = @current_scope
  process(args)
  process(rest)
  # add_scope(args, rest, ScopeData::Scope::TYPE_BLOCK)
  unless @current_scope == scope
    scope.bottom_line = [scope&.bottom_line, @current_scope.bottom_line].compact.max
    scope.save!
    pop_scope
  end
end

#on_mlhs(args, rest) ⇒ Object

Multiple left hand side (foo, bar) = somethingg…



159
160
161
162
# File 'lib/ruby_language_server/scope_parser.rb', line 159

def on_mlhs(args, rest)
  process(args)
  process(rest)
end

#on_module(args, rest) ⇒ Object



93
94
95
96
# File 'lib/ruby_language_server/scope_parser.rb', line 93

def on_module(args, rest)
  scope = add_scope(args.last, rest, ScopeData::Scope::TYPE_MODULE)
  assign_subclass(scope, rest)
end

#on_params(args, rest) ⇒ Object



169
170
171
172
# File 'lib/ruby_language_server/scope_parser.rb', line 169

def on_params(args, rest)
  process(args)
  process(rest)
end

#on_program(args, _rest) ⇒ Object



73
74
75
# File 'lib/ruby_language_server/scope_parser.rb', line 73

def on_program(args, _rest)
  process(args)
end

#on_sclass(_args, rest) ⇒ Object



63
64
65
# File 'lib/ruby_language_server/scope_parser.rb', line 63

def on_sclass(_args, rest)
  process(rest)
end

#on_var_field(args, rest) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ruby_language_server/scope_parser.rb', line 77

def on_var_field(args, rest)
  (_, name, (line, column)) = args
  return if name.nil?

  if name.start_with?('@')
    add_ivar(name, line, column)
  else
    add_variable(name, line, column)
  end
  process(rest)
end

#on_var_ref(_args, _rest) ⇒ Object

Used only to describe subclasses? – nope



136
137
138
139
140
# File 'lib/ruby_language_server/scope_parser.rb', line 136

def on_var_ref(_args, _rest)
  # [:@const, "Bar", [13, 20]]
  # (_, name) = args
  # @current_scope.set_superclass_name(name)
end

#on_vcall(_args, rest) ⇒ Object

foo = bar – bar is in the vcall. Pretty sure we don’t want to remember this.



68
69
70
71
# File 'lib/ruby_language_server/scope_parser.rb', line 68

def on_vcall(_args, rest)
  # Seriously - discard args.  Maybe process rest?
  process(rest)
end

#process(sexp) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/ruby_language_server/scope_parser.rb', line 36

def process(sexp)
  return if sexp.nil?

  root, args, *rest = sexp
  # RubyLanguageServer.logger.error("Doing #{[root, args, rest]}")
  case root
  when Array
    sexp.each { |child| process(child) }
  when Symbol
    root = root.to_s.gsub(/^@+/, '')
    method_name = "on_#{root}"
    if respond_to? method_name
      send(method_name, args, rest)
    else
      RubyLanguageServer.logger.debug("We don't have a #{method_name} with #{args}")
      process(args)
    end
  when String
    # We really don't do anything with it!
    RubyLanguageServer.logger.debug("We don't do Strings like #{root} with #{args}")
  when NilClass, FalseClass
    process(args)
  else
    RubyLanguageServer.logger.warn("We don't respond to the likes of #{root} of class #{root.class}")
  end
end

#root_scopeObject



27
28
29
30
31
32
33
34
# File 'lib/ruby_language_server/scope_parser.rb', line 27

def root_scope
  return @root_scope unless @root_scope.nil?

  @root_scope = ScopeData::Scope.where(path: nil, class_type: ScopeData::Scope::TYPE_ROOT).first_or_create!
  @current_scope = @root_scope
  process(@sexp)
  @root_scope
end