solargraph 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: acab722ec6710f43f14625320985dbcd30dd2e19
4
- data.tar.gz: c19b6542c62b928a4b585df460a131e70ab58912
3
+ metadata.gz: e99bdc143ca29ca298dc48668e2eab23a73fb338
4
+ data.tar.gz: a2c8fc45842f2d30f30c964f427962992223315e
5
5
  SHA512:
6
- metadata.gz: e310d1270047cd3dbd9f21c4249efeaf3342417233bf1484f0e4cb86d944b1a6c24bf900a4a00580c86413f9b130f51289a705579e533f5bbdeab02958b13b49
7
- data.tar.gz: 6815c3a16e85e7246e86ca3fe7358b4732b91fac3a09867fd149c32d9be7903436ac10f72650ddfb472164ca0be7bd322822da8be43385261844a9c581f109c5
6
+ metadata.gz: e7eee90d32b54be8ed26faa90c55794c68981e90e6fae4dfafba8a226847bb06fe90485876a82be679d83bca3fac5eb33c63072d98a517418c6437ba91956873
7
+ data.tar.gz: c8b992d21569e07e36db02c08c332c7309ee206daf81ff5489ce440a308b50d1e6cdabc337b728ecd37403e745f1f6fc4ffbb1562e3d486908f8372b2cafcd5d
@@ -15,14 +15,20 @@ module Solargraph
15
15
  MAPPABLE_METHODS = [
16
16
  :include, :require, :autoload, :attr_reader, :attr_writer, :attr_accessor, :private, :public, :protected
17
17
  ]
18
+
18
19
  include NodeMethods
19
20
 
20
21
  attr_reader :workspace
21
22
 
22
23
  def initialize workspace = nil
23
24
  @workspace = workspace
24
- #process_workspace
25
25
  clear
26
+ unless @workspace.nil? #or has_yardoc?
27
+ files = Dir[File.join workspace, 'lib', '**', '*.rb'] + Dir[File.join workspace, 'app', '**', '*.rb']
28
+ files.each { |f|
29
+ append_file f
30
+ }
31
+ end
26
32
  end
27
33
 
28
34
  def clear
@@ -31,17 +37,12 @@ module Solargraph
31
37
  @parent_stack = {}
32
38
  @namespace_map = {}
33
39
  @namespace_tree = {}
34
- #@pending_requires = []
35
40
  @required = []
36
41
  end
37
42
 
38
- #def process_workspace
39
- # clear
40
- # return if @workspace.nil?
41
- # process_files
42
- # process_requires
43
- # process_maps
44
- #end
43
+ def has_yardoc?
44
+ workspace and File.exist?(File.join(workspace, '.yardoc'))
45
+ end
45
46
 
46
47
  def append_file filename
47
48
  append_source File.read(filename), filename
@@ -53,22 +54,21 @@ module Solargraph
53
54
  end
54
55
 
55
56
  def append_node node, comments, filename = nil
56
- mapified = mapify(node)
57
+ @file_comments[filename] = associate_comments(node, comments)
58
+ mapified = reduce(node, @file_comments[filename])
57
59
  root = AST::Node.new(:begin, [filename])
58
60
  mapified.children.each { |c|
59
61
  root = root.append c
60
62
  }
61
63
  @file_nodes[filename] = root
62
- @file_comments[filename] = associate_comments(mapified, comments)
63
64
  @required.uniq!
64
65
  process_maps
65
66
  end
66
67
 
67
68
  def associate_comments node, comments
68
- comment_hash = Parser::Source::Comment.associate(node, comments)
69
+ comment_hash = Parser::Source::Comment.associate_locations(node, comments)
69
70
  yard_hash = {}
70
71
  comment_hash.each_pair { |k, v|
71
- #ctxt = v.map(&:text).join("\r\n")
72
72
  ctxt = ''
73
73
  v.each { |l|
74
74
  ctxt += l.text.gsub(/^#/, '') + "\n"
@@ -81,7 +81,8 @@ module Solargraph
81
81
 
82
82
  def get_comment_for node
83
83
  filename = get_filename_for(node)
84
- @file_comments[filename][node] unless @file_comments[filename].nil?
84
+ return nil if @file_comments[filename].nil?
85
+ @file_comments[filename][node.loc]
85
86
  end
86
87
 
87
88
  def self.get_keywords without_snippets: false
@@ -142,7 +143,17 @@ module Solargraph
142
143
  }
143
144
  unless cursor.nil?
144
145
  cursor.keys.each { |k|
145
- result.push Suggestion.new(k, kind: Suggestion::CLASS)
146
+ type = get_namespace_type(k, fqns)
147
+ kind = nil
148
+ detail = nil
149
+ if type == :class
150
+ kind = Suggestion::CLASS
151
+ detail = 'Class'
152
+ elsif type == :module
153
+ kind = Suggestion::MODULE
154
+ detail = 'Module'
155
+ end
156
+ result.push Suggestion.new(k, kind: kind, detail: detail)
146
157
  }
147
158
  nodes = get_namespace_nodes(fqns)
148
159
  nodes.each { |n|
@@ -225,7 +236,6 @@ module Solargraph
225
236
  if node.kind_of?(AST::Node)
226
237
  node.children.each { |c|
227
238
  if c.kind_of?(AST::Node)
228
- #next if [:class, :module].include?(c.type)
229
239
  is_inst = !find_parent(c, :def).nil?
230
240
  if c.type == :ivasgn and ( (scope == :instance and is_inst) or (scope != :instance and !is_inst) )
231
241
  arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE)
@@ -266,7 +276,7 @@ module Solargraph
266
276
  end
267
277
 
268
278
  def get_global_variables
269
- # TODO I bet these aren't getting mapped at all. Damn.
279
+ # TODO: Get them
270
280
  []
271
281
  end
272
282
 
@@ -323,19 +333,25 @@ module Solargraph
323
333
  end
324
334
 
325
335
  def get_instance_methods(namespace, root = '')
326
- meths = inner_get_instance_methods(namespace, root, [])
336
+ meths = []
337
+ meths += inner_get_instance_methods(namespace, root, []) unless has_yardoc?
327
338
  yard = YardMap.new(required: @required, workspace: @workspace)
328
- type = get_namespace_type(namespace, root)
329
- if type == :class
330
- meths += yard.get_instance_methods('Object')
331
- elsif type == :module
332
- meths += yard.get_instance_methods('Module')
333
- end
334
- meths += yard.get_instance_methods(namespace, root)
335
- sc = get_superclass(namespace, root)
336
- until sc.nil?
337
- meths += yard.get_instance_methods(sc, root)
338
- sc = get_superclass(sc)
339
+ yard_meths = yard.get_instance_methods(namespace, root)
340
+ if yard_meths.any?
341
+ meths.concat yard_meths
342
+ else
343
+ type = get_namespace_type(namespace, root)
344
+ if type == :class
345
+ meths += yard.get_instance_methods('Object')
346
+ elsif type == :module
347
+ meths += yard.get_instance_methods('Module')
348
+ end
349
+ # TODO: Look out for repeats. Consider not doing this at all.
350
+ sc = get_superclass(namespace, root)
351
+ until sc.nil?
352
+ meths += yard.get_instance_methods(sc, root)
353
+ sc = get_superclass(sc)
354
+ end
339
355
  end
340
356
  meths
341
357
  end
@@ -374,7 +390,7 @@ module Solargraph
374
390
  elsif current_scope == :public
375
391
  if c.kind_of?(AST::Node) and c.type == :def
376
392
  cmnt = get_comment_for(c)
377
- meths.push Suggestion.new(c.children[0], kind: Suggestion::METHOD, documentation: cmnt) if c.children[0].to_s[0].match(/[a-z]/i)
393
+ meths.push Suggestion.new(c.children[0], kind: Suggestion::METHOD, documentation: cmnt, detail: fqns) if c.children[0].to_s[0].match(/[a-z]/i)
378
394
  elsif c.kind_of?(AST::Node) and c.type == :send and c.children[1] == :attr_reader
379
395
  c.children[2..-1].each { |x|
380
396
  meths.push Suggestion.new(x.children[0], kind: Suggestion::METHOD) if x.type == :sym
@@ -421,15 +437,6 @@ module Solargraph
421
437
 
422
438
  private
423
439
 
424
- def mapify node
425
- root = node
426
- if !root.kind_of?(AST::Node) or root.type != :begin
427
- root = AST::Node.new(:begin, [node], {})
428
- end
429
- root = reduce root
430
- root
431
- end
432
-
433
440
  def mappable?(node)
434
441
  # TODO Add node.type :casgn (constant assignment)
435
442
  if node.kind_of?(AST::Node) and (node.type == :class or node.type == :module or node.type == :def or node.type == :defs or node.type == :ivasgn or node.type == :gvasgn or node.type == :or_asgn)
@@ -441,42 +448,42 @@ module Solargraph
441
448
  end
442
449
  end
443
450
 
444
- def reduce node
445
- mappable = get_mappable_nodes(node.children)
451
+ def reduce node, comment_hash
452
+ mappable = get_mappable_nodes(node.children, comment_hash)
446
453
  result = node.updated nil, mappable
447
454
  result
448
455
  end
449
456
 
450
- def get_mappable_nodes arr
457
+ def get_mappable_nodes arr, comment_hash
451
458
  result = []
452
459
  arr.each { |n|
453
460
  if mappable?(n)
454
- min = minify(n)
461
+ min = minify(n, comment_hash)
455
462
  result.push min
456
463
  else
457
464
  next unless n.kind_of?(AST::Node)
458
- result += get_mappable_nodes(n.children)
465
+ result += get_mappable_nodes(n.children, comment_hash)
459
466
  end
460
467
  }
461
468
  result
462
469
  end
463
470
 
464
- def minify node
471
+ def minify node, comment_hash
465
472
  return node if node.type == :args
466
473
  type = node.type
467
474
  children = []
468
475
  if node.type == :class
469
476
  children += node.children[0, 2]
470
- children += get_mappable_nodes(node.children[2..-1])
477
+ children += get_mappable_nodes(node.children[2..-1], comment_hash)
471
478
  elsif node.type == :def
472
479
  children += node.children[0, 2]
473
- children += get_mappable_nodes(node.children[2..-1])
480
+ children += get_mappable_nodes(node.children[2..-1], comment_hash)
474
481
  elsif node.type == :defs
475
482
  children += node.children[0, 3]
476
- children += get_mappable_nodes(node.children[3..-1])
483
+ children += get_mappable_nodes(node.children[3..-1], comment_hash)
477
484
  elsif node.type == :module
478
485
  children += node.children[0, 1]
479
- children += get_mappable_nodes(node.children[1..-1])
486
+ children += get_mappable_nodes(node.children[1..-1], comment_hash)
480
487
  elsif node.type == :ivasgn or node.type == :gvasgn
481
488
  children += node.children
482
489
  elsif node.type == :send and node.children[1] == :include
@@ -500,7 +507,7 @@ module Solargraph
500
507
  end
501
508
 
502
509
  def map_parents node, tree = []
503
- if node.kind_of?(AST::Node) #and (node.type == :class or node.type == :module)
510
+ if node.kind_of?(AST::Node)
504
511
  @parent_stack[node] = tree
505
512
  node.children.each { |c|
506
513
  map_parents c, [node] + tree
@@ -7,26 +7,30 @@ module Solargraph
7
7
 
8
8
  include NodeMethods
9
9
 
10
- def initialize code: '', filename: nil
11
- workspace = nil
12
- unless filename.nil?
13
- filename.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
10
+ def initialize code: '', filename: nil, workspace: nil
11
+ if workspace.nil? and !filename.nil?
12
+ filename = filename.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
14
13
  workspace = CodeMap.find_workspace(filename)
15
14
  end
16
- @api_map = ApiMap.new(workspace)
17
- unless workspace.nil?
18
- files = Dir[File.join workspace, '**', '*.rb']
19
- files.each { |f|
20
- unless filename == f
21
- @api_map.append_file f
15
+ if workspace.nil?
16
+ @api_map = ApiMap.new(workspace)
17
+ else
18
+ ser_file = File.join(workspace, '.solargraph.ser')
19
+ if File.exist?(ser_file)
20
+ begin
21
+ @api_map = Marshal.load(File.read(ser_file))
22
+ rescue Exception => e
23
+ @api_map = ApiMap.new(workspace)
22
24
  end
23
- }
25
+ else
26
+ @api_map = ApiMap.new(workspace)
27
+ end
24
28
  end
25
29
 
26
30
  @code = code.gsub(/\r/, '')
27
31
  tries = 0
28
32
  # Hide incomplete code to avoid syntax errors
29
- tmp = "#{code}\nX".gsub(/[\.@]([\s])/, '#\1').gsub(/([\A\s]?)def([\s]*?[\n\Z])/, '\1#ef\2')
33
+ tmp = "#{@code}\nX".gsub(/[\.@]([\s])/, '#\1').gsub(/([\A\s]?)def([\s]*?[\n\Z])/, '\1#ef\2')
30
34
  #tmp = code
31
35
  begin
32
36
  @node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
@@ -36,7 +40,6 @@ module Solargraph
36
40
  tries += 1
37
41
  spot = e.diagnostic.location.begin_pos
38
42
  if spot == tmp.length
39
- puts e.message
40
43
  tmp = tmp[0..-2] + '#'
41
44
  else
42
45
  tmp = tmp[0..spot] + '#' + tmp[spot+2..-1].to_s
@@ -181,20 +184,7 @@ module Solargraph
181
184
  result = @api_map.namespaces_in(ns)
182
185
  end
183
186
  elsif phrase.include?('.')
184
- # It's a method call
185
- # TODO: For now we're assuming only one period. That's obviously a bad assumption.
186
- #base = phrase[0..phrase.index('.')-1]
187
- #ns_here = namespace_at(index)
188
- #result = @api_map.get_methods(base, ns_here)
189
- #scope = parent_node_from(index, :class, :module, :def, :defs) || @node
190
- #var = find_local_variable_node(base, scope)
191
- #unless var.nil?
192
- # obj = infer(var.children[1])
193
- # result = @api_map.get_instance_methods(obj) unless obj.nil?
194
- #end
195
-
196
- # TODO: Alternate version that resolves signature
197
- result = resolve_signature_at index
187
+ result = resolve_signature_at @code[0, index].rindex('.')
198
188
  else
199
189
  current_namespace = namespace_at(index)
200
190
  parts = current_namespace.to_s.split('::')
@@ -14,7 +14,7 @@ module Solargraph
14
14
  rescue Exception => e
15
15
  STDERR.puts e
16
16
  STDERR.puts e.backtrace.join("\n")
17
- { "status" => "err", "message" => e.message }.to_json
17
+ { "status" => "err", "message" => e.message + "\n" + e.backtrace.join("\n") }.to_json
18
18
  end
19
19
  end
20
20
  end
@@ -54,6 +54,18 @@ module Solargraph
54
54
  Solargraph::Server.run!
55
55
  end
56
56
 
57
+ desc 'serialize DIRECTORY', 'Cache an API map of DIRECTORY'
58
+ def serialize directory
59
+ api_map = ApiMap.new directory
60
+ files = Dir[File.join directory, 'lib', '**', '*.rb'] + Dir[File.join directory, 'app', '**', '*.rb']
61
+ files.each { |f|
62
+ api_map.append_file f
63
+ }
64
+ File.open(File.join(directory, '.solargraph.ser'), 'wb') { |file|
65
+ file << Marshal.dump(api_map)
66
+ }
67
+ end
68
+
57
69
  desc 'suggest', 'Get code suggestions for the provided input'
58
70
  long_desc <<-LONGDESC
59
71
  Analyze a Ruby file and output a list of code suggestions in JSON format.
@@ -76,7 +88,8 @@ module Solargraph
76
88
  rescue Exception => e
77
89
  STDERR.puts e
78
90
  STDERR.puts e.backtrace.join("\n")
79
- result = { "status" => "err", "message" => e.message }.to_json
91
+ #result = { "status" => "err", "message" => e.message }.to_json
92
+ result = { "status" => "err", "message" => e.message + "\n" + e.backtrace.join("\n") }.to_json
80
93
  STDOUT.puts result
81
94
  end
82
95
  end
@@ -10,15 +10,16 @@ module Solargraph
10
10
  VARIABLE = 'Variable'
11
11
  SNIPPET = 'Snippet'
12
12
 
13
- attr_reader :label, :kind, :insert, :detail, :documentation
13
+ attr_reader :label, :kind, :insert, :detail, :documentation, :code_object, :location
14
14
 
15
- def initialize label, kind: KEYWORD, insert: nil, detail: nil, documentation: nil, code_object: nil
15
+ def initialize label, kind: KEYWORD, insert: nil, detail: nil, documentation: nil, code_object: nil, location: nil
16
16
  @label = label.to_s
17
17
  @kind = kind
18
18
  @insert = insert || @label
19
19
  @detail = detail
20
20
  @code_object = code_object
21
21
  @documentation = documentation
22
+ @location = location
22
23
  end
23
24
 
24
25
  def to_s
@@ -26,14 +27,19 @@ module Solargraph
26
27
  end
27
28
 
28
29
  def to_json args={}
29
- {
30
+ obj = {
30
31
  label: @label,
31
32
  kind: @kind,
32
33
  insert: @insert,
33
34
  detail: @detail,
34
- #documentation: (@documentation.nil? ? nil : @documentation.all)
35
- documentation: (@code_object.nil? ? nil : YARD::Templates::Engine.render(format: :text, object: @code_object))
36
- }.to_json(args)
35
+ location: (@location.nil? ? nil : @location.to_s)
36
+ }
37
+ if @code_object.nil?
38
+ obj[:documentation] = @documentation.all unless @documentation.nil?
39
+ else
40
+ obj[:documentation] = @code_object.docstring.all unless @code_object.docstring.nil?
41
+ end
42
+ obj.to_json(args)
37
43
  end
38
44
  end
39
45
 
@@ -1,3 +1,3 @@
1
1
  module Solargraph
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -10,12 +10,20 @@ module Solargraph
10
10
  wsy = File.join(workspace, '.yardoc')
11
11
  yardocs.push wsy if File.exist?(wsy)
12
12
  end
13
+ used = []
13
14
  required.each { |r|
14
- gy = YARD::Registry.yardoc_file_for_gem(r)
15
- yardocs.push gy unless gy.nil?
15
+ if workspace.nil? or !File.exist?(File.join workspace, 'lib', "#{r}.rb")
16
+ g = r.split('/').first
17
+ unless used.include?(g)
18
+ used.push g
19
+ gy = YARD::Registry.yardoc_file_for_gem(g)
20
+ yardocs.push gy unless gy.nil?
21
+ end
22
+ end
16
23
  }
17
24
  yardocs.push File.join(Dir.home, '.solargraph', 'cache', '2.0.0', 'yardoc')
18
25
  #yardocs.push File.join(Dir.home, '.solargraph', 'cache', '2.0.0', 'yardoc-stdlib')
26
+ yardocs.uniq!
19
27
  end
20
28
 
21
29
  def yardocs
@@ -38,7 +46,16 @@ module Solargraph
38
46
  end
39
47
  }
40
48
  consts.each { |c|
41
- result.push Suggestion.new(c.to_s.split('::').last, kind: Suggestion::CLASS)
49
+ detail = nil
50
+ kind = nil
51
+ if c.kind_of?(YARD::CodeObjects::ClassObject)
52
+ detail = 'Class'
53
+ kind = Suggestion::CLASS
54
+ elsif c.kind_of?(YARD::CodeObjects::ModuleObject)
55
+ detail = 'Module'
56
+ kind = Suggestion::MODULE
57
+ end
58
+ result.push Suggestion.new(c.to_s.split('::').last, detail: detail, kind: kind)
42
59
  }
43
60
  result
44
61
  end
@@ -79,7 +96,7 @@ module Solargraph
79
96
  unless ns.nil?
80
97
  ns.meths(scope: :class, visibility: [:public]).each { |m|
81
98
  n = m.to_s.split('.').last
82
- meths.push Suggestion.new("#{n}", kind: Suggestion::METHOD) if n.to_s.match(/^[a-z]/i)
99
+ meths.push Suggestion.new("#{n}", kind: Suggestion::METHOD, detail: "#{ns}") if n.to_s.match(/^[a-z]/i)
83
100
  }
84
101
  if ns.kind_of?(YARD::CodeObjects::ClassObject) and namespace != 'Class'
85
102
  meths += get_instance_methods('Class')
@@ -104,7 +121,9 @@ module Solargraph
104
121
  unless ns.nil?
105
122
  ns.meths(scope: :instance, visibility: [:public]).each { |m|
106
123
  n = m.to_s.split('#').last
107
- meths.push Suggestion.new("#{n}", kind: Suggestion::METHOD, documentation: m.docstring, code_object: m) if n.to_s.match(/^[a-z]/i) and !m.to_s.start_with?('Kernel#') and !m.docstring.to_s.include?(':nodoc:')
124
+ if n.to_s.match(/^[a-z]/i) and !m.to_s.start_with?('Kernel#') and !m.docstring.to_s.include?(':nodoc:')
125
+ meths.push Suggestion.new("#{n}", kind: Suggestion::METHOD, documentation: m.docstring, code_object: m, detail: "#{ns}", location: "#{m.file}:#{m.line}")
126
+ end
108
127
  }
109
128
  if ns.kind_of?(YARD::CodeObjects::ClassObject) and namespace != 'Object'
110
129
  meths += get_instance_methods('Object')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-18 00:00:00.000000000 Z
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser