solargraph 0.40.1 → 0.41.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +15 -0
  4. data/SPONSORS.md +1 -0
  5. data/lib/.rubocop.yml +1 -1
  6. data/lib/solargraph.rb +8 -7
  7. data/lib/solargraph/api_map.rb +52 -75
  8. data/lib/solargraph/api_map/store.rb +5 -0
  9. data/lib/solargraph/bench.rb +19 -18
  10. data/lib/solargraph/compat.rb +15 -1
  11. data/lib/solargraph/diagnostics/rubocop.rb +10 -2
  12. data/lib/solargraph/diagnostics/rubocop_helpers.rb +19 -20
  13. data/lib/solargraph/language_server/host.rb +74 -1
  14. data/lib/solargraph/language_server/message/completion_item/resolve.rb +1 -0
  15. data/lib/solargraph/language_server/message/extended/environment.rb +3 -3
  16. data/lib/solargraph/language_server/message/initialize.rb +30 -35
  17. data/lib/solargraph/language_server/message/text_document/formatting.rb +68 -18
  18. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  19. data/lib/solargraph/library.rb +94 -21
  20. data/lib/solargraph/parser/legacy/node_methods.rb +4 -0
  21. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -1
  22. data/lib/solargraph/parser/rubyvm/node_methods.rb +9 -2
  23. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +11 -12
  24. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +1 -6
  25. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +1 -1
  26. data/lib/solargraph/source.rb +1 -1
  27. data/lib/solargraph/source/chain/head.rb +0 -16
  28. data/lib/solargraph/source/source_chainer.rb +1 -0
  29. data/lib/solargraph/source_map/mapper.rb +0 -5
  30. data/lib/solargraph/type_checker.rb +49 -39
  31. data/lib/solargraph/type_checker/checks.rb +9 -5
  32. data/lib/solargraph/type_checker/rules.rb +5 -1
  33. data/lib/solargraph/version.rb +1 -1
  34. data/lib/solargraph/workspace/config.rb +19 -3
  35. data/lib/solargraph/yard_map/core_fills.rb +1 -0
  36. data/solargraph.gemspec +1 -1
  37. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e02cce6c25457bb0284d221d1a9bef97730b7d7be18129cdee0dcfa1aae9c02b
4
- data.tar.gz: 5143879ae0f89fd7ca608646cfe96fccc1e6c15afd7940fc7948dff358f286f3
3
+ metadata.gz: 821bc3321cdaf8fd3a707fbf5c04545fadde37dbb4ce177c0f64cfc2330dd2f2
4
+ data.tar.gz: 29bfda592a11968558178117ac849e1dbc7bf02c5685740a79682e911cadadf4
5
5
  SHA512:
6
- metadata.gz: 7c868fd6c6bb344f532eeee02ef06f9b80df086b51a49e408f5c04961303524f36d6b27cfae37b200a92bf0b65e15a3e68be029aad66bf912dd1aa73b1f48714
7
- data.tar.gz: 6a9cbcd839ab51d1c71d83a30a73464f337faffda715059e91de57a3519783793574901f23d738c83a49d1116d41268c4992fa304c08d8892e394e12bb5011c8
6
+ metadata.gz: 0c1c06c2996704646df0c140b47e5109bfd6f70ba7504e7021fec26c1b6c8416cb099f4d23654b06959ccf268f2d213f65c2d586a2aae7b3ba6e6479d1f83bfb
7
+ data.tar.gz: '09f816e9bc2d8aed6e95f414e9859a32996f8b989d41e06519afdd2be72cffe6409c9502e9a57594c9d587fda187a2b218b0bdd46027a0d11e044d4320284842'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## 0.41.1 - May 31, 2021
2
+ - ApiMap handles required bundles (#443)
3
+
4
+ ## 0.41.0 - May 30, 2021
5
+ - Chain constant at last double colon with more than two nested namespaces
6
+ - Fill Integer#times return type (#440)
7
+ - Validate included modules in type checks (#424)
8
+ - Faster language server initialization
9
+ - Server response to initialize is near immediate
10
+ - Workspace is mapped in a background thread
11
+ - Supported clients report mapping progress
12
+ - Log RuboCop corrections at info level (#426)
13
+ - Allow configuring the version of RuboCop to require (#430)
14
+ - Fix source of diagnostic (#434)
15
+ - Fix file argument in RuboCop (#435)
16
+ - Config ignores directories with .rb extension (#423)
17
+
18
+ ## 0.40.4 - March 3, 2021
19
+ - Fix optarg and blockarg ordering
20
+ - Override specialization for #initialize
21
+ - Find definitions with cursor after double colon
22
+
23
+ ## 0.40.3 - February 7, 2021
24
+ - Simplify and allow to configure rubocop formatter (#403)
25
+ - Type checker shows tag in param type errors (#398)
26
+ - Handle bare private_constant (#408)
27
+ - Type checker handles splatted variables (#396)
28
+
29
+ ## 0.40.2 - January 18, 2021
30
+ - Type checker ignores splatted calls in arity (#396)
31
+ - Allow Parser 3.0 (#400)
32
+
1
33
  ## 0.40.1 - December 28, 2020
2
34
  - Use temp directory for RuboCop formatting (#397)
3
35
  - NodeMethods reads splatted hashes (#396)
data/README.md CHANGED
@@ -79,6 +79,21 @@ Run `bundle install` and use `bundle exec yard gems` to generate the documentati
79
79
 
80
80
  In order to make sure you're using the correct dependencies, you can start the language server with Bundler. In VS Code, there's a `solargraph.useBundler` option. Other clients will vary, but the command you probably want to run is `bundle exec solargraph socket` or `bundle exec solargraph stdio`.
81
81
 
82
+ ### Rubocop Version
83
+
84
+ If you have multiple versions of [`rubocop`](https://rubygems.org/gems/rubocop) installed and you would like to choose a version other than the latest to use, this specific version can be configured.
85
+
86
+ In `.solargraph.yml`:
87
+
88
+ ```yaml
89
+ ---
90
+ reporters:
91
+ - rubocop:version=0.61.0 # diagnostics
92
+ formatter:
93
+ rubocop:
94
+ version: 0.61.0 # formatting
95
+ ```
96
+
82
97
  ### Integrating Other Editors
83
98
 
84
99
  The [language server protocol](https://microsoft.github.io/language-server-protocol/specification) is the recommended way for integrating Solargraph into editors and IDEs. Clients can connect using either stdio or TCP. Language client developers should refer to [https://solargraph.org/guides/language-server](https://solargraph.org/guides/language-server).
data/SPONSORS.md CHANGED
@@ -13,3 +13,4 @@ The following people and organizations provide funding or other resources. [Beco
13
13
  - Emily Strickland
14
14
  - Tom de Grunt
15
15
  - Akira Yamada
16
+ - Jared White
data/lib/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  Layout/EndOfLine:
2
- EnforcedStyle: lf
2
+ Enabled: false
3
3
  Style/MethodDefParentheses:
4
4
  Enabled: false
5
5
  Layout/EmptyLineAfterGuardClause:
data/lib/solargraph.rb CHANGED
@@ -9,13 +9,14 @@ require 'solargraph/version'
9
9
  # static analysis, and language server libraries.
10
10
  #
11
11
  module Solargraph
12
- class InvalidOffsetError < RangeError; end
13
- class DiagnosticsError < RuntimeError; end
14
- class FileNotFoundError < RuntimeError; end
15
- class SourceNotAvailableError < StandardError; end
16
- class ComplexTypeError < StandardError; end
17
- class WorkspaceTooLargeError < RuntimeError; end
18
- class BundleNotFoundError < StandardError; end
12
+ class InvalidOffsetError < RangeError; end
13
+ class DiagnosticsError < RuntimeError; end
14
+ class FileNotFoundError < RuntimeError; end
15
+ class SourceNotAvailableError < StandardError; end
16
+ class ComplexTypeError < StandardError; end
17
+ class WorkspaceTooLargeError < RuntimeError; end
18
+ class BundleNotFoundError < StandardError; end
19
+ class InvalidRubocopVersionError < RuntimeError; end
19
20
 
20
21
  autoload :Position, 'solargraph/position'
21
22
  autoload :Range, 'solargraph/range'
@@ -33,7 +33,12 @@ module Solargraph
33
33
  # @param pins [Array<Pin::Base>]
34
34
  # @return [self]
35
35
  def index pins
36
- catalog Bench.new(pins: pins)
36
+ # @todo This implementation is incomplete. It should probably create a
37
+ # Bench.
38
+ @source_map_hash = {}
39
+ implicit.clear
40
+ cache.clear
41
+ @store = Store.new(yard_map.pins + pins)
37
42
  self
38
43
  end
39
44
 
@@ -42,97 +47,55 @@ module Solargraph
42
47
  # @param source [Source]
43
48
  # @return [self]
44
49
  def map source
45
- catalog Bench.new(opened: [source])
50
+ map = Solargraph::SourceMap.map(source)
51
+ catalog Bench.new(source_maps: [map])
46
52
  self
47
53
  end
48
54
 
49
- # @param name [String]
50
- # @return [YARD::Tags::MacroDirective, nil]
51
- def named_macro name
52
- store.named_macros[name]
53
- end
54
-
55
55
  # Catalog a bench.
56
56
  #
57
57
  # @param bench [Bench]
58
- # @return [self]
59
58
  def catalog bench
60
- new_map_hash = {}
61
- # Bench always needs to be merged if it adds or removes sources
62
- merged = (bench.sources.length == source_map_hash.values.length)
63
- bench.sources.each do |source|
64
- if source_map_hash.key?(source.filename)
65
- if source_map_hash[source.filename].code == source.code &&
66
- source_map_hash[source.filename].source.synchronized? &&
67
- source.synchronized?
68
- new_map_hash[source.filename] = source_map_hash[source.filename]
69
- elsif !source.synchronized?
70
- new_map_hash[source.filename] = source_map_hash[source.filename]
71
- # @todo Smelly instance variable access
72
- new_map_hash[source.filename].instance_variable_set(:@source, source)
73
- else
74
- map = Solargraph::SourceMap.map(source)
75
- if source_map_hash[source.filename].try_merge!(map)
76
- new_map_hash[source.filename] = source_map_hash[source.filename]
77
- else
78
- new_map_hash[source.filename] = map
79
- merged = false
80
- end
81
- end
82
- else
83
- map = Solargraph::SourceMap.map(source)
84
- new_map_hash[source.filename] = map
85
- merged = false
86
- end
87
- end
88
- return self if bench.pins.empty? && @store && merged
89
59
  implicit.clear
60
+ @cache.clear
61
+ @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
90
62
  pins = []
91
- reqs = Set.new
92
- # @param map [SourceMap]
93
- new_map_hash.each_value do |map|
63
+ @required = Set.new
64
+ local_path_hash.clear
65
+ source_map_hash.each_value do |map|
94
66
  pins.concat map.pins
95
- reqs.merge map.requires.map(&:name)
96
- end
97
- pins.concat bench.pins
98
- reqs.merge bench.workspace.config.required
99
- @required = reqs
100
- bench.sources.each do |src|
101
- implicit.merge new_map_hash[src.filename].environ
67
+ @required.merge map.requires.map(&:name)
68
+ implicit.merge map.environ
102
69
  end
103
- # implicit.merge Convention.for_global(self)
104
- local_path_hash.clear
105
- unless bench.workspace.require_paths.empty?
106
- file_keys = new_map_hash.keys
107
- workspace_path = Pathname.new(bench.workspace.directory)
108
- reqs.delete_if do |r|
109
- bench.workspace.require_paths.any? do |base|
110
- pn = workspace_path.join(base, "#{r}.rb").to_s
111
- if file_keys.include? pn
112
- local_path_hash[r] = pn
113
- true
114
- else
115
- false
116
- end
117
- end
70
+ @required.merge implicit.requires
71
+ external_requires = []
72
+ @required.each do |req|
73
+ result = bench.load_paths.find do |path|
74
+ full = Pathname.new(path).join("#{req}.rb").to_s
75
+ @source_map_hash.key?(full)
76
+ end
77
+ if result
78
+ local_path_hash[req] = Pathname.new(result).join("#{req}.rb").to_s
79
+ else
80
+ external_requires.push req unless result
118
81
  end
119
82
  end
120
- reqs.merge implicit.requires
121
- br = reqs.include?('bundler/require') ? require_from_bundle(bench.workspace.directory) : {}
122
- reqs.merge br.keys
123
- yard_map.change(reqs.to_a, br, bench.workspace.gemnames)
124
- new_store = Store.new(yard_map.pins + implicit.pins + pins)
125
- @cache.clear
126
- @source_map_hash = new_map_hash
127
- @store = new_store
83
+ br = @required.include?('bundler/require') ? require_from_bundle(bench.directory) : {}
84
+ @required.merge br.keys
85
+ yard_map.change(external_requires, br, bench.gemnames)
86
+ @store = Store.new(yard_map.pins + implicit.pins + pins)
128
87
  @unresolved_requires = yard_map.unresolved_requires
129
- workspace_filenames.clear
130
- workspace_filenames.concat bench.workspace.filenames
131
88
  @rebindable_method_names = nil
132
89
  store.block_pins.each { |blk| blk.rebind(self) }
133
90
  self
134
91
  end
135
92
 
93
+ # @param name [String]
94
+ # @return [YARD::Tags::MacroDirective, nil]
95
+ def named_macro name
96
+ store.named_macros[name]
97
+ end
98
+
136
99
  def required
137
100
  @required ||= Set.new
138
101
  end
@@ -173,7 +136,10 @@ module Solargraph
173
136
  def self.load directory
174
137
  api_map = new
175
138
  workspace = Solargraph::Workspace.new(directory)
176
- api_map.catalog Bench.new(workspace: workspace)
139
+ # api_map.catalog Bench.new(workspace: workspace)
140
+ library = Library.new(workspace)
141
+ library.map!
142
+ api_map.catalog library.bench
177
143
  api_map
178
144
  end
179
145
 
@@ -531,6 +497,15 @@ module Solargraph
531
497
  @yard_map ||= YardMap.new
532
498
  end
533
499
 
500
+ # Check if the host class includes the specified module.
501
+ #
502
+ # @param host [String] The class
503
+ # @param mod [String] The module
504
+ # @return [Boolean]
505
+ def type_include?(host, mod)
506
+ store.get_includes(host).include?(mod)
507
+ end
508
+
534
509
  private
535
510
 
536
511
  # @return [Array<String>]
@@ -544,7 +519,9 @@ module Solargraph
544
519
  attr_reader :source_map_hash
545
520
 
546
521
  # @return [ApiMap::Store]
547
- attr_reader :store
522
+ def store
523
+ @store ||= Store.new
524
+ end
548
525
 
549
526
  # @return [Solargraph::ApiMap::Cache]
550
527
  attr_reader :cache
@@ -238,11 +238,16 @@ module Solargraph
238
238
  pins_by_class(Pin::Reference::Override).each do |ovr|
239
239
  pin = get_path_pins(ovr.name).first
240
240
  next if pin.nil?
241
+ new_pin = if pin.path.end_with?('#initialize')
242
+ get_path_pins(pin.path.sub(/#initialize/, '.new')).first
243
+ end
241
244
  (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag|
242
245
  pin.docstring.delete_tags tag.to_sym
246
+ new_pin.docstring.delete_tags tag.to_sym if new_pin
243
247
  end
244
248
  ovr.tags.each do |tag|
245
249
  pin.docstring.add_tag(tag)
250
+ new_pin.docstring.add_tag(tag) if new_pin
246
251
  end
247
252
  end
248
253
  end
@@ -1,30 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module Solargraph
4
- # An aggregation of a workspace and additional sources to be cataloged in an
5
- # ApiMap.
6
+ # A container of source maps and gem specs to be cataloged in an ApiMap.
6
7
  #
7
8
  class Bench
8
- # @return [Workspace]
9
- attr_reader :workspace
9
+ # @return [Set<SourceMap>]
10
+ attr_reader :source_maps
10
11
 
11
- # @return [Array<Source>]
12
- attr_reader :opened
12
+ # @return [Set<String>]
13
+ attr_reader :load_paths
13
14
 
14
- # @return [Array<Pin::Base>]
15
- attr_reader :pins
15
+ # @return [Set<String>]
16
+ attr_reader :gemnames
16
17
 
17
- # @param workspace [Workspace]
18
- # @param opened [Array<Source>]
19
- def initialize workspace: Workspace.new, opened: [], pins: []
20
- @workspace = workspace
21
- @opened = opened
22
- @pins = pins
23
- end
18
+ # @return [String]
19
+ attr_reader :directory
24
20
 
25
- # @return [Array<Source>]
26
- def sources
27
- @sources ||= (opened + workspace.sources).uniq(&:filename)
21
+ # @param source_maps [Array<SourceMap>, Set<SourceMap>]
22
+ # @param load_paths [Array<String>, Set<String>]
23
+ # @param gemnames [Array<String>, Set<String>]
24
+ def initialize source_maps: [], load_paths: [], gemnames: [], directory: ''
25
+ @source_maps = source_maps.to_set
26
+ @load_paths = load_paths.to_set
27
+ @gemnames = gemnames.to_set
28
+ @directory = directory
28
29
  end
29
30
  end
30
31
  end
@@ -1,9 +1,23 @@
1
+ unless Hash.method_defined?(:transform_keys)
2
+ class Hash
3
+ def transform_keys &block
4
+ result = {}
5
+ each_pair do |k, v|
6
+ result[block.call(k)] = v
7
+ end
8
+ result
9
+ end
10
+ end
11
+ end
12
+
1
13
  unless Hash.method_defined?(:transform_values)
2
14
  class Hash
3
15
  def transform_values &block
16
+ result = {}
4
17
  each_pair do |k, v|
5
- self[k] = block.call(v)
18
+ result[k] = block.call(v)
6
19
  end
20
+ result
7
21
  end
8
22
  end
9
23
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubocop'
4
3
  require 'stringio'
5
4
 
6
5
  module Solargraph
@@ -23,6 +22,7 @@ module Solargraph
23
22
  # @param _api_map [Solargraph::ApiMap]
24
23
  # @return [Array<Hash>]
25
24
  def diagnose source, _api_map
25
+ require_rubocop(rubocop_version)
26
26
  options, paths = generate_options(source.filename, source.code)
27
27
  store = RuboCop::ConfigStore.new
28
28
  runner = RuboCop::Runner.new(options, store)
@@ -36,6 +36,13 @@ module Solargraph
36
36
 
37
37
  private
38
38
 
39
+ # Extracts the rubocop version from _args_
40
+ #
41
+ # @return [String]
42
+ def rubocop_version
43
+ args.find { |a| a =~ /version=/ }.to_s.split('=').last
44
+ end
45
+
39
46
  # @param resp [Hash]
40
47
  # @return [Array<Hash>]
41
48
  def make_array resp
@@ -57,7 +64,8 @@ module Solargraph
57
64
  range: offense_range(off).to_hash,
58
65
  # 1 = Error, 2 = Warning, 3 = Information, 4 = Hint
59
66
  severity: SEVERITIES[off['severity']],
60
- source: off['cop_name'],
67
+ source: 'rubocop',
68
+ code: off['cop_name'],
61
69
  message: off['message'].gsub(/^#{off['cop_name']}\:/, '')
62
70
  }
63
71
  end
@@ -7,38 +7,37 @@ module Solargraph
7
7
  module RubocopHelpers
8
8
  module_function
9
9
 
10
+ # Requires a specific version of rubocop, or the latest installed version
11
+ # if _version_ is `nil`.
12
+ #
13
+ # @param version [String]
14
+ # @raise [InvalidRubocopVersionError] if _version_ is not installed
15
+ def require_rubocop(version = nil)
16
+ begin
17
+ gem_path = Gem::Specification.find_by_name('rubocop', version).full_gem_path
18
+ gem_lib_path = File.join(gem_path, 'lib')
19
+ $LOAD_PATH.unshift(gem_lib_path) unless $LOAD_PATH.include?(gem_lib_path)
20
+ rescue Gem::MissingSpecVersionError => e
21
+ raise InvalidRubocopVersionError,
22
+ "could not find '#{e.name}' (#{e.requirement}) - "\
23
+ "did find: [#{e.specs.map { |s| s.version.version }.join(', ')}]"
24
+ end
25
+ require 'rubocop'
26
+ end
27
+
10
28
  # Generate command-line options for the specified filename and code.
11
29
  #
12
30
  # @param filename [String]
13
31
  # @param code [String]
14
32
  # @return [Array(Array<String>, Array<String>)]
15
33
  def generate_options filename, code
16
- args = ['-f', 'j']
17
- rubocop_file = find_rubocop_file(filename)
18
- args.push('-c', fix_drive_letter(rubocop_file)) unless rubocop_file.nil?
19
- args.push filename
34
+ args = ['-f', 'j', filename]
20
35
  base_options = RuboCop::Options.new
21
36
  options, paths = base_options.parse(args)
22
37
  options[:stdin] = code
23
38
  [options, paths]
24
39
  end
25
40
 
26
- # Find a RuboCop configuration file in a file's directory tree.
27
- #
28
- # @param filename [String]
29
- # @return [String, nil]
30
- def find_rubocop_file filename
31
- return nil unless File.exist?(filename)
32
- filename = File.realpath(filename)
33
- dir = File.dirname(filename)
34
- until File.dirname(dir) == dir
35
- here = File.join(dir, '.rubocop.yml')
36
- return here if File.exist?(here)
37
- dir = File.dirname(dir)
38
- end
39
- nil
40
- end
41
-
42
41
  # RuboCop internally uses capitalized drive letters for Windows paths,
43
42
  # so we need to convert the paths provided to the command.
44
43
  #