solargraph 0.40.2 → 0.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) 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 +2 -2
  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/host/diagnoser.rb +9 -1
  15. data/lib/solargraph/language_server/message/completion_item/resolve.rb +1 -0
  16. data/lib/solargraph/language_server/message/extended/environment.rb +3 -3
  17. data/lib/solargraph/language_server/message/initialize.rb +30 -35
  18. data/lib/solargraph/language_server/message/text_document/formatting.rb +68 -18
  19. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  20. data/lib/solargraph/library.rb +94 -21
  21. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -1
  22. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +11 -12
  23. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +1 -6
  24. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +1 -1
  25. data/lib/solargraph/source.rb +1 -1
  26. data/lib/solargraph/source/chain/head.rb +0 -16
  27. data/lib/solargraph/source/source_chainer.rb +1 -0
  28. data/lib/solargraph/source_map/mapper.rb +0 -5
  29. data/lib/solargraph/type_checker.rb +25 -22
  30. data/lib/solargraph/type_checker/checks.rb +9 -5
  31. data/lib/solargraph/type_checker/rules.rb +5 -1
  32. data/lib/solargraph/version.rb +1 -1
  33. data/lib/solargraph/workspace/config.rb +19 -3
  34. data/lib/solargraph/yard_map/core_fills.rb +1 -0
  35. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70d201ad10b41bf9e0322370c2de7fa8c0efa25d8746b5b7cb3d1adc81902721
4
- data.tar.gz: 7547665a359af045b4c9d1a0991d4dec4b37c621e7baf15b20656228ce69fa1f
3
+ metadata.gz: e4533d6cac061c01e292b70313f68a9de816c8af4ca6d34ddf7bde467d0428b0
4
+ data.tar.gz: 1fd4959f4e1652c8725fcc79f5da92df9f91a90eb5462879f87c30c0c8f2ada2
5
5
  SHA512:
6
- metadata.gz: 4f79d086cf827d4bfa0a1c24c002a7487e742a0de65e5094126c9cb0dd12ca43aa08ec7e62debf074577adf81b8cd0d7e8916ee90676a3d2a5f0e8bfa6e9296c
7
- data.tar.gz: 15afa63bb7bf32dfa51dff97432c8a8070d6521a364097b4f399e5a5962b25f9d86d336e26aad3810c66de01ba5ec273224030628d56b6640db60f10501cc578
6
+ metadata.gz: 48b7f57a7b84140cb4ff9b1f2428fb1dfe2c814ef9fca26585bec17ea61a122a8755ab60e8da52969942c5ab674501332f6fc0655fcceb77606f1ab353899a4b
7
+ data.tar.gz: cd817d200a9579e66f612a100b17096fb56eb468c356de4c75d7c5d0f5621f6e734596f9babe809398f26bd2bcc2226bf424215393fe03626adf03f856eb24b9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## 0.41.2 - June 9, 2021
2
+ - Rescue InvalidOffset in async diagnosis
3
+ - Remove erroneous escaping from Hover
4
+
5
+ ## 0.41.1 - May 31, 2021
6
+ - ApiMap handles required bundles (#443)
7
+
8
+ ## 0.41.0 - May 30, 2021
9
+ - Chain constant at last double colon with more than two nested namespaces
10
+ - Fill Integer#times return type (#440)
11
+ - Validate included modules in type checks (#424)
12
+ - Faster language server initialization
13
+ - Server response to initialize is near immediate
14
+ - Workspace is mapped in a background thread
15
+ - Supported clients report mapping progress
16
+ - Log RuboCop corrections at info level (#426)
17
+ - Allow configuring the version of RuboCop to require (#430)
18
+ - Fix source of diagnostic (#434)
19
+ - Fix file argument in RuboCop (#435)
20
+ - Config ignores directories with .rb extension (#423)
21
+
22
+ ## 0.40.4 - March 3, 2021
23
+ - Fix optarg and blockarg ordering
24
+ - Override specialization for #initialize
25
+ - Find definitions with cursor after double colon
26
+
27
+ ## 0.40.3 - February 7, 2021
28
+ - Simplify and allow to configure rubocop formatter (#403)
29
+ - Type checker shows tag in param type errors (#398)
30
+ - Handle bare private_constant (#408)
31
+ - Type checker handles splatted variables (#396)
32
+
1
33
  ## 0.40.2 - January 18, 2021
2
34
  - Type checker ignores splatted calls in arity (#396)
3
35
  - Allow Parser 3.0 (#400)
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,7 +1,7 @@
1
1
  Layout/EndOfLine:
2
2
  EnforcedStyle: lf
3
- # Style/MethodDefParentheses:
4
- # Enabled: false
3
+ Style/MethodDefParentheses:
4
+ Enabled: false
5
5
  Layout/EmptyLineAfterGuardClause:
6
6
  Enabled: false
7
7
  Layout/SpaceAroundMethodCallOperator:
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
  #