From 29fbd34ba5e61f14577455be1fdb60332c880171 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 15 Jan 2026 09:49:06 -0500 Subject: [PATCH 001/206] Spec performance fixes On my machine, I got a 33% speed-up with these tweaks: before: Finished in 1 minute 15.88 seconds after: Finished in 49 seconds --- lib/solargraph/diagnostics/base.rb | 3 ++- lib/solargraph/diagnostics/type_check.rb | 4 ++-- spec/api_map_method_spec.rb | 3 +-- .../message/extended/check_gem_version_spec.rb | 17 ++++++++++------- spec/language_server/protocol_spec.rb | 4 ++++ spec/pin/base_spec.rb | 6 ++++-- spec/yard_map/mapper_spec.rb | 11 ----------- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/solargraph/diagnostics/base.rb b/lib/solargraph/diagnostics/base.rb index ff91a9062..31b9a4342 100644 --- a/lib/solargraph/diagnostics/base.rb +++ b/lib/solargraph/diagnostics/base.rb @@ -20,8 +20,9 @@ def initialize *args # # @param source [Solargraph::Source] # @param api_map [Solargraph::ApiMap] + # @param workspace [Solargraph::Workspace, nil] # @return [Array] - def diagnose source, api_map + def diagnose source, api_map, workspace: nil [] end end diff --git a/lib/solargraph/diagnostics/type_check.rb b/lib/solargraph/diagnostics/type_check.rb index 80f53eb7c..78de3d490 100644 --- a/lib/solargraph/diagnostics/type_check.rb +++ b/lib/solargraph/diagnostics/type_check.rb @@ -7,11 +7,11 @@ module Diagnostics # class TypeCheck < Base # @return [Array] - def diagnose source, api_map + def diagnose source, api_map, workspace: nil # return [] unless args.include?('always') || api_map.workspaced?(source.filename) severity = Diagnostics::Severities::ERROR level = (args.reverse.find { |a| ['normal', 'typed', 'strict', 'strong'].include?(a) }) || :normal - checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym) + checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym, workspace: workspace) checker.problems .sort { |a, b| a.location.range.start.line <=> b.location.range.start.line } .map do |problem| diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 9d4e4f553..ae3bab875 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -111,8 +111,7 @@ class B end describe '#get_method_stack' do - let(:out) { StringIO.new } - let(:api_map) { Solargraph::ApiMap.load_with_cache(Dir.pwd, out) } + let(:api_map) { Solargraph::ApiMap.load('') } context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } diff --git a/spec/language_server/message/extended/check_gem_version_spec.rb b/spec/language_server/message/extended/check_gem_version_spec.rb index 935917442..783770ddf 100644 --- a/spec/language_server/message/extended/check_gem_version_spec.rb +++ b/spec/language_server/message/extended/check_gem_version_spec.rb @@ -33,6 +33,10 @@ end it "responds to update actions" do + status = instance_double(Process::Status) + allow(status).to receive(:==).with(0).and_return(true) + allow(Open3).to receive(:capture2).with('gem update solargraph').and_return(['', status]) + host = Solargraph::LanguageServer::Host.new message = Solargraph::LanguageServer::Message::Extended::CheckGemVersion.new(host, {}, current: Gem::Version.new('0.0.1')) message.process @@ -42,13 +46,12 @@ response = data end reader.receive host.flush - expect { - action = { - "id" => response['id'], - "result" => response['params']['actions'].first - } - host.receive action - }.not_to raise_error + action = { + "id" => response['id'], + "result" => response['params']['actions'].first + } + host.receive action + expect(Open3).to have_received(:capture2).with('gem update solargraph') end it 'uses bundler' do diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index e88fb9c05..fdaf69e73 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -432,9 +432,13 @@ def bar baz end it "handles $/solargraph/documentGems" do + status = instance_double(Process::Status) + allow(status).to receive(:==).with(0).and_return(true) + allow(Open3).to receive(:capture2).with('solargraph', 'gems').and_return(['', status]) @protocol.request '$/solargraph/documentGems', {} response = @protocol.response expect(response['error']).to be_nil + expect(Open3).to have_received(:capture2).with('solargraph', 'gems') end it "handles textDocument/formatting" do diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index 1a6cfd1e8..06f558162 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -50,8 +50,10 @@ end it 'deals well with known closure combination issue' do - Solargraph::Shell.new.uncache('yard') - api_map = Solargraph::ApiMap.load_with_cache('.', $stderr) + # if this fails you might not have an rbs collection installed + api_map = Solargraph::ApiMap.load '' + bench = Solargraph::Bench.new(external_requires: ['yard']) + api_map.catalog bench pins = api_map.get_method_stack('YARD::Docstring', 'parser', scope: :class) expect(pins.length).to eq(1) parser_method_pin = pins.first diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index d45af985b..88704471b 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -32,17 +32,6 @@ expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['self']) end - it 'marks correct return type from RuboCop::Options.new' do - # Using rubocop because it's a known dependency - rubocop = Gem::Specification.find_by_name('rubocop') - Solargraph::Yardoc.cache([], rubocop) - Solargraph::Yardoc.load!(rubocop) - pins = Solargraph::YardMap::Mapper.new(YARD::Registry.all).map - pins = pins.select { |pin| pin.path == 'RuboCop::Options.new' } - expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['self']) - expect(pins.flat_map(&:signatures).map(&:return_type).uniq.map(&:to_s)).to eq(['self']) - end - it 'marks non-explicit methods' do # Using rspec-expectations because it's a known dependency rspec = Gem::Specification.find_by_name('rspec-expectations') From 70ea4bb19c23cd5b204a4b62eaa7aa916b34eee1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 15 Jan 2026 21:33:12 -0500 Subject: [PATCH 002/206] Add rbs collection in undercover run --- .github/workflows/rspec.yml | 13 ++++++++++--- spec/api_map_method_spec.rb | 1 + spec/library_spec.rb | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index c82ade49b..e21f06694 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -58,8 +58,6 @@ jobs: run: | bundle _2.5.23_ install bundle update rbs # use latest available for this Ruby version - bundle list - bundle exec solargraph pin 'Bundler::Dsl#source' - name: Update types run: | bundle exec rbs collection update @@ -77,9 +75,18 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.4' + # match version in Gemfile.lock and use same version below + bundler: 2.5.23 bundler-cache: false + - name: Set rbs version + run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile - name: Install gems - run: bundle install + run: | + bundle _2.5.23__ install + bundle update rbs # use latest available for this Ruby version + - name: Update types + run: | + bundle exec rbs collection update - name: Run tests run: bundle exec rake spec - name: Check PR coverage diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index ae3bab875..435a4e290 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -127,6 +127,7 @@ class B let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } it 'handles finding Thor.desc' do + # if this fails you may not have an rbs collection installed expect(method_stack).not_to be_empty end end diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 34de9e1f0..95c4a2366 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -32,7 +32,7 @@ end it "returns a Completion" do - library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, + library = Solargraph::Library.new(Solargraph::Workspace.new('', Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( require 'backport' From 8c049fb77f57544be7dbd74835a49aeff9fdc526 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 15 Jan 2026 21:35:17 -0500 Subject: [PATCH 003/206] Add rbs collection in undercover run --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e21f06694..ea3585988 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -82,7 +82,7 @@ jobs: run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile - name: Install gems run: | - bundle _2.5.23__ install + bundle _2.5.23_ install bundle update rbs # use latest available for this Ruby version - name: Update types run: | From e428f804885e01f2548a2cd70963d6a3b2ac373e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 15 Jan 2026 21:42:34 -0500 Subject: [PATCH 004/206] Fix copy-and-paste-o --- .github/workflows/rspec.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index ea3585988..13f1c79b5 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -78,8 +78,6 @@ jobs: # match version in Gemfile.lock and use same version below bundler: 2.5.23 bundler-cache: false - - name: Set rbs version - run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile - name: Install gems run: | bundle _2.5.23_ install From ffc25d6d08fdf488086295ad72b769a5fc401b49 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 15 Jan 2026 21:58:38 -0500 Subject: [PATCH 005/206] Perform specific caching --- spec/api_map_method_spec.rb | 6 ++++++ spec/pin/base_spec.rb | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 435a4e290..944dcaf64 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -118,6 +118,9 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do + spec = Gem::Specification.find_by_name('yaml') + api_map.cache_gem(spec) + expect(method_stack).not_to be_empty end end @@ -127,6 +130,9 @@ class B let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } it 'handles finding Thor.desc' do + spec = Gem::Specification.find_by_name('thor') + api_map.cache_gem(spec) + # if this fails you may not have an rbs collection installed expect(method_stack).not_to be_empty end diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index 06f558162..296b1e349 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -52,6 +52,10 @@ it 'deals well with known closure combination issue' do # if this fails you might not have an rbs collection installed api_map = Solargraph::ApiMap.load '' + + spec = Gem::Specification.find_by_name('yard') + api_map.cache_gem(spec) + bench = Solargraph::Bench.new(external_requires: ['yard']) api_map.catalog bench pins = api_map.get_method_stack('YARD::Docstring', 'parser', scope: :class) From f9319c6a55120630a19e1ebfbb012b14707491a3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 16 Jan 2026 19:39:01 -0500 Subject: [PATCH 006/206] Pre-release branch 2026-01-12 (#1152) * Fix merge * Adjust annotations * Fix typecheck errors * Fix merge * Fix merge * Fix some @sg-ignores * Fix merge * Merge branch 'intersection_types' into flow_sensitive_typing_2_0 * Fix RuboCop issue * Fix type issues * Fix type issues * Fix rspec.yml * Fix rspec.yml * Add @sg-ignores * Remove @sg-ignores * Merge branch 'or_support_in_flow_sensitive_typing' into union_type_enforcement * Fix spec * Fix annotations * Fix RuboCop issues * Bump RBS versions in rspec test * Fix version * Fix version matrix * Fix version matrix * Fix version matrix * Fix version matrix * Fix version matrix * Exclude another * Exclude another * Add version, fix doc * init -> config * Fix rbs-version for Ruby 4.0 in CI workflow * Clean up ruby-version entries in rspec.yml Removed deprecated ruby-version entries for RBS. * Fix RBS version for Ruby 4.0 in workflow * Fix merge * Improve signature combination Use our generated RBS signature from parameters as a key to combine method signatures from RBS/YARD pins. This is closer to what RBS does than the current technique of using the arity alone, and fixes a key degenerate case in Integer#+ revealed by updated definitions used by recently released RBS gems * Update annotations * Drop annotation * Fix RuboCop issue * Fix merge * Don't use solargraph-rspec branch * Fix merge * Debug * Add another use of stdlib dependencies in RBS * Mock additional call * Fix annotations * Update types in rspec undercover * Debug * Debug * Drop incorrect rbs collection use in spec * Update rubocop todo * Revert change * Fix RuboCop issue * Fix annotations * Fix annotations * RuboCop fix * Use "type arity" to guide signature combination * Update rubocop todo * Include return type arity in comparison * Add dodgy return type * Fix RuboCop issue * Add Ruby 4.0 jobs * Exclude another combo * Exclude another combo * Update rules to use report? * Fix merge * Fix merge * Drop dead code * Bump version to 0.59.0.dev.1 * Rename rule * Update RuboCop todo file * Update RuboCop todo file * Ratchet rubocop TODO file * Move to skip: * Mark spec as pending * Revert spec change * Drop old workaround * Fix merge * Fix typechecking issues * Revert doc * Fix spelling * Fix merge issue * Exclude the current gemspec from pins brought in from gem * Check pathname instead * Add sg-ignore * Avoid rbs pollution We were using the sig/shims directory for some internally helpful shims; unfortunately that exported them during gem installs, causing https://github.com/castwide/solargraph/issues/1144 * Test with RBS 4.0.0.dev.5 * Open up in gemspec * Fix missing spot * Typecheck using RBS prereleases * Move point of ignoring cached gems for gem projects * Fix issues resolving cgi escape functions * Be more careful marking things as stdlib * Reclassify rbs gem * Fix merge * Add sg-ignore * Fix merge * Remove outdated workaround * Fix @sg-ignore name * Restore workaround * Restore workaround * Merge branch 'flow_sensitive_typing_2_0' into 2025-01-06 * Fix method signature * Fix annotations * Add regression test and fix for issue found during future merge * Add regression test and fix for issue found during future merge * Fix merge * Fix merge * Fix merge * Fix merge * Use correct field for self type resolution Add a regression test and fix for self type resolution issue found on a future branch * Fix 'solargraph pin --references ClassName' private method call * Add error handling * Fix another location with another test case * Drop now-unneeded @sg-ignore * Don't log caching for each dependent library This causes duplicate logging on standard libraries, many of which are esoteric (e.g., "cgi-escaping"). The current method as of the 2025-01-06 branch would result in each stdlib library being cached individually. * Drop logging entirely * Fix some types based on future branch feedback * Provide Gem::Specification to outside interface * Provide Gem::Specification to outside interface * Use #to_spec * Provide Gem::Specification to outside interface * Fix typechecking error * Use consistent bundler versions * Fix type issue * Fix annotations based on future branch feedback * Add some @todos * Fix annotations * Fix annotations * Fix annotation * Add diff::lcs shim * Improve spec expectations * Add @sg-ignore * Fix rspec checks to run on all types of PRs * Fix merge * Fix merge * Fix merge --- lib/solargraph/api_map.rb | 7 +++++++ spec/api_map_method_spec.rb | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 904edff8e..e580687e7 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -760,6 +760,13 @@ def qualify_superclass fq_sub_tag store.qualify_superclass fq_sub_tag end + # @param require_path [String] + # + # @return [Array, nil] + def resolve_require require_path + workspace.resolve_require require_path + end + private # A hash of source maps with filename keys. diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index ceae4fcf8..7cbec546a 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -125,8 +125,8 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do - spec = Gem::Specification.find_by_name('yaml') - api_map.cache_gem(spec) + specs = api_map.resolve_require('yaml') + specs.each { |spec| api_map.cache_gem(spec) } expect(method_stack).not_to be_empty end @@ -137,8 +137,8 @@ class B let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } it 'handles finding Thor.desc' do - spec = Gem::Specification.find_by_name('thor') - api_map.cache_gem(spec) + specs = api_map.resolve_require('thor') + specs.each { |spec| api_map.cache_gem(spec) } # if this fails you may not have an rbs collection installed expect(method_stack).not_to be_empty From 861a899384a6b5a718bf3648d106d308496debfd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 21:19:50 -0500 Subject: [PATCH 007/206] Ensure api_map catalogs new pins --- spec/api_map_method_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 7cbec546a..40b83efb3 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -127,6 +127,7 @@ class B it 'handles the YAML gem aliased to Psych' do specs = api_map.resolve_require('yaml') specs.each { |spec| api_map.cache_gem(spec) } + api_map.catalog bench expect(method_stack).not_to be_empty end @@ -139,6 +140,7 @@ class B it 'handles finding Thor.desc' do specs = api_map.resolve_require('thor') specs.each { |spec| api_map.cache_gem(spec) } + api_map.catalog bench # if this fails you may not have an rbs collection installed expect(method_stack).not_to be_empty From 9216d730a5c7a223611181aa570f974b714dace8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 21:29:38 -0500 Subject: [PATCH 008/206] Avoid cache_doc_map_gems if possible --- spec/doc_map_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 7c541da27..0ac440c55 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -124,7 +124,9 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: nil) - doc_map_with_bundler_require.cache_doc_map_gems!(nil) + if doc_map_with_bundler_require.pins.length <= plain_doc_map.pins.length + doc_map_with_bundler_require.cache_doc_map_gems!(nil) + end expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive end end From 7741fcaf4a0f84506feffe7cf79e86bfe17d4d40 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 21:48:36 -0500 Subject: [PATCH 009/206] Avoid load_with_cache if possible --- spec/pin/method_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index c2ef40448..c4d33b0d0 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -554,7 +554,9 @@ class Foo # on type. Let's make sure we combine those with anything else # found (e.g., additions from the BigDecimal RBS collection) # without collapsing signatures - api_map = Solargraph::ApiMap.load_with_cache(Dir.pwd, nil) + api_map = Solargraph::ApiMap.load(Dir.pwd) + bench = Solargraph::Bench.new external_requires: ['bigdecimal'] + api_map.catalog(bench) method = api_map.get_method_stack('Integer', '+', scope: :instance).first expect(method.signatures.count).to be > 3 end From 80e0c94a41ccf8c4ab826c730e1220273ae91bda Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 21:59:13 -0500 Subject: [PATCH 010/206] Avoid load_with_cache if possible --- spec/rbs_map/conversions_spec.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 61771ac10..d9a0aea7a 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -95,7 +95,12 @@ def bar: () -> untyped context 'with standard loads for solargraph project' do before :all do # rubocop:disable RSpec/BeforeAfterAll - @api_map = Solargraph::ApiMap.load_with_cache('.') + @api_map = Solargraph::ApiMap.load('.') + gems = ['parser', 'ast', 'open3'] + bench = Solargraph::Bench.new(workspace: @api_map.workspace, external_requires: gems) + @api_map.catalog(bench) + @api_map.cache_all_for_doc_map! + @api_map.catalog(bench) end let(:api_map) { @api_map } @@ -149,7 +154,9 @@ class Sub < Hash[Symbol, untyped] if Gem::Version.new(RBS::VERSION) >= Gem::Version.new('3.9.1') context 'with method pin for Open3.capture2e' do it 'accepts chdir kwarg' do - api_map = Solargraph::ApiMap.load_with_cache('.', $stdout) + api_map = Solargraph::ApiMap.load('.') + bench = Solargraph::Bench.new(external_requires: ['open3']) + api_map.catalog(bench) method_pin = api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Method) && pin.path == 'Open3.capture2e' From c0596adce9f3d98fdb96c2127b0e0b52031db053 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 22:15:24 -0500 Subject: [PATCH 011/206] Use library with fewer dependencies to cache --- spec/yard_map/mapper_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index fb8378161..26ee93fbe 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -4,8 +4,8 @@ end def pins_with require - doc_map = Solargraph::DocMap.new([require], @api_map.workspace, out: nil) - doc_map.cache_doc_map_gems!(nil) + doc_map = Solargraph::DocMap.new([require], @api_map.workspace, out: $stderr) + doc_map.cache_doc_map_gems!($stderr) doc_map.pins end @@ -37,9 +37,10 @@ def pins_with require it 'marks correct return type from RuboCop::Options.new' do # Using rubocop because it's a known dependency - pins = pins_with('rubocop').select { |pin| pin.path == 'RuboCop::Options.new' } - expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['self']) - expect(pins.flat_map(&:signatures).map(&:return_type).uniq.map(&:to_s)).to eq(['self']) + all_pins = pins_with('open3') + pins = all_pins.select { |pin| pin.path == 'Open3.capture2e' } + expect(pins.map(&:return_type).uniq.map(&:to_s)).to eq(['Array(String, Process::Status)']) + expect(pins.flat_map(&:signatures).map(&:return_type).uniq.map(&:to_s)).to eq(['Array(String, Process::Status)']) end it 'marks non-explicit methods' do From 55612e78b643d36385cf2d94632808f7f4807808 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 22:26:43 -0500 Subject: [PATCH 012/206] Simulate yardoc run --- spec/yardoc_spec.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 6cd575de0..80e8d4764 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -62,9 +62,18 @@ end it 'is idempotent' do + result = instance_double(Process::Status) + allow(result).to receive(:success?).and_return(true) + allow(Open3).to receive(:capture2e).and_return([output, result]).once do + # write the complete file to simulate successful run + FileUtils.mkdir_p(gem_yardoc_path) + FileUtils.touch(File.join(gem_yardoc_path, 'complete')) + end + described_class.build_docs(gem_yardoc_path, [], gemspec) described_class.build_docs(gem_yardoc_path, [], gemspec) # second time - expect(File.exist?(File.join(gem_yardoc_path, 'complete'))).to be true + + expect(Open3).to have_received(:capture2e).once end context 'with an error from yard' do From c5d5843fb4e7ea826d3ade6d641b4c5875d5cde5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 22:27:32 -0500 Subject: [PATCH 013/206] Add bundle cache --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 5b7e991f9..bd9cba48d 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -74,7 +74,7 @@ jobs: # # match version in Gemfile.lock and use same version below bundler: 2.5.23 - bundler-cache: false + bundler-cache: true - name: Set rbs version run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile # /home/runner/.rubies/ruby-head/lib/ruby/gems/3.5.0+2/gems/rbs-3.9.4/lib/rbs.rb:11: From 9d725f3bbce08b37e14b372ef0c92258144005f6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 22:52:23 -0500 Subject: [PATCH 014/206] Add empty lines --- spec/language_server/protocol_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index fdaf69e73..1b42935b9 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -435,8 +435,10 @@ def bar baz status = instance_double(Process::Status) allow(status).to receive(:==).with(0).and_return(true) allow(Open3).to receive(:capture2).with('solargraph', 'gems').and_return(['', status]) + @protocol.request '$/solargraph/documentGems', {} response = @protocol.response + expect(response['error']).to be_nil expect(Open3).to have_received(:capture2).with('solargraph', 'gems') end From a6df0bf554476fc7196223b699ce33a299c09ac3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 23:02:42 -0500 Subject: [PATCH 015/206] Cache bundles --- .github/workflows/plugins.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 4556215f1..9835cebdb 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -57,7 +57,7 @@ jobs: ruby-version: 3.4 # keep same as typecheck.yml # See https://github.com/castwide/solargraph/actions/runs/19000135777/job/54265647107?pr=1119 rubygems: latest - bundler-cache: false + bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: yq @@ -86,7 +86,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: 3.4 # keep same as typecheck.yml - bundler-cache: false + bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: yq @@ -126,7 +126,7 @@ jobs: with: ruby-version: 3.4 rubygems: latest - bundler-cache: false + bundler-cache: true - name: Install gems run: | set -x @@ -181,7 +181,7 @@ jobs: with: # solargraph-rails supports Ruby 3.0+ ruby-version: '3.0' - bundler-cache: false + bundler-cache: true # https://github.com/apiology/solargraph/actions/runs/19400815835/job/55508092473?pr=17 rubygems: latest bundler: latest From f4ad280cd734ee3fc4cfe4b1ebd780da3c075f61 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Tue, 27 Jan 2026 23:15:29 -0500 Subject: [PATCH 016/206] Split up regression cases --- .github/workflows/plugins.yml | 86 +++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 9835cebdb..015d262a9 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - regression: + rails_and_rspec_typechecking: runs-on: ubuntu-latest steps: @@ -44,9 +44,36 @@ jobs: run: bundle exec rbs collection update - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level strong + rails_and_rspec_specs: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 # keep same as typecheck.yml + bundler-cache: true + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: yq + version: 1.0 + - name: Install gems + run: | + echo 'gem "solargraph-rails"' > .Gemfile + echo 'gem "solargraph-rspec"' >> .Gemfile + bundle install + bundle update --pre rbs + - name: Configure to use plugins + run: | + bundle exec solargraph config + yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml + yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml + - name: Install gem types + run: bundle exec rbs collection update - name: Ensure specs still run run: bundle exec rake spec - rails: + rails_typechecking: runs-on: ubuntu-latest steps: @@ -75,9 +102,36 @@ jobs: run: bundle exec rbs collection update - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level strong + rails_specs: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 # keep same as typecheck.yml + # See https://github.com/castwide/solargraph/actions/runs/19000135777/job/54265647107?pr=1119 + rubygems: latest + bundler-cache: true + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: yq + version: 1.0 + - name: Install gems + run: | + echo 'gem "solargraph-rails"' > .Gemfile + bundle install + bundle update --pre rbs + - name: Configure to use plugins + run: | + bundle exec solargraph config + yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml + - name: Install gem types + run: bundle exec rbs collection update - name: Ensure specs still run run: bundle exec rake spec - rspec: + rspec_typechecking: runs-on: ubuntu-latest steps: @@ -104,9 +158,33 @@ jobs: run: bundle exec rbs collection update - name: Ensure typechecking still works run: bundle exec solargraph typecheck --level strong + rspec_specs: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.4 # keep same as typecheck.yml + bundler-cache: true + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: yq + version: 1.0 + - name: Install gems + run: | + echo 'gem "solargraph-rspec"' >> .Gemfile + bundle install + bundle update --pre rbs + - name: Configure to use plugins + run: | + bundle exec solargraph config + yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml + - name: Install gem types + run: bundle exec rbs collection update - name: Ensure specs still run run: bundle exec rake spec - run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem runs-on: ubuntu-latest From 98851f49202cdf78d5fec671f67816a83d14f822 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 07:42:24 -0500 Subject: [PATCH 017/206] Run specs in parallel --- Rakefile | 7 ++++++- spec/spec_helper.rb | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Rakefile b/Rakefile index c83d9ab6b..ff5aa6f1b 100755 --- a/Rakefile +++ b/Rakefile @@ -36,10 +36,15 @@ task spec: %i[spec_failed undercover_no_fail full_spec] do undercover end +desc "Run RSpec tests in parallel, starting with the ones that failed last time" +task parallel_spec: %i[spec_failed undercover_no_fail full_spec] do + undercover +end + desc "Run all RSpec tests" task :full_spec do warn 'starting spec' - sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec rspec' # --profile' + sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec prspec spec/' # --profile' warn 'ending spec' # move coverage/full-new to coverage/full on success so that we # always have the last successful run's 'coverage info diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 59d107aa3..7507ad90d 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,9 +26,26 @@ c.example_status_persistence_file_path = 'rspec-examples.txt' end require 'solargraph' + # Suppress logger output in specs (if possible) -if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') - Solargraph::Logging.logger.reopen(File::NULL) +def set_logging + # execute any logging blocks to make sure they don't blow up + Solargraph::Logging.logger.sev_threshold = Logger::DEBUG + # ...but still suppress logger output in specs (if possible) + if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') + Solargraph::Logging.logger.reopen(File::NULL) + warn "Logging set to null" + end +end + +set_logging +require 'parallel_rspec' + +ParallelRSpec.configure do |config| + config.after_fork do |worker_number| + warn "ParallelRSpec worker #{worker_number} starting in pid #{Process.pid}" + set_logging + end end # @param name [String] From 5749c4242450e41aec3ca5f8b8a1a01e76fc48e2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 07:45:34 -0500 Subject: [PATCH 018/206] Add gem --- solargraph.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/solargraph.gemspec b/solargraph.gemspec index 98f524d4d..48a42ab32 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -55,6 +55,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' + s.add_development_dependency 'parallel_rspec', '~> 2.1', '>= 2.1.1' # # very specific development-time RuboCop version patterns for CI # stability - feel free to update in an isolated PR From 2888594c045dab6e13ed36a84232c55a2bf6660f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 07:49:43 -0500 Subject: [PATCH 019/206] Finish Rakefile changes --- Rakefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index ff5aa6f1b..156ca279d 100755 --- a/Rakefile +++ b/Rakefile @@ -36,11 +36,6 @@ task spec: %i[spec_failed undercover_no_fail full_spec] do undercover end -desc "Run RSpec tests in parallel, starting with the ones that failed last time" -task parallel_spec: %i[spec_failed undercover_no_fail full_spec] do - undercover -end - desc "Run all RSpec tests" task :full_spec do warn 'starting spec' @@ -92,7 +87,7 @@ desc "Re-run failed specs. Add --fail-fast in your .rspec-local file if desired task :spec_failed do # allow user to check out any persistent failures while looking for # more in the whole test suite - sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures || true' + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec prspec --only-failures || true' end desc "Run undercover and show output without failing the task if it fails" From afb3fe039d640661652f73b7ace088fd1f87e0ab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 08:12:58 -0500 Subject: [PATCH 020/206] Fix issue revealed by more aggressive setting of DEBUG log level --- lib/solargraph/complex_type/unique_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index e490bff35..7908146dc 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -189,7 +189,7 @@ def to_rbs 'bool' elsif name.downcase == 'nil' 'nil' - elsif name == GENERIC_TAG_NAME + elsif name == GENERIC_TAG_NAME && !all_params.empty? all_params.first.name elsif ['Class', 'Module'].include?(name) rbs_name From 310408e9afd2f2c539a8ca88ad66c963b16bef24 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 08:15:48 -0500 Subject: [PATCH 021/206] Use valid values for parameters in tests Fix issue revealed by more aggressive setting of debug logging --- spec/source/chain_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/source/chain_spec.rb b/spec/source/chain_spec.rb index abc8c2b05..fa9c8f719 100644 --- a/spec/source/chain_spec.rb +++ b/spec/source/chain_spec.rb @@ -1,12 +1,12 @@ describe Solargraph::Source::Chain do it "gets empty definitions for undefined links" do chain = described_class.new([Solargraph::Source::Chain::Link.new]) - expect(chain.define(nil, nil, nil)).to be_empty + expect(chain.define(nil, nil, [])).to be_empty end it "infers undefined types for undefined links" do chain = described_class.new([Solargraph::Source::Chain::Link.new]) - expect(chain.infer(nil, nil, nil)).to be_undefined + expect(chain.infer(nil, nil, [])).to be_undefined end it "calls itself undefined if any of its links are undefined" do From 750b3710d034054bbc4366d37c9823d4bdb9fa9c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 08:19:14 -0500 Subject: [PATCH 022/206] Add missing step in spec This seems to reproduce when not run in order with other specs in file --- spec/yard_map/mapper_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index d45af985b..fe7f0ad4c 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -46,6 +46,7 @@ it 'marks non-explicit methods' do # Using rspec-expectations because it's a known dependency rspec = Gem::Specification.find_by_name('rspec-expectations') + Solargraph::Yardoc.cache([], rspec) Solargraph::Yardoc.load!(rspec) pins = Solargraph::YardMap::Mapper.new(YARD::Registry.all).map pin = pins.find { |pin| pin.path == 'RSpec::Matchers#expect' } From f8da41cba5e6d378b5858545b30da7f797d627c4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 08:24:51 -0500 Subject: [PATCH 023/206] Add .envrc to set number of parallel jobs --- .envrc | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..8eda4eb1e --- /dev/null +++ b/.envrc @@ -0,0 +1,6 @@ +#!/bin/bash + +if [[ "$(uname)" == "Darwin" ]]; then + WORKERS=$(sysctl -n hw.physicalcpu) + export WORKERS +fi From d4a21e6d24176a7cc6425cad263260a76f299b1c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 08:44:36 -0500 Subject: [PATCH 024/206] Add debug logging --- spec/yard_map/mapper_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index fe7f0ad4c..6d0c8bd37 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -91,6 +91,7 @@ gemspec = Gem::Specification.find_by_name('yard') Solargraph::Yardoc.cache([], gemspec) pins = Solargraph::YardMap::Mapper.new(Solargraph::Yardoc.load!(gemspec)).map + STDERR.puts("Total pins in yard: #{pins.length}") ext = pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Extend) && pin.name == 'Enumerable' && pin.closure.path == 'YARD::Registry' end From 70f0c5fc5fc8ba4f59a6ea10911ace4b081566d7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:00:29 -0500 Subject: [PATCH 025/206] Rely on bundler installed by ruby/setup-ruby --- .github/workflows/rspec.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index c82ade49b..f3fd28d14 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -41,11 +41,11 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} - # see https://github.com/castwide/solargraph/actions/runs/19391419903/job/55485410493?pr=1119 + # Don't hard-code this, as older bundler seems to cause problems in places: # - # match version in Gemfile.lock and use same version below - bundler: 2.5.23 - bundler-cache: false + # https://github.com/castwide/solargraph/actions/runs/21480813832/job/61876436934?pr=1167 + # bundler: 2.5.23 + bundler-cache: true - name: Set rbs version run: echo "gem 'rbs', '${{ matrix.rbs-version }}'" >> .Gemfile # /home/runner/.rubies/ruby-head/lib/ruby/gems/3.5.0+2/gems/rbs-3.9.4/lib/rbs.rb:11: @@ -56,7 +56,6 @@ jobs: run: echo "gem 'tsort'" >> .Gemfile - name: Install gems run: | - bundle _2.5.23_ install bundle update rbs # use latest available for this Ruby version bundle list bundle exec solargraph pin 'Bundler::Dsl#source' From 7a05bb22e6685f48f6bc3e93d2d958abf27d5995 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:24:05 -0500 Subject: [PATCH 026/206] Swap to name that prspec triggers on --- spec/rbs_map/conversions_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index d9a0aea7a..17cca9b35 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -94,7 +94,7 @@ def bar: () -> untyped end context 'with standard loads for solargraph project' do - before :all do # rubocop:disable RSpec/BeforeAfterAll + before :context do # rubocop:disable RSpec/BeforeAfterAll @api_map = Solargraph::ApiMap.load('.') gems = ['parser', 'ast', 'open3'] bench = Solargraph::Bench.new(workspace: @api_map.workspace, external_requires: gems) From c26e6d6fd46c25cd56a0672077d689e1ec6bad1d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:31:39 -0500 Subject: [PATCH 027/206] Set PARALLEL throughout based on number of physical cores --- .github/workflows/plugins.yml | 19 ++++++++++++++++--- .github/workflows/rspec.yml | 10 ++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 015d262a9..c8f336559 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -72,7 +72,10 @@ jobs: - name: Install gem types run: bundle exec rbs collection update - name: Ensure specs still run - run: bundle exec rake spec + run: | + PARALLEL=$(nproc --all) + export PARALLEL + bundle exec rake spec rails_typechecking: runs-on: ubuntu-latest @@ -130,7 +133,10 @@ jobs: - name: Install gem types run: bundle exec rbs collection update - name: Ensure specs still run - run: bundle exec rake spec + run: | + PARALLEL=$(nproc --all) + export PARALLEL + bundle exec rake spec rspec_typechecking: runs-on: ubuntu-latest @@ -184,7 +190,10 @@ jobs: - name: Install gem types run: bundle exec rbs collection update - name: Ensure specs still run - run: bundle exec rake spec + run: | + PARALLEL=$(nproc --all) + export PARALLEL + bundle exec rake spec run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem runs-on: ubuntu-latest @@ -242,6 +251,8 @@ jobs: - name: Run specs run: | cd ../solargraph-rspec + PARALLEL=$(nproc --all) + export PARALLEL bundle exec appraisal rspec --format progress run_solargraph_rails_specs: @@ -295,6 +306,8 @@ jobs: bundle info solargraph bundle info rbs bundle info yard + PARALLEL=$(nproc --all) + export PARALLEL ALLOW_IMPROVEMENTS=true bundle exec rake spec env: MATRIX_RAILS_VERSION: "7.0" diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 5ef28bd72..730dd41e4 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -89,7 +89,10 @@ jobs: - name: Update types run: bundle exec rbs collection update - name: Run tests - run: bundle exec rake spec + run: | + PARALLEL=$(nproc --all) + export PARALLEL + bundle exec rake spec undercover: runs-on: ubuntu-latest steps: @@ -112,5 +115,8 @@ jobs: - name: Run tests run: bundle exec rake spec - name: Check PR coverage - run: bundle exec rake undercover + run: | + PARALLEL=$(nproc --all) + export PARALLEL + bundle exec rake undercover continue-on-error: true From f2b16af4cecfa3ca9f07f08d8ffad0ac00dffacc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:34:14 -0500 Subject: [PATCH 028/206] Debug --- .github/workflows/rspec.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 730dd41e4..288473b50 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -92,6 +92,7 @@ jobs: run: | PARALLEL=$(nproc --all) export PARALLEL + echo "Running tests with PARALLEL=$PARALLEL" bundle exec rake spec undercover: runs-on: ubuntu-latest From 0adb139e9cd2d6a1a860c707c6987e031fb3cddf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:50:32 -0500 Subject: [PATCH 029/206] Bump prspec version --- solargraph.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solargraph.gemspec b/solargraph.gemspec index 04cb3bc38..57eee5d3d 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -56,7 +56,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' - s.add_development_dependency 'parallel_rspec', '~> 2.1', '>= 2.1.1' + s.add_development_dependency 'parallel_rspec', '~> 3.0' # # very specific development-time RuboCop version patterns for CI # stability - feel free to update in an isolated PR From 13eda650d81d68065aeb2bfe55960c97038c356a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 09:58:15 -0500 Subject: [PATCH 030/206] Flag individual sub-contexts for parallel_rspec --- .rubocop_todo.yml | 6 ++++++ spec/rbs_map/conversions_spec.rb | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 0616ea8d2..826fd02d2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -568,6 +568,7 @@ RSpec/BeforeAfterAll: - 'spec/api_map_spec.rb' - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without @@ -592,6 +593,11 @@ RSpec/DescribeClass: RSpec/DescribedClass: Enabled: false +# This cop supports safe autocorrection (--autocorrect). +RSpec/EmptyHook: + Exclude: + - 'spec/rbs_map/conversions_spec.rb' + # This cop supports safe autocorrection (--autocorrect). RSpec/EmptyLineAfterFinalLet: Exclude: diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 17cca9b35..57526dc82 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -94,7 +94,7 @@ def bar: () -> untyped end context 'with standard loads for solargraph project' do - before :context do # rubocop:disable RSpec/BeforeAfterAll + before :context do @api_map = Solargraph::ApiMap.load('.') gems = ['parser', 'ast', 'open3'] bench = Solargraph::Bench.new(workspace: @api_map.workspace, external_requires: gems) @@ -106,6 +106,11 @@ def bar: () -> untyped let(:api_map) { @api_map } context 'with superclass pin for Parser::AST::Node' do + before :context do + # include this to flag to parallel rspec that it should not try + # to parallelize this context + end + let(:superclass_pin) do api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' @@ -120,6 +125,12 @@ def bar: () -> untyped # https://github.com/castwide/solargraph/issues/1042 context 'with Hash superclass with untyped value and alias' do + before :context do + # include this to flag to parallel rspec that it should not + # try to parallelize this context - it does not seem smart + # enough to use the above + end + let(:rbs) do <<~RBS class Sub < Hash[Symbol, untyped] From a1629b500353b6f869e2579664f7d0a0b4b9ba84 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 10:06:54 -0500 Subject: [PATCH 031/206] Try ApiMap.new --- spec/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 40b83efb3..ea9acbb26 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -118,7 +118,7 @@ class B end describe '#get_method_stack' do - let(:api_map) { Solargraph::ApiMap.load('') } + let(:api_map) { Solargraph::ApiMap.new } context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } From e46e6b39efb71d01efcdaca94d30e0eab29e25b5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 10:25:04 -0500 Subject: [PATCH 032/206] More prspec updates --- .rubocop_todo.yml | 1 + spec/api_map_spec.rb | 5 ++++- spec/language_server/host/dispatch_spec.rb | 2 +- spec/language_server/protocol_spec.rb | 2 +- spec/yard_map/mapper_spec.rb | 5 ++++- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 826fd02d2..4a68cebf2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -569,6 +569,7 @@ RSpec/BeforeAfterAll: - 'spec/language_server/host/dispatch_spec.rb' - 'spec/language_server/protocol_spec.rb' - 'spec/rbs_map/conversions_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index f76adb078..ef51d6292 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -1,7 +1,10 @@ require 'tmpdir' describe Solargraph::ApiMap do - before :all do + # before :context here disables parallel tests in prspec, which + # would be needed regardless as we are changing the working + # directory + before :context do @api_map = Solargraph::ApiMap.new end diff --git a/spec/language_server/host/dispatch_spec.rb b/spec/language_server/host/dispatch_spec.rb index 9b314c403..5e392d47c 100644 --- a/spec/language_server/host/dispatch_spec.rb +++ b/spec/language_server/host/dispatch_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::LanguageServer::Host::Dispatch do - before :all do + before :context do # @dispatch = Solargraph::LanguageServer::Host::Dispatch @dispatch = Object.new @dispatch.extend Solargraph::LanguageServer::Host::Dispatch diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 1b42935b9..a032146d0 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -35,7 +35,7 @@ def stop end describe Protocol do - before :all do + before :context do @protocol = Protocol.new(Solargraph::LanguageServer::Host.new) end diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index 6bce2fa5b..a246f73e3 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,5 +1,8 @@ describe Solargraph::YardMap::Mapper do - before :all do # rubocop:disable RSpec/BeforeAfterAll + # before :context here disables parallel tests in prspec, which + # would be needed regardless as we are changing the working + # directory + before :context do @api_map = Solargraph::ApiMap.load('.') end From dba7d293603cde88b3a1740682d22723ec73eb76 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 10:32:04 -0500 Subject: [PATCH 033/206] Fix nil violation found in tests --- lib/solargraph/source/chain.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index a1e439ffb..787d724fd 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -149,6 +149,7 @@ def infer api_map, name_pin, locals @@inference_invalidation_key = api_map.hash @@inference_cache = {} end + # @todo Missed nil violation out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible logger.debug { "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" } @@inference_cache[cache_key] = out @@ -166,7 +167,13 @@ def infer_uncached api_map, name_pin, locals end type = infer_from_definitions(pins, links.last.last_context, api_map, locals) out = maybe_nil(type) - logger.debug { "Chain#infer_uncached(links=#{self.links.map(&:desc)}, locals=#{locals.map(&:desc)}, name_pin=#{name_pin}, name_pin.closure=#{name_pin.closure.inspect}, name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" } + logger.debug do + "Chain#infer_uncached(links=#{self.links.map(&:desc)}, " \ + "locals=#{locals.map(&:desc)}, " \ + "name_pin=#{name_pin}, " \ + "name_pin.closure=#{name_pin&.closure.inspect}, " \ + "name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" + end out end From 8a4d7223c3677cf246af2ae535d037bde76bfd88 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 10:34:38 -0500 Subject: [PATCH 034/206] Debug --- spec/gem_pins_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 944afd331..134ef6e86 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -22,6 +22,7 @@ end it 'finds locations from YARD' do + expect(pin).not_to be_nil, "Expected to find pin for #{path} in #{Dir.pwd}" expect(pin.location.filename).to end_with('environment_loader.rb') end end From f8260b419cb76be97cf725efae3c221ffda68921 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 11:51:25 -0500 Subject: [PATCH 035/206] Another nil fix --- lib/solargraph/source/chain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index 787d724fd..77c260d53 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -172,7 +172,7 @@ def infer_uncached api_map, name_pin, locals "locals=#{locals.map(&:desc)}, " \ "name_pin=#{name_pin}, " \ "name_pin.closure=#{name_pin&.closure.inspect}, " \ - "name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" + "name_pin.binder=#{name_pin&.binder}) => #{out.rooted_tags.inspect}" end out end From 2dabf641d707a6ec117ee81e5867c0668f184aa1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 11:59:05 -0500 Subject: [PATCH 036/206] Avoid direct reference to current directory --- spec/api_map_method_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index ea9acbb26..fbc9cca27 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -3,7 +3,8 @@ describe 'Solargraph::ApiMap methods' do let(:api_map) { Solargraph::ApiMap.new } let(:bench) do - Solargraph::Bench.new(external_requires: external_requires, workspace: Solargraph::Workspace.new('.')) + directory = File.expand_path('..', __dir__) + Solargraph::Bench.new(external_requires: external_requires, workspace: Solargraph::Workspace.new(directory)) end let(:external_requires) { [] } @@ -118,7 +119,7 @@ class B end describe '#get_method_stack' do - let(:api_map) { Solargraph::ApiMap.new } + let(:api_map) { Solargraph::ApiMap.load '.' } context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } From 4949ce1b1e4e0c61020abf101459fcb0910c10aa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 14:41:33 -0500 Subject: [PATCH 037/206] Drop @sg-ignore --- lib/solargraph/complex_type/unique_type.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index c04d33e2c..7577c3276 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -304,7 +304,6 @@ def desc rooted_tags end - # @sg-ignore Need better if/elseanalysis # @return [String] def to_rbs if duck_type? From 51f34ce18880729a5464fcd4a5cf67cd41bc4ed3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 14:43:51 -0500 Subject: [PATCH 038/206] Add PROJECT_DIRECTORY to avoid chdir issues --- spec/api_map_method_spec.rb | 6 +++--- spec/spec_helper.rb | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index fbc9cca27..66baacf85 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -3,8 +3,8 @@ describe 'Solargraph::ApiMap methods' do let(:api_map) { Solargraph::ApiMap.new } let(:bench) do - directory = File.expand_path('..', __dir__) - Solargraph::Bench.new(external_requires: external_requires, workspace: Solargraph::Workspace.new(directory)) + Solargraph::Bench.new(external_requires: external_requires, + workspace: Solargraph::Workspace.new(PROJECT_DIRECTORY)) end let(:external_requires) { [] } @@ -119,7 +119,7 @@ class B end describe '#get_method_stack' do - let(:api_map) { Solargraph::ApiMap.load '.' } + let(:api_map) { Solargraph::ApiMap.load PROJECT_DIRECTORY } context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7507ad90d..c844e97e3 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,6 +21,8 @@ enable_coverage(:branch) if ENV['SOLARGRAPH_BRANCH_COVERAGE'] end end +PROJECT_DIRECTORY = File.expand_path('..', __dir__) + RSpec.configure do |c| # Allow use of --only-failures with rspec, handy for local development c.example_status_persistence_file_path = 'rspec-examples.txt' From dee9fb606a784c2b0f6a9b39a4117190dd82c2ea Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 14:50:22 -0500 Subject: [PATCH 039/206] Try using main api_map --- spec/api_map_method_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 66baacf85..773968f8c 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -119,18 +119,17 @@ class B end describe '#get_method_stack' do - let(:api_map) { Solargraph::ApiMap.load PROJECT_DIRECTORY } - context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do specs = api_map.resolve_require('yaml') + expect(specs).not_to be_empty specs.each { |spec| api_map.cache_gem(spec) } api_map.catalog bench - expect(method_stack).not_to be_empty + expect(method_stack).not_to be_nil end end From d671728188d670f5582833ea8455442a3102442e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 15:04:12 -0500 Subject: [PATCH 040/206] Try excluding --- spec/api_map_method_spec.rb | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 773968f8c..cca8b405b 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -119,17 +119,19 @@ class B end describe '#get_method_stack' do - context 'with stdlib that has vital dependencies' do - let(:external_requires) { ['yaml'] } - let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } - - it 'handles the YAML gem aliased to Psych' do - specs = api_map.resolve_require('yaml') - expect(specs).not_to be_empty - specs.each { |spec| api_map.cache_gem(spec) } - api_map.catalog bench - - expect(method_stack).not_to be_nil + if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3.0') + context 'with stdlib that has vital dependencies' do + let(:external_requires) { ['yaml'] } + let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } + + it 'handles the YAML gem aliased to Psych' do + specs = api_map.resolve_require('yaml') + expect(specs).not_to be_empty + specs.each { |spec| api_map.cache_gem(spec) } + api_map.catalog bench + + expect(method_stack).not_to be_nil + end end end From 0b8bbcea9cd8abb9b04fa48a5efd9e3e649a39a0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 15:10:00 -0500 Subject: [PATCH 041/206] Use bundler cache more --- .github/workflows/linting.yml | 4 ++-- .github/workflows/typecheck.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 7a5ef4d05..170f8b85a 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -86,7 +86,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: 3.4 - bundler-cache: false + bundler-cache: true - name: Install gems run: bundle install @@ -103,7 +103,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: 3.4 - bundler-cache: false + bundler-cache: true - name: Install gems run: bundle install diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index ddb3e6527..d6f1fc92f 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -28,7 +28,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: 3.4 - bundler-cache: false + bundler-cache: true - name: Install gems run: | bundle install From cb976e2f7f00f6dafffde4f809d0d5d25ada2e5a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 15:11:53 -0500 Subject: [PATCH 042/206] Include psych in compilation --- spec/api_map_method_spec.rb | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index cca8b405b..5b54726e2 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -119,19 +119,17 @@ class B end describe '#get_method_stack' do - if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3.0') - context 'with stdlib that has vital dependencies' do - let(:external_requires) { ['yaml'] } - let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } - - it 'handles the YAML gem aliased to Psych' do - specs = api_map.resolve_require('yaml') - expect(specs).not_to be_empty - specs.each { |spec| api_map.cache_gem(spec) } - api_map.catalog bench - - expect(method_stack).not_to be_nil - end + context 'with stdlib that has vital dependencies' do + let(:external_requires) { ['yaml'] } + let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } + + it 'handles the YAML gem aliased to Psych' do + specs = api_map.resolve_require('yaml') + api_map.resolve_require('psych') + expect(specs).not_to be_empty + specs.each { |spec| api_map.cache_gem(spec) } + api_map.catalog bench + + expect(method_stack).not_to be_nil end end From 9c92f57d0c51c0733f0780d3113594671b2e4ba2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 17:36:40 -0500 Subject: [PATCH 043/206] Fix nil gemspecs list --- spec/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 5b54726e2..b9308992a 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -124,7 +124,7 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do - specs = api_map.resolve_require('yaml') + api_map.resolve_require('psych') + specs = api_map.resolve_require('yaml') || [] expect(specs).not_to be_empty specs.each { |spec| api_map.cache_gem(spec) } api_map.catalog bench From d39049e1de558bacac3d9a72f8c6c4f4e69fa885 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 17:44:57 -0500 Subject: [PATCH 044/206] Debug --- spec/api_map_method_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index b9308992a..29084e1fb 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -124,6 +124,7 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do + STDERR.puts "bench.workspace.rbs_collection_path: #{bench.workspace.rbs_collection_path.inspect}" specs = api_map.resolve_require('yaml') || [] expect(specs).not_to be_empty specs.each { |spec| api_map.cache_gem(spec) } From 8dc1f38f793e9d8aa0ca1444ca4b790412aa97c4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 19:21:47 -0500 Subject: [PATCH 045/206] Debug --- spec/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 29084e1fb..e43e62837 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -125,7 +125,7 @@ class B it 'handles the YAML gem aliased to Psych' do STDERR.puts "bench.workspace.rbs_collection_path: #{bench.workspace.rbs_collection_path.inspect}" - specs = api_map.resolve_require('yaml') || [] + specs = (api_map.resolve_require('yaml') || []) + (api_map.resolve_require('psych') || []) expect(specs).not_to be_empty specs.each { |spec| api_map.cache_gem(spec) } api_map.catalog bench From cf3dfb3af0543c5ce6910dd24ec8c6450050675f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 19:33:31 -0500 Subject: [PATCH 046/206] Debug --- spec/gem_pins_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 134ef6e86..9715e2468 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -14,7 +14,7 @@ let(:requires) { ['rbs'] } it 'can merge YARD and RBS' do - expect(pin.source).to eq(:combined) + expect(pin.source).to eq(:combined), "Expected to merge YARD and RBS for #{path} in #{workspace.directory}" end it 'finds types from RBS' do From d67378ec0a293195f0599d8d10d237e5566c30d0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 19:51:53 -0500 Subject: [PATCH 047/206] Mark fixed --- .rubocop_todo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4a68cebf2..9690dd638 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -871,7 +871,6 @@ Style/GlobalStdStream: - 'lib/solargraph/logging.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' - - 'spec/logging_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. From 7edf8b89712901152afc3c9eb1de10925371252c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 20:18:19 -0500 Subject: [PATCH 048/206] More things run serially --- .rubocop_todo.yml | 13 +++---------- spec/gem_pins_spec.rb | 9 +++++++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9690dd638..1bc5a5f57 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -561,15 +561,7 @@ RSpec/BeNil: - 'spec/language_server/host_spec.rb' RSpec/BeforeAfterAll: - Exclude: - - '**/spec/spec_helper.rb' - - '**/spec/rails_helper.rb' - - '**/spec/support/**/*.rb' - - 'spec/api_map_spec.rb' - - 'spec/language_server/host/dispatch_spec.rb' - - 'spec/language_server/protocol_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - - 'spec/yard_map/mapper_spec.rb' + Enabled: false # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without @@ -597,6 +589,7 @@ RSpec/DescribedClass: # This cop supports safe autocorrection (--autocorrect). RSpec/EmptyHook: Exclude: + - 'spec/gem_pins_spec.rb' - 'spec/rbs_map/conversions_spec.rb' # This cop supports safe autocorrection (--autocorrect). @@ -1260,4 +1253,4 @@ YARD/TagTypeSyntax: # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 244 + Max: 229 diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 9715e2468..081bf30d4 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -5,6 +5,11 @@ let(:doc_map) { Solargraph::DocMap.new(requires, workspace, out: nil) } let(:pin) { doc_map.pins.find { |pin| pin.path == path } } + before :context do + # avoid race conditions building the rbs gem by letting parallel + # rspec know to run these serially in the same worker + end + before do doc_map.cache_doc_map_gems!(STDERR) # rubocop:disable Style/GlobalStdStream end @@ -22,8 +27,8 @@ end it 'finds locations from YARD' do - expect(pin).not_to be_nil, "Expected to find pin for #{path} in #{Dir.pwd}" - expect(pin.location.filename).to end_with('environment_loader.rb') + expect(pin).not_to be_nil, "Expected to find pin for #{path} in #{workspace.directory}" + expect(Pin.location.filename).to end_with('environment_loader.rb') end end From 1965e63f39871dfc583e77dc1c50450e99cf4382 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 20:22:36 -0500 Subject: [PATCH 049/206] More things run serially --- spec/gem_pins_spec.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 081bf30d4..94a22adbe 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -5,16 +5,16 @@ let(:doc_map) { Solargraph::DocMap.new(requires, workspace, out: nil) } let(:pin) { doc_map.pins.find { |pin| pin.path == path } } - before :context do - # avoid race conditions building the rbs gem by letting parallel - # rspec know to run these serially in the same worker - end - before do doc_map.cache_doc_map_gems!(STDERR) # rubocop:disable Style/GlobalStdStream end context 'with a combined method pin' do + before :context do + # avoid race conditions building the rbs gem by letting parallel + # rspec know to run these serially in the same worker + end + let(:path) { 'RBS::EnvironmentLoader#core_root' } let(:requires) { ['rbs'] } @@ -33,6 +33,11 @@ end context 'with a YARD-only pin' do + before :context do + # avoid race conditions building the rbs gem by letting parallel + # rspec know to run these serially in the same worker + end + let(:requires) { ['rake'] } let(:path) { 'Rake::Task#prerequisites' } From 0cdb4ef666e4eb80d500dc14663887beb72cc288 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 20:34:30 -0500 Subject: [PATCH 050/206] Fix typo --- spec/gem_pins_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 94a22adbe..64ec19a0b 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -28,7 +28,7 @@ it 'finds locations from YARD' do expect(pin).not_to be_nil, "Expected to find pin for #{path} in #{workspace.directory}" - expect(Pin.location.filename).to end_with('environment_loader.rb') + expect(pin.location.filename).to end_with('environment_loader.rb') end end From 0eaf53dbb0c10de79ced0e3c05dc1f8b79f1b7d1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 20:51:52 -0500 Subject: [PATCH 051/206] Deal with another filesystem conflict --- .rubocop_todo.yml | 2 ++ spec/shell_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1bc5a5f57..5f81974f9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -591,6 +591,7 @@ RSpec/EmptyHook: Exclude: - 'spec/gem_pins_spec.rb' - 'spec/rbs_map/conversions_spec.rb' + - 'spec/shell_spec.rb' # This cop supports safe autocorrection (--autocorrect). RSpec/EmptyLineAfterFinalLet: @@ -864,6 +865,7 @@ Style/GlobalStdStream: - 'lib/solargraph/logging.rb' - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' + - 'spec/api_map_method_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index aca90eb7e..7df00a0b3 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -8,6 +8,12 @@ let(:temp_dir) { Dir.mktmpdir } + + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + before do File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| file.puts "source 'https://rubygems.org'" From 12b2f30a1db15397780707396471399dbccf22af Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 21:30:25 -0500 Subject: [PATCH 052/206] Deal with another filesystem conflict --- spec/shell_spec.rb | 109 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 7df00a0b3..52301d5bb 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -4,16 +4,15 @@ require 'open3' describe Solargraph::Shell do - let(:shell) { described_class.new } - - let(:temp_dir) { Dir.mktmpdir } - - before :context do # avoid race conditions re-installing solargraph by letting parallel # rspec know to run these serially in the same worker end + let(:shell) { described_class.new } + + let(:temp_dir) { Dir.mktmpdir } + before do File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| file.puts "source 'https://rubygems.org'" @@ -38,6 +37,11 @@ def bundle_exec(*cmd) end describe '--version' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:output) { bundle_exec('solargraph', '--version') } it 'returns output' do @@ -50,6 +54,11 @@ def bundle_exec(*cmd) end describe 'uncache' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'uncaches without erroring out' do output = capture_stdout do shell.uncache('backport') @@ -68,7 +77,17 @@ def bundle_exec(*cmd) end describe 'scan' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + context 'with mocked dependencies' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:api_map) { instance_double(Solargraph::ApiMap) } before do @@ -88,7 +107,17 @@ def bundle_exec(*cmd) end describe 'typecheck' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + context 'with mocked dependencies' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:type_checker) { instance_double(Solargraph::TypeChecker) } let(:api_map) { instance_double(Solargraph::ApiMap) } @@ -110,7 +139,17 @@ def bundle_exec(*cmd) end describe 'gems' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + context 'without mocked ApiMap' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'complains when gem does not exist' do output = capture_both do shell.gems('nonexistentgem') @@ -137,6 +176,11 @@ def bundle_exec(*cmd) end context 'with mocked Workspace' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:workspace) { instance_double(Solargraph::Workspace) } let(:gemspec) { instance_double(Gem::Specification, name: 'backport') } @@ -167,6 +211,11 @@ def bundle_exec(*cmd) end describe 'cache' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'caches a stdlib gem without erroring out' do expect { shell.cache('stringio') }.not_to raise_error end @@ -174,6 +223,11 @@ def bundle_exec(*cmd) context 'when gem does not exist' do subject(:call) { shell.cache('nonexistentgem8675309') } + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'gives a good error message' do # capture stderr output expect { call }.to output(/not found/).to_stderr @@ -193,6 +247,11 @@ def bundle_exec(*cmd) end describe 'pin on a class' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:api_map) { instance_double(Solargraph::ApiMap) } let(:string_pin) { instance_double(Solargraph::Pin::Namespace, name: 'String') } @@ -204,6 +263,11 @@ def bundle_exec(*cmd) end context 'with --references option' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:object_pin) { instance_double(Solargraph::Pin::Namespace, name: 'Object') } before do @@ -223,6 +287,11 @@ def bundle_exec(*cmd) end describe 'pin on a method' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + let(:api_map) { instance_double(Solargraph::ApiMap) } let(:to_s_pin) { instance_double(Solargraph::Pin::Method, return_type: Solargraph::ComplexType.parse('String')) } @@ -233,6 +302,11 @@ def bundle_exec(*cmd) end context 'with no options' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints a pin' do allow(to_s_pin).to receive(:inspect).and_return('pin inspect result') @@ -243,6 +317,11 @@ def bundle_exec(*cmd) end context 'with --rbs option' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints a pin with RBS type' do allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') @@ -255,6 +334,11 @@ def bundle_exec(*cmd) end context 'with --stack option' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints a pin using stack results' do allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') @@ -282,6 +366,11 @@ def bundle_exec(*cmd) end context 'with --typify option' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints a pin with typify type' do allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) @@ -294,6 +383,11 @@ def bundle_exec(*cmd) end context 'with --typify --rbs options' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints a pin with typify type' do allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) @@ -306,6 +400,11 @@ def bundle_exec(*cmd) end context 'with no pin' do + before :context do + # avoid race conditions re-installing solargraph by letting parallel + # rspec know to run these serially in the same worker + end + it 'prints error' do allow(api_map).to receive(:get_path_pins).with('Not#found').and_return([]) allow(Solargraph::Pin::Method).to receive(:===).with(nil).and_return(false) From 1c5c9696a2dca7e2a7975946c7b0d5d220afaf30 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Thu, 29 Jan 2026 21:45:49 -0500 Subject: [PATCH 053/206] Set spec timeout --- solargraph.gemspec | 1 + spec/spec_helper.rb | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/solargraph.gemspec b/solargraph.gemspec index 57eee5d3d..d2ac422da 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -56,6 +56,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' s.add_development_dependency 'rspec', '~> 3.5' + s.add_development_dependency 'rspec-time-guard', '~> 0.2.0' s.add_development_dependency 'parallel_rspec', '~> 3.0' # # very specific development-time RuboCop version patterns for CI diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c844e97e3..ccf7b0897 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'bundler/setup' require 'webmock/rspec' +require 'rspec_time_guard' WebMock.disable_net_connect!(allow_localhost: true) unless ENV['SIMPLECOV_DISABLED'] # set up lcov reporting for undercover @@ -27,6 +28,11 @@ # Allow use of --only-failures with rspec, handy for local development c.example_status_persistence_file_path = 'rspec-examples.txt' end +RspecTimeGuard.setup +RspecTimeGuard.configure do |config| + config.global_time_limit_seconds = 120 + config.continue_on_timeout = false +end require 'solargraph' # Suppress logger output in specs (if possible) From bcbf9a938e1a04fc9cc0dfcaa84766bc87ec7c7f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 10:22:12 -0500 Subject: [PATCH 054/206] Avoid race conditions caching/uncaching backport --- .rubocop_todo.yml | 1 + spec/library_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5f81974f9..4c0d330eb 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -590,6 +590,7 @@ RSpec/DescribedClass: RSpec/EmptyHook: Exclude: - 'spec/gem_pins_spec.rb' + - 'spec/library_spec.rb' - 'spec/rbs_map/conversions_spec.rb' - 'spec/shell_spec.rb' diff --git a/spec/library_spec.rb b/spec/library_spec.rb index d444151c8..583512820 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -27,6 +27,10 @@ end context 'with a require from a not-yet-cached external gem' do + before :context do + # avoid race conditions caching/uncaching backport + end + before do Solargraph::Shell.new.uncache('backport') end @@ -53,6 +57,10 @@ def foo(adapter) end context 'with a require from an already-cached external gem' do + before :context do + # avoid race conditions caching/uncaching backport + end + before do Solargraph::Shell.new.gems('backport') end From 936d3b43ee2b77833f2ba60f3dcb3703ecfc9b7d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 10:39:06 -0500 Subject: [PATCH 055/206] Remove lint issue, reduce race condition --- lib/solargraph/yardoc.rb | 15 +++++++++++++++ spec/api_map_method_spec.rb | 1 - 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/yardoc.rb b/lib/solargraph/yardoc.rb index 4accf9425..57c81e6f9 100644 --- a/lib/solargraph/yardoc.rb +++ b/lib/solargraph/yardoc.rb @@ -18,6 +18,21 @@ module Yardoc def build_docs gem_yardoc_path, yard_plugins, gemspec return if docs_built?(gem_yardoc_path) + # @todo there's still a small race condition here + if processing?(gem_yardoc_path) + Solargraph.logger.info { "YARD doc build already in process for #{gemspec.name} #{gemspec.version}" } + + # Wait for up to 60 seconds for another process to finish building + timeout = 60 + start_time = Time.now + + sleep 1 until docs_built?(gem_yardoc_path) || (Time.now - start_time > timeout) + + return if docs_built?(gem_yardoc_path) + + raise "YARD doc build for #{gemspec.name} #{gemspec.version} did not finish in #{timeout} seconds" + end + unless Dir.exist? gemspec.gem_dir # Can happen in at least some (old?) RubyGems versions when we # have a gemspec describing a standard library like bundler. diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index e43e62837..7078c47d5 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -124,7 +124,6 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do - STDERR.puts "bench.workspace.rbs_collection_path: #{bench.workspace.rbs_collection_path.inspect}" specs = (api_map.resolve_require('yaml') || []) + (api_map.resolve_require('psych') || []) expect(specs).not_to be_empty specs.each { |spec| api_map.cache_gem(spec) } From 59a0bd647d7febf534a7ed2f7e11fceaabdfbe75 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 11:18:51 -0500 Subject: [PATCH 056/206] Don't use same filename in multiple parallel specs --- .../message/text_document/rename_spec.rb | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index b16110455..850a9c657 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true describe Solargraph::LanguageServer::Message::TextDocument::Rename do + let(:temp_file_url) do + "file:///#{Dir.mktmpdir}/file.rb" + end + it "renames a symbol" do host = Solargraph::LanguageServer::Host.new host.start - host.open('file:///file.rb', %( + host.open(temp_file_url, %( class Foo end foo = Foo.new @@ -15,7 +19,7 @@ class Foo 'method' => 'textDocument/rename', 'params' => { 'textDocument' => { - 'uri' => 'file:///file.rb' + 'uri' => temp_file_url }, 'position' => { 'line' => 1, @@ -25,13 +29,13 @@ class Foo } }) rename.process - expect(rename.result[:changes]['file:///file.rb'].length).to eq(2) + expect(rename.result[:changes][temp_file_url].length).to eq(2) end it "renames an argument symbol from method signature" do host = Solargraph::LanguageServer::Host.new host.start - host.open('file:///file.rb', %( + host.open(temp_file_url, %( class Example def foo(bar) bar += 1 @@ -45,7 +49,7 @@ def foo(bar) 'method' => 'textDocument/rename', 'params' => { 'textDocument' => { - 'uri' => 'file:///file.rb' + 'uri' => temp_file_url }, 'position' => { 'line' => 2, @@ -55,13 +59,13 @@ def foo(bar) } }) rename.process - expect(rename.result[:changes]['file:///file.rb'].length).to eq(3) + expect(rename.result[:changes][temp_file_url].length).to eq(3) end it "renames an argument symbol from method body" do host = Solargraph::LanguageServer::Host.new host.start - host.open('file:///file.rb', %( + host.open(temp_file_url, %( class Example def foo(bar) bar += 1 @@ -74,7 +78,7 @@ def foo(bar) 'method' => 'textDocument/rename', 'params' => { 'textDocument' => { - 'uri' => 'file:///file.rb' + 'uri' => temp_file_url }, 'position' => { 'line' => 3, @@ -84,13 +88,13 @@ def foo(bar) } }) rename.process - expect(rename.result[:changes]['file:///file.rb'].length).to eq(3) + expect(rename.result[:changes][temp_file_url].length).to eq(3) end it "renames namespace symbol with proper range" do host = Solargraph::LanguageServer::Host.new host.start - host.open('file:///file.rb', %( + host.open(temp_file_url, %( module Namespace; end class Namespace::ExampleClass end @@ -101,7 +105,7 @@ class Namespace::ExampleClass 'method' => 'textDocument/rename', 'params' => { 'textDocument' => { - 'uri' => 'file:///file.rb' + 'uri' => temp_file_url }, 'position' => { 'line' => 2, @@ -111,7 +115,7 @@ class Namespace::ExampleClass } }) rename.process - changes = rename.result[:changes]['file:///file.rb'] + changes = rename.result[:changes][temp_file_url] expect(changes.length).to eq(3) expect(changes.first[:range][:start][:character]).to eq(13) expect(changes.first[:range][:end][:character]).to eq(22) From 535f21d114825aa741c0ad857ecca0ff764df3d3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 13:29:53 -0500 Subject: [PATCH 057/206] Apply suggestion from @apiology --- spec/rbs_map/conversions_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 57526dc82..3fc801fa6 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -94,6 +94,7 @@ def bar: () -> untyped end context 'with standard loads for solargraph project' do + # Use :context here instead of :all so that parallel_rspec runs these on the same worker and we only have to cache these gems on one worker before :context do @api_map = Solargraph::ApiMap.load('.') gems = ['parser', 'ast', 'open3'] From 18183825d3cc0fc3f8b1c0a538ba099bbda10e76 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 16:48:46 -0500 Subject: [PATCH 058/206] rubocop -a --- .rubocop_todo.yml | 677 +----------------- Gemfile | 14 +- Rakefile | 276 +++---- bin/solargraph | 2 +- lib/solargraph.rb | 10 +- lib/solargraph/api_map.rb | 68 +- lib/solargraph/api_map/index.rb | 10 +- lib/solargraph/api_map/source_to_yard.rb | 15 +- lib/solargraph/api_map/store.rb | 40 +- lib/solargraph/bench.rb | 1 - lib/solargraph/complex_type.rb | 63 +- lib/solargraph/complex_type/type_methods.rb | 18 +- lib/solargraph/complex_type/unique_type.rb | 78 +- lib/solargraph/convention.rb | 4 +- lib/solargraph/convention/data_definition.rb | 2 +- .../data_definition/data_assignment_node.rb | 2 +- .../data_definition/data_definition_node.rb | 6 +- .../convention/struct_definition.rb | 6 +- .../struct_assignment_node.rb | 2 +- .../struct_definition_node.rb | 6 +- lib/solargraph/diagnostics/rubocop.rb | 18 +- lib/solargraph/diagnostics/rubocop_helpers.rb | 4 +- lib/solargraph/diagnostics/type_check.rb | 23 +- lib/solargraph/diagnostics/update_errors.rb | 2 +- lib/solargraph/doc_map.rb | 14 +- lib/solargraph/equality.rb | 6 +- lib/solargraph/gem_pins.rb | 8 +- lib/solargraph/language_server/error_codes.rb | 20 +- lib/solargraph/language_server/host.rb | 33 +- .../language_server/host/diagnoser.rb | 2 +- .../language_server/host/dispatch.rb | 3 +- .../language_server/host/message_worker.rb | 4 +- .../language_server/host/sources.rb | 2 +- .../message/client/register_capability.rb | 4 +- .../message/completion_item/resolve.rb | 12 +- .../message/extended/check_gem_version.rb | 21 +- .../message/extended/document_gems.rb | 14 +- .../message/extended/download_core.rb | 3 +- .../message/extended/search.rb | 2 +- .../language_server/message/initialize.rb | 34 +- .../message/text_document/completion.rb | 19 +- .../message/text_document/definition.rb | 3 +- .../text_document/document_highlight.rb | 3 +- .../message/text_document/formatting.rb | 12 +- .../message/text_document/hover.rb | 8 +- .../message/text_document/prepare_rename.rb | 3 +- .../message/text_document/references.rb | 3 +- .../message/text_document/rename.rb | 9 +- .../message/text_document/signature_help.rb | 4 +- .../workspace/did_change_watched_files.rb | 7 +- .../workspace/did_change_workspace_folders.rb | 2 +- .../language_server/transport/data_reader.rb | 24 +- lib/solargraph/language_server/uri_helpers.rb | 4 +- lib/solargraph/library.rb | 43 +- lib/solargraph/location.rb | 8 +- lib/solargraph/logging.rb | 2 +- lib/solargraph/parser/comment_ripper.rb | 14 +- .../parser/flow_sensitive_typing.rb | 72 +- lib/solargraph/parser/node_processor.rb | 2 +- lib/solargraph/parser/node_processor/base.rb | 8 +- .../parser/parser_gem/class_methods.rb | 8 +- .../parser/parser_gem/flawed_builder.rb | 2 +- .../parser/parser_gem/node_chainer.rb | 33 +- .../parser/parser_gem/node_methods.rb | 105 +-- .../parser_gem/node_processors/args_node.rb | 24 +- .../parser_gem/node_processors/block_node.rb | 2 +- .../parser_gem/node_processors/def_node.rb | 6 +- .../parser_gem/node_processors/defs_node.rb | 16 +- .../parser_gem/node_processors/if_node.rb | 6 +- .../parser_gem/node_processors/ivasgn_node.rb | 3 +- .../parser_gem/node_processors/opasgn_node.rb | 2 +- .../parser_gem/node_processors/sclass_node.rb | 6 +- .../parser_gem/node_processors/send_node.rb | 220 +++--- .../parser_gem/node_processors/until_node.rb | 2 +- .../parser_gem/node_processors/when_node.rb | 2 +- .../parser_gem/node_processors/while_node.rb | 2 +- lib/solargraph/pin/base.rb | 104 +-- lib/solargraph/pin/base_variable.rb | 65 +- lib/solargraph/pin/block.rb | 5 +- lib/solargraph/pin/callable.rb | 41 +- lib/solargraph/pin/closure.rb | 7 +- lib/solargraph/pin/common.rb | 7 +- lib/solargraph/pin/constant.rb | 6 +- lib/solargraph/pin/conversions.rb | 10 +- lib/solargraph/pin/delegated_method.rb | 6 +- lib/solargraph/pin/documenting.rb | 5 +- lib/solargraph/pin/local_variable.rb | 4 +- lib/solargraph/pin/method.rb | 111 +-- lib/solargraph/pin/namespace.rb | 23 +- lib/solargraph/pin/parameter.rb | 42 +- lib/solargraph/pin/proxy_type.rb | 2 + lib/solargraph/pin/reference.rb | 1 + lib/solargraph/pin/search.rb | 2 +- lib/solargraph/pin/signature.rb | 15 +- lib/solargraph/pin/symbol.rb | 1 + lib/solargraph/pin_cache.rb | 23 +- lib/solargraph/position.rb | 2 +- lib/solargraph/range.rb | 9 +- lib/solargraph/rbs_map.rb | 6 +- lib/solargraph/rbs_map/conversions.rb | 131 ++-- lib/solargraph/rbs_map/core_fills.rb | 23 +- lib/solargraph/rbs_map/stdlib_map.rb | 1 - lib/solargraph/server_methods.rb | 2 +- lib/solargraph/shell.rb | 108 +-- lib/solargraph/source.rb | 51 +- lib/solargraph/source/chain.rb | 26 +- lib/solargraph/source/chain/call.rb | 74 +- lib/solargraph/source/chain/class_variable.rb | 2 +- .../source/chain/global_variable.rb | 2 +- lib/solargraph/source/chain/if.rb | 3 +- .../source/chain/instance_variable.rb | 4 +- lib/solargraph/source/chain/link.rb | 4 +- lib/solargraph/source/chain/literal.rb | 2 +- lib/solargraph/source/chain/variable.rb | 4 +- lib/solargraph/source/chain/z_super.rb | 2 +- lib/solargraph/source/change.rb | 2 +- lib/solargraph/source/cursor.rb | 34 +- lib/solargraph/source/encoding_fixes.rb | 13 +- lib/solargraph/source/source_chainer.rb | 55 +- lib/solargraph/source_map.rb | 10 +- lib/solargraph/source_map/clip.rb | 66 +- lib/solargraph/source_map/mapper.rb | 90 +-- lib/solargraph/type_checker.rb | 175 +++-- lib/solargraph/type_checker/rules.rb | 12 +- lib/solargraph/workspace.rb | 47 +- lib/solargraph/workspace/config.rb | 19 +- lib/solargraph/workspace/gemspecs.rb | 2 +- lib/solargraph/yard_map/helpers.rb | 6 +- lib/solargraph/yard_map/mapper.rb | 12 +- lib/solargraph/yard_map/mapper/to_method.rb | 12 +- .../yard_map/mapper/to_namespace.rb | 2 +- lib/solargraph/yard_tags.rb | 4 +- solargraph.gemspec | 155 ++-- spec/api_map/cache_spec.rb | 2 +- spec/api_map/config_spec.rb | 46 +- spec/api_map/source_to_yard_spec.rb | 26 +- spec/api_map_spec.rb | 2 +- spec/complex_type_spec.rb | 39 +- spec/convention/activesupport_concern_spec.rb | 6 +- spec/convention/struct_definition_spec.rb | 18 +- spec/diagnostics/base_spec.rb | 2 +- spec/diagnostics/require_not_found_spec.rb | 4 +- spec/diagnostics/rubocop_helpers_spec.rb | 10 +- spec/diagnostics/rubocop_spec.rb | 14 +- spec/diagnostics/type_check_spec.rb | 14 +- spec/diagnostics/update_errors_spec.rb | 12 +- spec/diagnostics_spec.rb | 2 +- spec/doc_map_spec.rb | 20 +- spec/gem_pins_spec.rb | 10 - spec/language_server/host/diagnoser_spec.rb | 2 +- spec/language_server/host/dispatch_spec.rb | 8 +- .../host/message_worker_spec.rb | 4 +- spec/language_server/host_spec.rb | 136 ++-- .../message/completion_item/resolve_spec.rb | 15 +- .../extended/check_gem_version_spec.rb | 31 +- .../message/initialize_spec.rb | 116 +-- .../message/text_document/definition_spec.rb | 45 +- .../message/text_document/formatting_spec.rb | 2 +- .../message/text_document/hover_spec.rb | 40 +- .../message/text_document/rename_spec.rb | 112 +-- .../text_document/type_definition_spec.rb | 20 +- .../did_change_watched_files_spec.rb | 84 +-- spec/language_server/message_spec.rb | 4 +- spec/language_server/protocol_spec.rb | 107 ++- .../language_server/transport/adapter_spec.rb | 12 +- .../transport/data_reader_spec.rb | 8 +- spec/library_spec.rb | 93 ++- spec/logging_spec.rb | 4 +- spec/parser/flow_sensitive_typing_spec.rb | 1 - spec/parser/node_chainer_spec.rb | 22 +- spec/parser/node_methods_spec.rb | 88 +-- spec/parser/node_processor_spec.rb | 4 +- spec/parser_spec.rb | 2 +- spec/pin/base_spec.rb | 16 +- spec/pin/base_variable_spec.rb | 4 +- spec/pin/constant_spec.rb | 8 +- spec/pin/documenting_spec.rb | 4 +- spec/pin/instance_variable_spec.rb | 6 +- spec/pin/keyword_spec.rb | 2 +- spec/pin/local_variable_spec.rb | 33 +- spec/pin/method_spec.rb | 12 +- spec/pin/namespace_spec.rb | 8 +- spec/pin/symbol_spec.rb | 27 +- spec/pin_cache_spec.rb | 3 +- spec/position_spec.rb | 10 +- spec/rbs_map/conversions_spec.rb | 15 +- spec/rbs_map/core_map_spec.rb | 18 +- spec/rbs_map/stdlib_map_spec.rb | 2 +- spec/shell_spec.rb | 111 +-- spec/source/chain/array_spec.rb | 2 +- spec/source/chain/call_spec.rb | 18 +- spec/source/chain/class_variable_spec.rb | 4 +- spec/source/chain/constant_spec.rb | 2 +- spec/source/chain/global_variable_spec.rb | 2 +- spec/source/chain/head_spec.rb | 2 +- spec/source/chain/instance_variable_spec.rb | 11 +- spec/source/chain/link_spec.rb | 10 +- spec/source/chain/literal_spec.rb | 2 +- spec/source/chain/z_super_spec.rb | 2 +- spec/source/chain_spec.rb | 49 +- spec/source/change_spec.rb | 18 +- spec/source/cursor_spec.rb | 48 +- spec/source/source_chainer_spec.rb | 134 ++-- spec/source/updater_spec.rb | 6 +- spec/source_map/clip_spec.rb | 8 +- spec/source_map/mapper_spec.rb | 340 ++++----- spec/source_map_spec.rb | 18 +- spec/source_spec.rb | 68 +- spec/spec_helper.rb | 17 +- spec/type_checker/levels/alpha_spec.rb | 3 +- spec/type_checker/levels/normal_spec.rb | 2 +- spec/type_checker/levels/strict_spec.rb | 17 +- spec/type_checker/levels/strong_spec.rb | 5 +- spec/type_checker/levels/typed_spec.rb | 2 +- spec/type_checker_spec.rb | 6 +- spec/workspace/config_spec.rb | 17 +- spec/workspace_spec.rb | 47 +- spec/yard_map/mapper/to_method_spec.rb | 40 +- 218 files changed, 2814 insertions(+), 3422 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4c0d330eb..23837b6d5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,16 +6,10 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# This cop supports safe autocorrection (--autocorrect). -Gemspec/AddRuntimeDependency: - Exclude: - - 'solargraph.gemspec' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity. Gemspec/DeprecatedAttributeAssignment: Exclude: - - 'solargraph.gemspec' - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' # Configuration parameters: EnforcedStyle, AllowedGems. @@ -24,12 +18,6 @@ Gemspec/DevelopmentDependencies: Exclude: - 'solargraph.gemspec' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation. -Gemspec/OrderedDependencies: - Exclude: - - 'solargraph.gemspec' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Severity. Gemspec/RequireMFA: @@ -44,243 +32,6 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: with_first_argument, with_fixed_indentation -Layout/ArgumentAlignment: - Exclude: - - 'lib/solargraph/pin/callable.rb' - - 'spec/source/source_chainer_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleAlignWith. -# SupportedStylesAlignWith: either, start_of_block, start_of_line -Layout/BlockAlignment: - Exclude: - - 'spec/source_map/mapper_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -Layout/ClosingHeredocIndentation: - Exclude: - - 'spec/diagnostics/rubocop_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowForAlignment. -Layout/CommentIndentation: - Exclude: - - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/source_map/mapper.rb' - -# This cop supports safe autocorrection (--autocorrect). -Layout/ElseAlignment: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. -Layout/EmptyLineBetweenDefs: - Exclude: - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/pin/delegated_method.rb' - -# This cop supports safe autocorrection (--autocorrect). -Layout/EmptyLines: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines -Layout/EmptyLinesAroundModuleBody: - Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleAlignWith, Severity. -# SupportedStylesAlignWith: keyword, variable, start_of_line -Layout/EndAlignment: - Enabled: false - -# Configuration parameters: EnforcedStyle. -# SupportedStyles: native, lf, crlf -Layout/EndOfLine: - Exclude: - - 'Gemfile' - - 'Rakefile' - - 'solargraph.gemspec' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. -Layout/ExtraSpacing: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb' - - 'lib/solargraph/pin/closure.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/type_checker.rb' - - 'spec/spec_helper.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses -Layout/FirstArgumentIndentation: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/args_node.rb' - - 'spec/source/source_chainer_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Layout/FirstArrayElementIndentation: - Exclude: - - 'lib/solargraph/source.rb' - - 'spec/diagnostics/update_errors_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source/source_chainer_spec.rb' - - 'spec/source_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Layout/FirstHashElementIndentation: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. -# SupportedHashRocketStyles: key, separator, table -# SupportedColonStyles: key, separator, table -# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit -Layout/HashAlignment: - Exclude: - - 'lib/solargraph/workspace/config.rb' - -# This cop supports safe autocorrection (--autocorrect). -Layout/HeredocIndentation: - Exclude: - - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/yard_map/mapper/to_method_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Width, AllowedPatterns. -Layout/IndentationWidth: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation. -Layout/LeadingCommentSpace: - Exclude: - - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/source/chain/call.rb' - - 'lib/solargraph/source_map/clip.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: space, no_space -Layout/LineContinuationSpacing: - Exclude: - - 'lib/solargraph/diagnostics/rubocop_helpers.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: symmetrical, new_line, same_line -Layout/MultilineMethodCallBraceLayout: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'spec/source/source_chainer_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: aligned, indented, indented_relative_to_receiver -Layout/MultilineMethodCallIndentation: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, IndentationWidth. -# SupportedStyles: aligned, indented -Layout/MultilineOperationIndentation: - Exclude: - - 'lib/solargraph/language_server/host/dispatch.rb' - - 'lib/solargraph/source.rb' - -# This cop supports safe autocorrection (--autocorrect). -Layout/SpaceAfterComma: - Exclude: - - 'spec/source/cursor_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: space, no_space -Layout/SpaceAroundEqualsInParameterDefault: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals. -# SupportedStylesForExponentOperator: space, no_space -# SupportedStylesForRationalLiterals: space, no_space -Layout/SpaceAroundOperators: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceBeforeBlockBraces: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -Layout/SpaceBeforeComma: - Exclude: - - 'spec/source/cursor_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceInsideBlockBraces: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. -# SupportedStyles: space, no_space, compact -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceInsideHashLiteralBraces: - Exclude: - - 'lib/solargraph/language_server/message/extended/search.rb' - - 'lib/solargraph/language_server/message/initialize.rb' - - 'lib/solargraph/workspace/config.rb' - - 'spec/language_server/host/message_worker_spec.rb' - - 'spec/language_server/message/extended/check_gem_version_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: space, compact, no_space -Layout/SpaceInsideParens: - Exclude: - - 'lib/solargraph/pin/namespace.rb' - - 'lib/solargraph/source_map.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowInHeredoc. -Layout/TrailingWhitespace: - Exclude: - - 'lib/solargraph/language_server/message/client/register_capability.rb' - - 'spec/api_map/config_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedMethods, AllowedPatterns. -Lint/AmbiguousBlockAssociation: - Exclude: - - 'lib/solargraph/language_server/host.rb' - -# This cop supports safe autocorrection (--autocorrect). -Lint/AmbiguousOperator: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -Lint/AmbiguousOperatorPrecedence: - Exclude: - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/source.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: RequireParenthesesForMethodChains. Lint/AmbiguousRange: @@ -300,7 +51,6 @@ Lint/BinaryOperatorWithIdenticalOperands: Lint/BooleanSymbol: Exclude: - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - 'lib/solargraph/source/chain/literal.rb' # Configuration parameters: AllowedMethods. @@ -351,18 +101,6 @@ Lint/NonAtomicFileOperation: Exclude: - 'spec/diagnostics/rubocop_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -Lint/ParenthesesAsGroupedExpression: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'spec/language_server/host_spec.rb' - - 'spec/source_map/clip_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -Lint/RedundantRequireStatement: - Exclude: - - 'spec/language_server/protocol_spec.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods. # AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? @@ -372,27 +110,6 @@ Lint/RedundantSafeNavigation: - 'lib/solargraph/api_map/source_to_yard.rb' - 'lib/solargraph/rbs_map.rb' -# This cop supports safe autocorrection (--autocorrect). -Lint/RedundantStringCoercion: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/pin/conversions.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/namespace.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - -# This cop supports safe autocorrection (--autocorrect). -Lint/RedundantWithIndex: - Exclude: - - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: strict, consistent -Lint/SymbolConversion: - Exclude: - - 'lib/solargraph/pin/base.rb' - # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: Exclude: @@ -401,21 +118,12 @@ Lint/UnderscorePrefixedVariableName: # Configuration parameters: Methods. Lint/UnexpectedBlockArity: Exclude: - - 'lib/solargraph/language_server/message/completion_item/resolve.rb' - 'lib/solargraph/type_checker.rb' Lint/UnmodifiedReduceAccumulator: Exclude: - 'lib/solargraph/pin/method.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. -Lint/UnusedBlockArgument: - Exclude: - - 'lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb' - - 'lib/solargraph/logging.rb' - - 'spec/language_server/transport/data_reader_spec.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError @@ -424,7 +132,11 @@ Lint/UnusedMethodArgument: # This cop supports safe autocorrection (--autocorrect). Lint/UselessAssignment: - Enabled: false + Exclude: + - 'lib/solargraph/pin/block.rb' + - 'spec/fixtures/long_squiggly_heredoc.rb' + - 'spec/fixtures/rubocop-unused-variable-error/app.rb' + - 'spec/fixtures/unicode.rb' Lint/UselessConstantScoping: Exclude: @@ -442,7 +154,7 @@ Metrics/AbcSize: # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: - Max: 57 + Max: 61 # Configuration parameters: CountBlocks, CountModifierForms. Metrics/BlockNesting: @@ -467,7 +179,7 @@ Metrics/MethodLength: # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: - Max: 169 + Max: 167 # Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: @@ -552,16 +264,16 @@ RSpec/BeEq: - 'spec/complex_type_spec.rb' - 'spec/pin/method_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: be, be_nil -RSpec/BeNil: - Exclude: - - 'spec/api_map/source_to_yard_spec.rb' - - 'spec/language_server/host_spec.rb' - RSpec/BeforeAfterAll: - Enabled: false + Exclude: + - '**/spec/spec_helper.rb' + - '**/spec/rails_helper.rb' + - '**/spec/support/**/*.rb' + - 'spec/api_map_spec.rb' + - 'spec/language_server/host/dispatch_spec.rb' + - 'spec/language_server/protocol_spec.rb' + - 'spec/rbs_map/conversions_spec.rb' + - 'spec/yard_map/mapper_spec.rb' # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without @@ -586,36 +298,10 @@ RSpec/DescribeClass: RSpec/DescribedClass: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -RSpec/EmptyHook: - Exclude: - - 'spec/gem_pins_spec.rb' - - 'spec/library_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - - 'spec/shell_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -RSpec/EmptyLineAfterFinalLet: - Exclude: - - 'spec/workspace/config_spec.rb' - # Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. -# DisallowedExamples: works -RSpec/ExampleWording: - Exclude: - - 'spec/pin/base_spec.rb' - - 'spec/pin/method_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -RSpec/ExcessiveDocstringSpacing: - Exclude: - - 'spec/source/chain/call_spec.rb' - # This cop supports safe autocorrection (--autocorrect). RSpec/ExpectActual: Exclude: @@ -623,12 +309,6 @@ RSpec/ExpectActual: - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/source_map/mapper_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: implicit, each, example -RSpec/HookArgument: - Enabled: false - # Configuration parameters: AssignmentOnly. RSpec/InstanceVariable: Enabled: false @@ -637,11 +317,6 @@ RSpec/LeakyConstantDeclaration: Exclude: - 'spec/complex_type_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -RSpec/LetBeforeExamples: - Exclude: - - 'spec/complex_type_spec.rb' - RSpec/MissingExampleGroupArgument: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' @@ -658,14 +333,6 @@ RSpec/NestedGroups: RSpec/NoExpectationExample: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: not_to, to_not -RSpec/NotToNot: - Exclude: - - 'spec/api_map_spec.rb' - - 'spec/rbs_map/core_map_spec.rb' - RSpec/PendingWithoutReason: Exclude: - 'spec/api_map_spec.rb' @@ -702,11 +369,6 @@ RSpec/RepeatedExample: - 'spec/source_map/clip_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -RSpec/ScatteredLet: - Exclude: - - 'spec/complex_type_spec.rb' - # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Enabled: false @@ -721,16 +383,6 @@ Security/MarshalLoad: Style/AccessModifierDeclarations: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: separated, grouped -Style/AccessorGrouping: - Exclude: - - 'lib/solargraph/parser/flow_sensitive_typing.rb' - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/rbs_map.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals @@ -746,19 +398,15 @@ Style/ArgumentsForwarding: Exclude: - 'lib/solargraph/complex_type.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. -# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces -# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object -# FunctionalMethods: let, let!, subject, watch -# AllowedMethods: lambda, proc, it -Style/BlockDelimiters: - Enabled: false - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: MinBranchesCount. Style/CaseLikeIf: - Enabled: false + Exclude: + - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' + - 'lib/solargraph/pin/parameter.rb' + - 'lib/solargraph/rbs_map/conversions.rb' + - 'lib/solargraph/source/source_chainer.rb' + - 'lib/solargraph/yard_map/mapper.rb' # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules. @@ -782,11 +430,6 @@ Style/CollectionCompact: Exclude: - 'lib/solargraph/pin/constant.rb' -# This cop supports safe autocorrection (--autocorrect). -Style/ColonMethodCall: - Exclude: - - 'spec/type_checker_spec.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/CombinableLoops: Exclude: @@ -803,49 +446,20 @@ Style/ConcatArrayLiterals: Style/ConditionalAssignment: Exclude: - 'lib/solargraph/api_map/source_to_yard.rb' - - 'lib/solargraph/parser/parser_gem/node_processors/defs_node.rb' - - 'lib/solargraph/source/chain/call.rb' # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -Style/EmptyLambdaParameter: - Exclude: - - 'spec/rbs_map/core_map_spec.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - - 'lib/solargraph/language_server/message/client/register_capability.rb' - 'spec/fixtures/formattable.rb' - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: trailing_conditional, ternary -Style/EmptyStringInsideInterpolation: - Exclude: - - 'lib/solargraph/library.rb' - - 'lib/solargraph/pin/documenting.rb' - - 'lib/solargraph/shell.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/ExpandPathArguments: - Exclude: - - 'solargraph.gemspec' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowedVars, DefaultToNil. -Style/FetchEnvVar: - Exclude: - - 'spec/api_map/config_spec.rb' - - 'spec/spec_helper.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv @@ -864,14 +478,13 @@ Style/FrozenStringLiteralComment: Style/GlobalStdStream: Exclude: - 'lib/solargraph/logging.rb' - - 'lib/solargraph/pin/base.rb' - 'lib/solargraph/shell.rb' - - 'spec/api_map_method_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: - Enabled: false + Exclude: + - 'lib/solargraph/source_map/clip.rb' # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSplatArgument. @@ -887,37 +500,21 @@ Style/HashEachMethods: - 'lib/solargraph/library.rb' - 'lib/solargraph/source.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. -# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys -# SupportedShorthandSyntax: always, never, either, consistent, either_consistent -Style/HashSyntax: - Exclude: - - 'spec/source/chain/class_variable_spec.rb' - - 'spec/source/cursor_spec.rb' - - 'spec/source/source_chainer_spec.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/IdenticalConditionalBranches: Exclude: - 'lib/solargraph/library.rb' - 'lib/solargraph/type_checker.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowIfModifier. -Style/IfInsideElse: - Enabled: false - # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: call, braces -Style/LambdaCall: +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: InverseMethods, InverseBlocks. +Style/InverseMethods: Exclude: - - 'lib/solargraph/library.rb' + - 'lib/solargraph/source.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/MapIntoArray: @@ -940,69 +537,19 @@ Style/MapToSet: # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: - Enabled: false + Exclude: + - 'spec/fixtures/rdoc-lib/lib/example.rb' Style/MultilineBlockChain: Exclude: - 'lib/solargraph/pin/search.rb' -# This cop supports safe autocorrection (--autocorrect). -Style/MultilineIfModifier: - Exclude: - - 'lib/solargraph/pin/callable.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/MultilineTernaryOperator: - Exclude: - - 'lib/solargraph/language_server/host.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMethodComparison, ComparisonsThreshold. -Style/MultipleComparison: - Enabled: false - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: both, prefix, postfix -Style/NegatedIf: - Exclude: - - 'lib/solargraph/language_server/host/diagnoser.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/NegatedIfElseCondition: - Exclude: - - 'lib/solargraph/diagnostics/rubocop.rb' - - 'lib/solargraph/language_server/message/extended/document_gems.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/type_checker.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/NestedTernaryOperator: - Exclude: - - 'lib/solargraph/pin/conversions.rb' - - 'lib/solargraph/pin/method.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, MinBodyLength, AllowConsecutiveConditionals. -# SupportedStyles: skip_modifier_ifs, always -Style/Next: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/pin/signature.rb' - - 'lib/solargraph/source_map/clip.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. -Style/NumericLiterals: - MinDigits: 6 - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison @@ -1018,13 +565,6 @@ Style/OpenStructUse: Style/OptionalBooleanParameter: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. -Style/ParenthesesAroundCondition: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/send_node.rb' - - 'lib/solargraph/type_checker.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: short, verbose @@ -1038,82 +578,12 @@ Style/RedundantArgument: Exclude: - 'lib/solargraph/source_map/mapper.rb' -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantAssignment: - Exclude: - - 'lib/solargraph/language_server/host/dispatch.rb' - - 'lib/solargraph/workspace/config.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantBegin: - Exclude: - - 'lib/solargraph/language_server/transport/data_reader.rb' - - 'lib/solargraph/shell.rb' - - 'lib/solargraph/source/cursor.rb' - - 'lib/solargraph/source/encoding_fixes.rb' - - 'lib/solargraph/workspace.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantException: - Exclude: - - 'spec/language_server/host_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantFreeze: - Exclude: - - 'lib/solargraph/complex_type.rb' - - 'lib/solargraph/source_map/mapper.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/RedundantInterpolation: Exclude: - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - 'lib/solargraph/source_map/mapper.rb' -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantParentheses: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantRegexpArgument: - Exclude: - - 'lib/solargraph/api_map/index.rb' - - 'lib/solargraph/workspace/config.rb' - - 'spec/diagnostics/rubocop_helpers_spec.rb' - - 'spec/diagnostics/rubocop_spec.rb' - - 'spec/language_server/host_spec.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantRegexpEscape: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowMultipleReturnValues. -Style/RedundantReturn: - Exclude: - - 'lib/solargraph/complex_type/type_methods.rb' - - 'lib/solargraph/parser/parser_gem/node_methods.rb' - - 'lib/solargraph/source/chain/z_super.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/RedundantSelf: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowInnerSlashes. -# SupportedStyles: slashes, percent_r, mixed -Style/RegexpLiteral: - Exclude: - - 'lib/solargraph/language_server/uri_helpers.rb' - - 'lib/solargraph/workspace/config.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: implicit, explicit -Style/RescueStandardError: - Exclude: - - 'lib/solargraph/pin/base.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! @@ -1129,22 +599,6 @@ Style/SafeNavigationChainLength: Style/SlicingWithRange: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowModifier. -Style/SoleNestedConditional: - Exclude: - - 'lib/solargraph/complex_type/unique_type.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/source.rb' - - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/type_checker.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/StderrPuts: - Exclude: - - 'lib/solargraph/pin/base.rb' - - 'lib/solargraph/shell.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: @@ -1154,7 +608,8 @@ Style/StringConcatenation: # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: - Enabled: false + Exclude: + - 'spec/fixtures/rdoc-lib/rdoc-lib.gemspec' # This cop supports safe autocorrection (--autocorrect). Style/SuperArguments: @@ -1163,71 +618,12 @@ Style/SuperArguments: - 'lib/solargraph/pin/method.rb' - 'lib/solargraph/pin/signature.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, MinSize. -# SupportedStyles: percent, brackets -Style/SymbolArray: - Enabled: false - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. # AllowedMethods: define_method Style/SymbolProc: Enabled: false -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowSafeAssignment. -# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex -Style/TernaryParentheses: - Exclude: - - 'lib/solargraph/source_map/mapper.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma -Style/TrailingCommaInArguments: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma -Style/TrailingCommaInArrayLiteral: - Exclude: - - 'lib/solargraph/language_server/message/text_document/formatting.rb' - - 'lib/solargraph/rbs_map/core_fills.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma -Style/TrailingCommaInHashLiteral: - Exclude: - - 'lib/solargraph/pin/callable.rb' - - 'lib/solargraph/pin/closure.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. -# AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym -Style/TrivialAccessors: - Exclude: - - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' - -# This cop supports safe autocorrection (--autocorrect). -Style/WhileUntilModifier: - Exclude: - - 'lib/solargraph/complex_type.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, MinSize, WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - Enabled: false - -# This cop supports safe autocorrection (--autocorrect). -Style/YAMLFileRead: - Exclude: - - 'lib/solargraph/workspace/config.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Exclude: @@ -1236,13 +632,6 @@ Style/ZeroLengthPredicate: - 'lib/solargraph/source/chain/array.rb' - 'spec/language_server/protocol_spec.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: long, short -YARD/CollectionType: - Exclude: - - 'lib/solargraph/range.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. # SupportedStylesPrototypeName: before, after @@ -1256,4 +645,4 @@ YARD/TagTypeSyntax: # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 229 + Max: 215 diff --git a/Gemfile b/Gemfile index 3a5ae2b84..6849d21b5 100755 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ -source 'https://rubygems.org' - -gemspec name: 'solargraph' - -# Local gemfile for development tools, etc. -local_gemfile = File.expand_path(".Gemfile", __dir__) -instance_eval File.read local_gemfile if File.exist? local_gemfile +source 'https://rubygems.org' + +gemspec name: 'solargraph' + +# Local gemfile for development tools, etc. +local_gemfile = File.expand_path('.Gemfile', __dir__) +instance_eval File.read local_gemfile if File.exist? local_gemfile diff --git a/Rakefile b/Rakefile index 957386d09..cbb5a9da1 100755 --- a/Rakefile +++ b/Rakefile @@ -1,138 +1,138 @@ -require 'rake' -require 'bundler/gem_tasks' -require 'fileutils' -require 'open3' - -desc "Open a Pry session preloaded with this library" -task :console do - sh "pry -I lib -r solargraph.rb" -end - -desc "Run the type checker" -task typecheck: [:typecheck_strong] - -desc "Run the type checker at typed level - return code issues provable without annotations being correct" -task :typecheck_typed do - sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed" -end - -desc "Run the type checker at strict level - report issues using type annotations" -task :typecheck_strict do - sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strict" -end - -desc "Run the type checker at strong level - enforce that type annotations exist" -task :typecheck_strong do - sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strong" -end - -desc "Run the type checker at alpha level - run high-false-alarm checks" -task :typecheck_alpha do - sh "SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level alpha" -end - -desc "Run RSpec tests, starting with the ones that failed last time" -task spec: %i[spec_failed undercover_no_fail full_spec] do - undercover -end - -desc "Run all RSpec tests" -task :full_spec do - warn 'starting spec' - sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec prspec spec/' # --profile' - warn 'ending spec' - # move coverage/full-new to coverage/full on success so that we - # always have the last successful run's 'coverage info - FileUtils.rm_rf('coverage/full') - FileUtils.mv('coverage/full-new', 'coverage/full') -end - -# @sg-ignore #undercover return type could not be inferred -# @return [Process::Status] -def undercover - simplecov_collate - cmd = 'bundle exec undercover ' \ - '--simplecov coverage/combined/coverage.json ' \ - '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \ - '--compare origin/master' - output, status = Bundler.with_unbundled_env do - Open3.capture2e(cmd) - end - puts output - $stdout.flush - status -rescue StandardError => e - warn "hit error: #{e.message}" - # @sg-ignore Need to add nil check here - warn "Backtrace:\n#{e.backtrace.join("\n")}" - warn "output: #{output}" - puts "Flushing" - $stdout.flush - raise -end - -desc "Check PR coverage" -task :undercover do - raise "Undercover failed" unless undercover.success? -end - -desc "Branch-focused fast-feedback quality/spec/coverage checks" -task test: %i[overcommit spec typecheck] do - # do these in order - Rake::Task['typecheck_strict'].invoke - Rake::Task['typecheck_strong'].invoke - Rake::Task['typecheck_alpha'].invoke -end - -desc "Re-run failed specs. Add --fail-fast in your .rspec-local file if desired." -task :spec_failed do - # allow user to check out any persistent failures while looking for - # more in the whole test suite - sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec prspec --only-failures || true' -end - -desc "Run undercover and show output without failing the task if it fails" -task :undercover_no_fail do - undercover -rescue StandardError - puts "Undercover failed, but continuing with other tasks." -end - -# @return [void] -def simplecov_collate - require 'simplecov' - require 'simplecov-lcov' - require 'undercover/simplecov_formatter' - - SimpleCov.collate(Dir["coverage/{next-failure,full,ad-hoc}/.resultset.json"]) do - cname = 'combined' - command_name cname - new_dir = File.join('coverage', cname) - coverage_dir new_dir - - formatter \ - SimpleCov::Formatter::MultiFormatter - .new([ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::Undercover, - SimpleCov::Formatter::LcovFormatter - ]) - SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true - end - puts "Simplecov collated results into coverage/combined/.resultset.json" -rescue StandardError => e - puts "Simplecov collate failed: #{e.message}" -ensure - $stdout.flush -end - -desc 'Add incremental coverage for rapid iteration with undercover' -task :simplecov_collate do - simplecov_collate -end - -desc "Show quality checks on this development branch so far, including any staged files" -task :overcommit do - # OVERCOMMIT_DEBUG=1 will show more detail - sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master' -end +require 'rake' +require 'bundler/gem_tasks' +require 'fileutils' +require 'open3' + +desc 'Open a Pry session preloaded with this library' +task :console do + sh 'pry -I lib -r solargraph.rb' +end + +desc 'Run the type checker' +task typecheck: [:typecheck_strong] + +desc 'Run the type checker at typed level - return code issues provable without annotations being correct' +task :typecheck_typed do + sh 'SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed' +end + +desc 'Run the type checker at strict level - report issues using type annotations' +task :typecheck_strict do + sh 'SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strict' +end + +desc 'Run the type checker at strong level - enforce that type annotations exist' +task :typecheck_strong do + sh 'SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level strong' +end + +desc 'Run the type checker at alpha level - run high-false-alarm checks' +task :typecheck_alpha do + sh 'SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level alpha' +end + +desc 'Run RSpec tests, starting with the ones that failed last time' +task spec: %i[spec_failed undercover_no_fail full_spec] do + undercover +end + +desc 'Run all RSpec tests' +task :full_spec do + warn 'starting spec' + sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec prspec spec/' # --profile' + warn 'ending spec' + # move coverage/full-new to coverage/full on success so that we + # always have the last successful run's 'coverage info + FileUtils.rm_rf('coverage/full') + FileUtils.mv('coverage/full-new', 'coverage/full') +end + +# @sg-ignore #undercover return type could not be inferred +# @return [Process::Status] +def undercover + simplecov_collate + cmd = 'bundle exec undercover ' \ + '--simplecov coverage/combined/coverage.json ' \ + '--exclude-files "Rakefile,spec/*,spec/**/*,lib/solargraph/version.rb" ' \ + '--compare origin/master' + output, status = Bundler.with_unbundled_env do + Open3.capture2e(cmd) + end + puts output + $stdout.flush + status +rescue StandardError => e + warn "hit error: #{e.message}" + # @sg-ignore Need to add nil check here + warn "Backtrace:\n#{e.backtrace.join("\n")}" + warn "output: #{output}" + puts 'Flushing' + $stdout.flush + raise +end + +desc 'Check PR coverage' +task :undercover do + raise 'Undercover failed' unless undercover.success? +end + +desc 'Branch-focused fast-feedback quality/spec/coverage checks' +task test: %i[overcommit spec typecheck] do + # do these in order + Rake::Task['typecheck_strict'].invoke + Rake::Task['typecheck_strong'].invoke + Rake::Task['typecheck_alpha'].invoke +end + +desc 'Re-run failed specs. Add --fail-fast in your .rspec-local file if desired.' +task :spec_failed do + # allow user to check out any persistent failures while looking for + # more in the whole test suite + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec prspec --only-failures || true' +end + +desc 'Run undercover and show output without failing the task if it fails' +task :undercover_no_fail do + undercover +rescue StandardError + puts 'Undercover failed, but continuing with other tasks.' +end + +# @return [void] +def simplecov_collate + require 'simplecov' + require 'simplecov-lcov' + require 'undercover/simplecov_formatter' + + SimpleCov.collate(Dir['coverage/{next-failure,full,ad-hoc}/.resultset.json']) do + cname = 'combined' + command_name cname + new_dir = File.join('coverage', cname) + coverage_dir new_dir + + formatter \ + SimpleCov::Formatter::MultiFormatter + .new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::Undercover, + SimpleCov::Formatter::LcovFormatter + ]) + SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true + end + puts 'Simplecov collated results into coverage/combined/.resultset.json' +rescue StandardError => e + puts "Simplecov collate failed: #{e.message}" +ensure + $stdout.flush +end + +desc 'Add incremental coverage for rapid iteration with undercover' +task :simplecov_collate do + simplecov_collate +end + +desc 'Show quality checks on this development branch so far, including any staged files' +task :overcommit do + # OVERCOMMIT_DEBUG=1 will show more detail + sh 'SOLARGRAPH_ASSERTS=on bundle exec overcommit --run --diff origin/master' +end diff --git a/bin/solargraph b/bin/solargraph index 248dc42fd..8f47bbda6 100755 --- a/bin/solargraph +++ b/bin/solargraph @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # turn off warning diagnostics from Ruby -$VERBOSE=nil +$VERBOSE = nil require 'solargraph' diff --git a/lib/solargraph.rb b/lib/solargraph.rb index 27a79e4ad..544a4e2e2 100755 --- a/lib/solargraph.rb +++ b/lib/solargraph.rb @@ -71,7 +71,7 @@ def self.asserts_on? # @param msg [String, nil] An optional message to log # @param block [Proc] A block that returns a message to log # @return [void] - def self.assert_or_log(type, msg = nil, &block) + def self.assert_or_log type, msg = nil, &block if asserts_on? # @type [String, nil] msg ||= block.call @@ -113,10 +113,10 @@ def self.logger # @return [generic] def self.with_clean_env &block meth = if Bundler.respond_to?(:with_original_env) - :with_original_env - else - :with_clean_env - end + :with_original_env + else + :with_clean_env + end Bundler.send meth, &block end end diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index e580687e7..118baab5e 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -51,15 +51,15 @@ def self.reset_core out: nil # # @param other [Object] - def eql?(other) + def eql? other self.class == other.class && # @sg-ignore flow sensitive typing needs to handle self.class == other.class equality_fields == other.equality_fields end # @param other [Object] - def ==(other) - self.eql?(other) + def == other + eql?(other) end # @return [Integer] @@ -212,7 +212,7 @@ def cache_all_for_doc_map! out: $stderr, rebuild: false # @param rebuild [Boolean] # @param out [StringIO, IO, nil] # @return [void] - def cache_gem(gemspec, rebuild: false, out: nil) + def cache_gem gemspec, rebuild: false, out: nil doc_map.cache(gemspec, rebuild: rebuild, out: out) end @@ -317,19 +317,19 @@ def resolve name, *gates # # @param pin [Pin::Reference] # @return [String, nil] - def dereference(pin) + def dereference pin store.constants.dereference(pin) end # @param fqns [String] # @return [Array] - def get_extends(fqns) + def get_extends fqns store.get_extends(fqns) end # @param fqns [String] # @return [Array] - def get_includes(fqns) + def get_includes fqns store.get_includes(fqns) end @@ -341,7 +341,7 @@ def get_includes(fqns) # @return [Array] def get_instance_variable_pins namespace, scope = :instance result = [] - used = [namespace] + [namespace] result.concat store.get_instance_variables(namespace, scope) sc_fqns = namespace while (sc = store.get_superclass(sc_fqns)) @@ -364,12 +364,12 @@ def get_instance_variable_pins namespace, scope = :instance # @param location [Location] # # @return [Pin::BaseVariable, nil] - def var_at_location(candidates, name, closure, location) - with_correct_name = candidates.select { |pin| pin.name == name} + def var_at_location candidates, name, closure, location + with_correct_name = candidates.select { |pin| pin.name == name } vars_at_location = with_correct_name.reject do |pin| # visible_at? excludes the starting position, but we want to # include it for this purpose - (!pin.visible_at?(closure, location) && !pin.starts_at?(location)) + !pin.visible_at?(closure, location) && !pin.starts_at?(location) end vars_at_location.inject(&:combine_with) @@ -446,7 +446,7 @@ def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true comments: init_pin.comments, closure: init_pin.closure, source: init_pin.source, - type_location: init_pin.type_location, + type_location: init_pin.type_location ) new_pin.parameters = init_pin.parameters.map do |init_param| param = init_param.clone @@ -536,7 +536,8 @@ def get_complex_type_methods complex_type, context = '', internal = false # @param preserve_generics [Boolean] True to preserve any # unresolved generic parameters, false to erase them # @return [Array] - def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false + def get_method_stack rooted_tag, name, scope: :instance, visibility: %i[private protected public], + preserve_generics: false rooted_type = ComplexType.parse(rooted_tag) fqns = rooted_type.namespace namespace_pin = store.get_path_pins(fqns).first @@ -661,7 +662,7 @@ def bundled? filename # @param sup [String] The superclass # @param sub [String] The subclass # @return [Boolean] - def super_and_sub?(sup, sub) + def super_and_sub? sup, sub sup = ComplexType.try_parse(sup) sub = ComplexType.try_parse(sub) # @todo If two literals are different values of the same type, it would @@ -693,14 +694,14 @@ def super_and_sub?(sup, sub) # @param module_ns [String] The module namespace (no type parameters) # # @return [Boolean] - def type_include?(host_ns, module_ns) + def type_include? host_ns, module_ns store.get_includes(host_ns).map { |inc_tag| inc_tag.type.name }.include?(module_ns) end # @param pins [Enumerable] # @param visibility [Enumerable] # @return [Array] - def resolve_method_aliases pins, visibility = [:public, :private, :protected] + def resolve_method_aliases pins, visibility = %i[public private protected] with_resolved_aliases = pins.map do |pin| next pin unless pin.is_a?(Pin::MethodAlias) resolved = resolve_method_alias(pin) @@ -730,7 +731,7 @@ def workspace # @param skip [Set] # @param no_core [Boolean] Skip core classes if true # @return [Array] - def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core) + def inner_get_methods_from_reference fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" } # Ensure the types returned by the methods in the referenced @@ -793,7 +794,7 @@ def store def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false rooted_type = ComplexType.parse(rooted_tag).force_rooted fqns = rooted_type.namespace - fqns_generic_params = rooted_type.all_params + rooted_type.all_params namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/ reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}" @@ -804,7 +805,9 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # ensure we start out with any immediate methods in this # namespace so we roughly match the same ordering of get_methods # and obey the 'deep' instruction - direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag } + direct_convention_methods, convention_methods_by_reference = environ.pins.partition do |p| + p.namespace == rooted_tag + end result.concat direct_convention_methods if deep && scope == :instance @@ -816,8 +819,10 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false # Store#get_methods doesn't know about full tags, just # namespaces; resolving the generics in the method pins is this # class' responsibility - methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name } - logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" } + methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort { |a, b| a.name <=> b.name } + logger.info do + "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" + end result.concat methods if deep result.concat convention_methods_by_reference @@ -826,7 +831,8 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false store.get_includes(fqns).reverse.each do |ref| in_tag = dereference(ref) # @sg-ignore Need to add nil check here - result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true) + result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, + skip, true) end rooted_sc_tag = qualify_superclass(rooted_tag) unless rooted_sc_tag.nil? @@ -834,7 +840,9 @@ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false visibility, true, skip, no_core) end else - logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" } + logger.info do + "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" + end store.get_extends(fqns).reverse.each do |em| fqem = dereference(em) result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil? @@ -870,7 +878,7 @@ def path_macros def get_namespace_type fqns return nil if fqns.nil? # @type [Pin::Namespace, nil] - pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first + pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first return nil if pin.nil? pin.type end @@ -896,7 +904,7 @@ def prefer_non_nil_variables pins # @param alias_pin [Pin::MethodAlias] # @return [Pin::Method, nil] - def resolve_method_alias(alias_pin) + def resolve_method_alias alias_pin ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag) # @type [Pin::Method, nil] original = nil @@ -927,7 +935,9 @@ def resolve_method_alias(alias_pin) end if original.nil? # :nocov: - Solargraph.assert_or_log(:alias_target_missing) { "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}" } + Solargraph.assert_or_log(:alias_target_missing) do + "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}" + end return nil # :nocov: end @@ -940,7 +950,7 @@ def resolve_method_alias(alias_pin) # @param alias_pin [Pin::MethodAlias] The alias pin to resolve # @param original [Pin::Method] The original method pin that was already found # @return [Pin::Method] The resolved method pin - def create_resolved_alias_pin(alias_pin, original) + def create_resolved_alias_pin alias_pin, original # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup) args = { location: alias_pin.location, @@ -956,7 +966,7 @@ def create_resolved_alias_pin(alias_pin, original) return_type: original.return_type, source: :resolve_method_alias } - resolved_pin = Pin::Method.new **args + resolved_pin = Pin::Method.new(**args) # Clone signatures and parameters resolved_pin.signatures.each do |sig| @@ -994,7 +1004,7 @@ def should_erase_generics_when_done? namespace_pin, rooted_type end # @param namespace_pin [Pin::Namespace, Pin::Constant] - def has_generics?(namespace_pin) + def has_generics? namespace_pin namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty? end diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 48cf05706..3d7405e0f 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -118,15 +118,15 @@ def catalog new_pins # @param k [String] # @param v [Set] set.classify(&:class) - .map { |k, v| pin_class_hash[k].concat v.to_a } + .map { |k, v| pin_class_hash[k].concat v.to_a } # @param k [String] # @param v [Set] set.classify(&:namespace) - .map { |k, v| namespace_hash[k].concat v.to_a } + .map { |k, v| namespace_hash[k].concat v.to_a } # @param k [String] # @param v [Set] set.classify(&:path) - .map { |k, v| path_pin_hash[k].concat v.to_a } + .map { |k, v| path_pin_hash[k].concat v.to_a } @namespaces = path_pin_hash.keys.compact.to_set map_references Pin::Reference::Include, include_references map_references Pin::Reference::Prepend, prepend_references @@ -157,9 +157,7 @@ def map_overrides pins = path_pin_hash[ovr.name] logger.debug { "ApiMap::Index#map_overrides: pins for path=#{ovr.name}: #{pins}" } pins.each do |pin| - new_pin = if pin.path.end_with?('#initialize') - path_pin_hash[pin.path.sub(/#initialize/, '.new')].first - end + new_pin = (path_pin_hash[pin.path.sub('#initialize', '.new')].first if pin.path.end_with?('#initialize')) (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag| # @sg-ignore Wrong argument type for # YARD::Docstring#delete_tags: name expected String, diff --git a/lib/solargraph/api_map/source_to_yard.rb b/lib/solargraph/api_map/source_to_yard.rb index 05010c636..f971d285d 100644 --- a/lib/solargraph/api_map/source_to_yard.rb +++ b/lib/solargraph/api_map/source_to_yard.rb @@ -3,7 +3,6 @@ module Solargraph class ApiMap module SourceToYard - # Get the YARD CodeObject at the specified path. # # @sg-ignore Declared return type generic, nil does not match @@ -36,20 +35,20 @@ def rake_yard store end if pin.type == :class # @param obj [YARD::CodeObjects::RootObject] - code_object_map[pin.path] ||= YARD::CodeObjects::ClassObject.new(root_code_object, pin.path) { |obj| + code_object_map[pin.path] ||= YARD::CodeObjects::ClassObject.new(root_code_object, pin.path) do |obj| # @sg-ignore flow sensitive typing needs to handle attrs next if pin.location.nil? || pin.location.filename.nil? # @sg-ignore flow sensitive typing needs to handle attrs obj.add_file(pin.location.filename, pin.location.range.start.line, !pin.comments.empty?) - } + end else # @param obj [YARD::CodeObjects::RootObject] - code_object_map[pin.path] ||= YARD::CodeObjects::ModuleObject.new(root_code_object, pin.path) { |obj| + code_object_map[pin.path] ||= YARD::CodeObjects::ModuleObject.new(root_code_object, pin.path) do |obj| # @sg-ignore flow sensitive typing needs to handle attrs next if pin.location.nil? || pin.location.filename.nil? # @sg-ignore flow sensitive typing needs to handle attrs obj.add_file(pin.location.filename, pin.location.range.start.line, !pin.comments.empty?) - } + end end code_object_map[pin.path].docstring = pin.docstring store.get_includes(pin.path).each do |ref| @@ -75,12 +74,14 @@ def rake_yard store # @sg-ignore Need to add nil check here # @param obj [YARD::CodeObjects::RootObject] - code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new(code_object_at(pin.namespace, YARD::CodeObjects::NamespaceObject), pin.name, pin.scope) { |obj| + code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new( + code_object_at(pin.namespace, YARD::CodeObjects::NamespaceObject), pin.name, pin.scope + ) do |obj| # @sg-ignore flow sensitive typing needs to handle attrs next if pin.location.nil? || pin.location.filename.nil? # @sg-ignore flow sensitive typing needs to handle attrs obj.add_file pin.location.filename, pin.location.range.start.line - } + end method_object = code_object_at(pin.path, YARD::CodeObjects::MethodObject) # @sg-ignore Need to add nil check here method_object.docstring = pin.docstring diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index bb371d173..cca195337 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -38,10 +38,10 @@ def update *pinsets pinsets[changed..].each_with_index do |pins, idx| @pinsets[changed + idx] = pins @indexes[changed + idx] = if pins.empty? - @indexes[changed + idx - 1] - else - @indexes[changed + idx - 1].merge(pins) - end + @indexes[changed + idx - 1] + else + @indexes[changed + idx - 1].merge(pins) + end end constants.clear cached_qualify_superclass.clear @@ -60,10 +60,10 @@ def inspect # @param visibility [Array] # @return [Enumerable] def get_constants fqns, visibility = [:public] - namespace_children(fqns).select { |pin| + namespace_children(fqns).select do |pin| # @sg-ignore flow sensitive typing not smart enough to handle this case !pin.name.empty? && (pin.is_a?(Pin::Namespace) || pin.is_a?(Pin::Constant)) && visibility.include?(pin.visibility) - } + end end # @param fqns [String] @@ -77,8 +77,10 @@ def get_methods fqns, scope: :instance, visibility: [:public] GemPins.combine_method_pins_by_path(all_pins) end - BOOLEAN_SUPERCLASS_PIN = Pin::Reference::Superclass.new(name: 'Boolean', closure: Pin::ROOT_PIN, source: :solargraph) - OBJECT_SUPERCLASS_PIN = Pin::Reference::Superclass.new(name: 'Object', closure: Pin::ROOT_PIN, source: :solargraph) + BOOLEAN_SUPERCLASS_PIN = Pin::Reference::Superclass.new(name: 'Boolean', closure: Pin::ROOT_PIN, + source: :solargraph) + OBJECT_SUPERCLASS_PIN = Pin::Reference::Superclass.new(name: 'Object', closure: Pin::ROOT_PIN, + source: :solargraph) # @param fqns [String, nil] # @return [Pin::Reference::Superclass, nil] @@ -129,17 +131,17 @@ def get_path_pins path # @param fqns [String, nil] # @param scope [Symbol] :class or :instance # @return [Enumerable] - def get_instance_variables(fqns, scope = :instance) - all_instance_variables.select { |pin| + def get_instance_variables fqns, scope = :instance + all_instance_variables.select do |pin| pin.binder.namespace == fqns && pin.binder.scope == scope - } + end end # @param fqns [String] # # @return [Enumerable] - def get_class_variables(fqns) - namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable)} + def get_class_variables fqns + namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable) } end # @return [Enumerable] @@ -149,7 +151,7 @@ def get_symbols # @param fqns [String] # @return [Boolean] - def namespace_exists?(fqns) + def namespace_exists? fqns fqns_pins(fqns).any? end @@ -165,7 +167,7 @@ def method_pins # @param fqns [String] # @return [Array] - def domains(fqns) + def domains fqns result = [] fqns_pins(fqns).each do |nspin| result.concat nspin.domains @@ -178,7 +180,7 @@ def named_macros @named_macros ||= begin result = {} pins.each do |pin| - pin.macros.select{|m| m.tag.tag_name == 'macro' && !m.tag.text.empty? }.each do |macro| + pin.macros.select { |m| m.tag.tag_name == 'macro' && !m.tag.text.empty? }.each do |macro| next if macro.tag.name.nil? || macro.tag.name.empty? result[macro.tag.name] = macro end @@ -217,7 +219,7 @@ def fqns_pins fqns # Get all ancestors (superclasses, includes, prepends, extends) for a namespace # @param fqns [String] The fully qualified namespace # @return [Array] Array of ancestor namespaces including the original - def get_ancestors(fqns) + def get_ancestors fqns return [] if fqns.nil? || fqns.empty? ancestors = [fqns] @@ -260,7 +262,7 @@ def get_ancestors(fqns) # @param fqns [String] # # @return [Array] - def get_ancestor_references(fqns) + def get_ancestor_references fqns (get_prepends(fqns) + get_includes(fqns) + [get_superclass(fqns)]).compact end @@ -350,7 +352,7 @@ def all_instance_variables # @param fqns [String] # @return [Pin::Reference::Superclass, nil] - def try_special_superclasses(fqns) + def try_special_superclasses fqns return OBJECT_SUPERCLASS_PIN if fqns == 'Boolean' return OBJECT_SUPERCLASS_PIN if !%w[BasicObject Object].include?(fqns) && namespace_exists?(fqns) diff --git a/lib/solargraph/bench.rb b/lib/solargraph/bench.rb index e6180c933..ed72c5a82 100644 --- a/lib/solargraph/bench.rb +++ b/lib/solargraph/bench.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true - module Solargraph # A container of source maps and workspace data to be cataloged in an ApiMap. # diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index c67f9c2a4..5b6af7126 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -4,7 +4,7 @@ module Solargraph # A container for type data based on YARD type tags. # class ComplexType - GENERIC_TAG_NAME = 'generic'.freeze + GENERIC_TAG_NAME = 'generic' # @!parse # include TypeMethods include Equality @@ -19,7 +19,7 @@ def initialize types = [UniqueType::UNDEFINED] # @type [Array] items = types.flat_map(&:items).uniq(&:to_s) if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' } - items.delete_if { |i| i.name == 'false' || i.name == 'true' } + items.delete_if { |i| %w[false true].include?(i.name) } items.unshift(UniqueType::BOOLEAN) end # @type [Array] @@ -36,10 +36,11 @@ def initialize types = [UniqueType::UNDEFINED] # @param api_map [ApiMap] # @param context [String] # @return [ComplexType] + # @param [Array] gates def qualify api_map, *gates red = reduce_object types = red.items.map do |t| - next t if ['nil', 'void', 'undefined'].include?(t.name) + next t if %w[nil void undefined].include?(t.name) next t if ['::Boolean'].include?(t.rooted_name) t.qualify api_map, *gates end @@ -53,7 +54,10 @@ def qualify api_map, *gates def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {} return self unless generic? - ComplexType.new(@items.map { |i| i.resolve_generics_from_context(generics_to_resolve, context_type, resolved_generic_values: resolved_generic_values) }) + ComplexType.new(@items.map do |i| + i.resolve_generics_from_context(generics_to_resolve, context_type, + resolved_generic_values: resolved_generic_values) + end) end # @return [UniqueType] @@ -84,14 +88,14 @@ def self_to_type dst # @sg-ignore Declared return type # ::Array<::Solargraph::ComplexType::UniqueType> does not match # inferred type ::Array<::Proc> for Solargraph::ComplexType#map - def map(&block) + def map &block @items.map(&block) end # @yieldparam [UniqueType] # @return [Enumerable] def each &block - @items.each &block + @items.each(&block) end # @yieldparam [UniqueType] @@ -102,7 +106,7 @@ def each_unique_type &block return enum_for(__method__) unless block_given? @items.each do |item| - item.each_unique_type &block + item.each_unique_type(&block) end end @@ -112,7 +116,7 @@ def each_unique_type &block # @param rooted [Boolean, nil] # @param new_subtypes [Array, nil] # @return [self] - def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil) + def recreate new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil ComplexType.new(map do |ut| ut.recreate(new_name: new_name, make_rooted: make_rooted, @@ -133,13 +137,13 @@ def to_a # @param index [Integer] # @return [UniqueType] - def [](index) + def [] index @items[index] end # @return [Array] def select &block - @items.select &block + @items.select(&block) end # @return [String] @@ -155,6 +159,7 @@ def namespaces # @param name [Symbol] # @return [Object, nil] + # @param [Array] args def method_missing name, *args, &block return if @items.first.nil? return @items.first.send(name, *args, &block) if respond_to_missing?(name) @@ -163,7 +168,7 @@ def method_missing name, *args, &block # @param name [Symbol] # @param include_private [Boolean] - def respond_to_missing?(name, include_private = false) + def respond_to_missing? name, include_private = false TypeMethods.public_instance_methods.include?(name) || super end @@ -212,10 +217,10 @@ def desc # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>] # @param variance [:invariant, :covariant, :contravariant] # @return [Boolean] - def conforms_to?(api_map, expected, + def conforms_to? api_map, expected, situation, rules = [], - variance: erased_variance(situation)) + variance: erased_variance(situation) expected = expected.downcast_to_literal_if_possible inferred = downcast_to_literal_if_possible @@ -256,14 +261,14 @@ def rooted_tags # @yieldparam [UniqueType] def all? &block - @items.all? &block + @items.all?(&block) end # @yieldparam [UniqueType] # @yieldreturn [Boolean] # @return [Boolean] def any? &block - @items.compact.any? &block + @items.compact.any?(&block) end def selfy? @@ -283,8 +288,10 @@ def simplify_literals # @yieldparam t [UniqueType] # @yieldreturn [UniqueType] # @return [ComplexType] - def transform(new_name = nil, &transform_type) - raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::') + def transform new_name = nil, &transform_type + if new_name&.start_with?('::') + raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" + end ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) }) end @@ -322,7 +329,7 @@ def all_params # @return [ComplexType] def reduce_class_type new_items = items.flat_map do |type| - next type unless ['Module', 'Class'].include?(type.name) + next type unless %w[Module Class].include?(type.name) next type if type.all_params.empty? type.all_params @@ -337,7 +344,7 @@ def all_rooted? end # @param other [ComplexType, UniqueType] - def erased_version_of?(other) + def erased_version_of? other return false if items.length != 1 || other.items.length != 1 @items.first.erased_version_of?(other.items.first) @@ -427,6 +434,7 @@ class << self # Chain::Call needs to know the decl type (:arg, :optarg, # :kwarg, etc) of the arguments given, instead of just having # an array of Chains as the arguments. + # @param [Boolean] partial def parse *strings, partial: false # @type [Hash{Array => ComplexType, Array}] @cache ||= {} @@ -446,14 +454,14 @@ def parse *strings, partial: false # @param char [String] type_string&.each_char do |char| if char == '=' - #raise ComplexTypeError, "Invalid = in type #{type_string}" unless curly_stack > 0 + # raise ComplexTypeError, "Invalid = in type #{type_string}" unless curly_stack > 0 elsif char == '<' point_stack += 1 elsif char == '>' if subtype_string.end_with?('=') && curly_stack > 0 subtype_string += char elsif base.end_with?('=') - raise ComplexTypeError, "Invalid hash thing" unless key_types.nil? + raise ComplexTypeError, 'Invalid hash thing' unless key_types.nil? # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)]) # @sg-ignore Need to add nil check here types.push UniqueType.parse(base[0..-2].strip, subtype_string) @@ -498,12 +506,15 @@ def parse *strings, partial: false subtype_string.concat char end end - raise ComplexTypeError, "Unclosed subtype in #{type_string}" if point_stack != 0 || curly_stack != 0 || paren_stack != 0 + if point_stack != 0 || curly_stack != 0 || paren_stack != 0 + raise ComplexTypeError, + "Unclosed subtype in #{type_string}" + end # types.push ComplexType.new([UniqueType.new(base, subtype_string)]) types.push UniqueType.parse(base.strip, subtype_string.strip) end unless key_types.nil? - raise ComplexTypeError, "Invalid use of key/value parameters" unless partial + raise ComplexTypeError, 'Invalid use of key/value parameters' unless partial return key_types if types.empty? return [key_types, types] end @@ -515,7 +526,7 @@ def parse *strings, partial: false # @param strings [Array] # @return [ComplexType] def try_parse *strings - parse *strings + parse(*strings) rescue ComplexTypeError => e Solargraph.logger.info "Error parsing complex type `#{strings.join(', ')}`: #{e.message}" ComplexType::UNDEFINED @@ -540,9 +551,7 @@ def try_parse *strings # @param dst [String] # @return [String] def reduce_class dst - while dst =~ /^(Class|Module)\<(.*?)\>$/ - dst = dst.sub(/^(Class|Module)\$/, '') - end + dst = dst.sub(/^(Class|Module)$/, '') while dst =~ /^(Class|Module)<(.*?)>$/ dst end end diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index 1725189a4..465f36502 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -87,7 +87,7 @@ def erased_variance situation = :method_call # @param generics_to_erase [Enumerable] # @return [self] - def erase_generics(generics_to_erase) + def erase_generics generics_to_erase transform do |type| if type.name == ComplexType::GENERIC_TAG_NAME if type.all_params.length == 1 && generics_to_erase.include?(type.all_params.first.to_s) @@ -142,7 +142,7 @@ def namespace @namespace ||= lambda do return 'Object' if duck_type? return 'NilClass' if nil_type? - return (name == 'Class' || name == 'Module') && !subtypes.empty? ? subtypes.first.name : name + %w[Class Module].include?(name) && !subtypes.empty? ? subtypes.first.name : name end.call end @@ -150,7 +150,7 @@ def namespace def namespace_type return ComplexType.parse('::Object') if duck_type? return ComplexType.parse('::NilClass') if nil_type? - return subtypes.first if (name == 'Class' || name == 'Module') && !subtypes.empty? + return subtypes.first if %w[Class Module].include?(name) && !subtypes.empty? self end @@ -177,7 +177,7 @@ def rooted_substring end # @return [String] - def generate_substring_from(&to_str) + def generate_substring_from &to_str key_types_str = key_types.map(&to_str).join(', ') subtypes_str = subtypes.map(&to_str).join(', ') if key_types.none?(&:defined?) && subtypes.none?(&:defined?) @@ -188,19 +188,17 @@ def generate_substring_from(&to_str) "{#{key_types_str} => #{subtypes_str}}" elsif fixed_parameters? "(#{subtypes_str})" + elsif name == 'Hash' + "<#{key_types_str}, #{subtypes_str}>" else - if name == 'Hash' - "<#{key_types_str}, #{subtypes_str}>" - else - "<#{key_types_str}#{subtypes_str}>" - end + "<#{key_types_str}#{subtypes_str}>" end end # @return [::Symbol] :class or :instance def scope @scope ||= :instance if duck_type? || nil_type? - @scope ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? :class : :instance + @scope ||= %w[Class Module].include?(name) && !subtypes.empty? ? :class : :instance end # @param other [Object] diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 7577c3276..202626141 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -25,9 +25,7 @@ class UniqueType # @param make_rooted [Boolean, nil] # @return [UniqueType] def self.parse name, substring = '', make_rooted: nil - if name.start_with?(':::') - raise ComplexTypeError, "Illegal prefix: #{name}" - end + raise ComplexTypeError, "Illegal prefix: #{name}" if name.start_with?(':::') if name.start_with?('::') name = name[2..-1] rooted = true @@ -48,13 +46,17 @@ def self.parse name, substring = '', make_rooted: nil # @sg-ignore Need to add nil check here parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0]) if parameters_type == :hash - raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType) + unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType) + raise ComplexTypeError, + "Bad hash type: name=#{name}, substring=#{substring}" + end key_types.concat(subs[0].map { |u| ComplexType.new([u]) }) subtypes.concat(subs[1].map { |u| ComplexType.new([u]) }) elsif parameters_type == :list && name == 'Hash' # Treat Hash as Hash{A => B} if subs.length != 2 - raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters" + raise ComplexTypeError, + "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters" end key_types.concat(subs[0].map { |u| ComplexType.new([u]) }) subtypes.concat(subs[1].map { |u| ComplexType.new([u]) }) @@ -71,9 +73,9 @@ def self.parse name, substring = '', make_rooted: nil # @param subtypes [Array] # @param rooted [Boolean] # @param parameters_type [Symbol, nil] - def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil) - if parameters_type.nil? - raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty? + def initialize name, key_types = [], subtypes = [], rooted:, parameters_type: nil + if parameters_type.nil? && !(key_types.empty? && subtypes.empty?) + raise 'You must supply parameters_type if you provide parameters' end raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::') @name = name @@ -95,7 +97,7 @@ def implicit_union? # @todo use api_map to establish number of generics in type; # if only one is allowed but multiple are passed in, treat # those as implicit unions - ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed + %w[Hash Array Set _ToAry Enumerable _Each].include?(name) && parameters_type != :fixed end def to_s @@ -175,7 +177,7 @@ def determine_non_literal_name # | `false` return name if name.empty? return 'NilClass' if name == 'nil' - return 'Boolean' if ['true', 'false'].include?(name) + return 'Boolean' if %w[true false].include?(name) return 'Symbol' if name[0] == ':' # @sg-ignore Need to add nil check here return 'String' if ['"', "'"].include?(name[0]) @@ -183,7 +185,7 @@ def determine_non_literal_name name end - def eql?(other) + def eql? other self.class == other.class && # @sg-ignore flow sensitive typing should support .class == .class @name == other.name && @@ -199,7 +201,7 @@ def eql?(other) @parameters_type == other.parameters_type end - def ==(other) + def == other eql?(other) end @@ -231,7 +233,7 @@ def parameter_variance _situation, default = :covariant # covariant # contravariant?: Proc - can be changed, so we can pass # in less specific super types - if ['Hash', 'Tuple', 'Array', 'Set', 'Enumerable'].include?(name) && fixed_parameters? + if %w[Hash Tuple Array Set Enumerable].include?(name) && fixed_parameters? :covariant else default @@ -244,7 +246,7 @@ def interface? end # @param other [UniqueType] - def erased_version_of?(other) + def erased_version_of? other name == other.name && (all_params.empty? || all_params.all?(&:undefined?)) end @@ -253,8 +255,8 @@ def erased_version_of?(other) # @param situation [:method_call, :assignment, :return_type] # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>] # @param variance [:invariant, :covariant, :contravariant] - def conforms_to?(api_map, expected, situation, rules = [], - variance: erased_variance(situation)) + def conforms_to? api_map, expected, situation, rules = [], + variance: erased_variance(situation) return true if undefined? && rules.include?(:allow_undefined) # @todo teach this to validate duck types as inferred type @@ -314,9 +316,9 @@ def to_rbs 'nil' elsif name == GENERIC_TAG_NAME && !all_params.empty? all_params.first.name - elsif ['Class', 'Module'].include?(name) + elsif %w[Class Module].include?(name) rbs_name - elsif ['Tuple', 'Array'].include?(name) && fixed_parameters? + elsif %w[Tuple Array].include?(name) && fixed_parameters? # tuples don't have a name; they're just [foo, bar, baz]. if substring == '()' # but there are no zero element tuples, so we go with an array @@ -341,7 +343,7 @@ def parameters? # @param types [Array] # @return [String] - def rbs_union(types) + def rbs_union types if types.length == 1 types.first.to_rbs else @@ -396,7 +398,8 @@ def resolve_generics_from_context generics_to_resolve, context_type, resolved_ge end if new_binding resolved_generic_values.transform_values! do |complex_type| - complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values) + complex_type.resolve_generics_from_context(generics_to_resolve, nil, + resolved_generic_values: resolved_generic_values) end end # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo) @@ -404,8 +407,10 @@ def resolve_generics_from_context generics_to_resolve, context_type, resolved_ge end # @todo typechecking should complain when the method being called has no @yieldparam tag - new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:key_types) - new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:subtypes) + new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, + &:key_types) + new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, + &:subtypes) recreate(new_key_types: new_key_types, new_subtypes: new_subtypes) end @@ -414,7 +419,7 @@ def resolve_generics_from_context generics_to_resolve, context_type, resolved_ge # @param resolved_generic_values [Hash{String => ComplexType}] # @yieldreturn [Array] # @return [Array] - def resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values) + def resolve_param_generics_from_context generics_to_resolve, context_type, resolved_generic_values types = yield self types.each_with_index.flat_map do |ct, i| ct.items.flat_map do |ut| @@ -422,10 +427,12 @@ def resolve_param_generics_from_context(generics_to_resolve, context_type, resol if context_params && context_params[i] type_arg = context_params[i] type_arg.map do |new_unique_context_type| - ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, resolved_generic_values: resolved_generic_values + ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, + resolved_generic_values: resolved_generic_values end else - ut.resolve_generics_from_context generics_to_resolve, nil, resolved_generic_values: resolved_generic_values + ut.resolve_generics_from_context generics_to_resolve, nil, + resolved_generic_values: resolved_generic_values end end end @@ -481,7 +488,7 @@ def map &block # @yieldreturn [self] # @return [Enumerable] def each &block - [self].each &block + [self].each(&block) end # @return [Array] @@ -495,7 +502,7 @@ def to_a # @param rooted [Boolean, nil] # @param new_subtypes [Array, nil] # @return [self] - def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil) + def recreate new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::') new_name ||= name @@ -529,8 +536,10 @@ def force_rooted # @yieldparam t [UniqueType] # @yieldreturn [self] # @return [self] - def transform(new_name = nil, &transform_type) - raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::') + def transform new_name = nil, &transform_type + if new_name&.start_with?('::') + raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" + end if name == ComplexType::GENERIC_TAG_NAME # doesn't make sense to manipulate the name of the generic new_key_types = @key_types @@ -539,7 +548,8 @@ def transform(new_name = nil, &transform_type) new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } } new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } } end - new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted) + new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, + make_rooted: @rooted) yield new_type end @@ -548,6 +558,7 @@ def transform(new_name = nil, &transform_type) # @param api_map [ApiMap] The ApiMap that performs qualification # @param context [String] The namespace from which to resolve names # @return [self, ComplexType, UniqueType] The generated ComplexType + # @param [Array] gates def qualify api_map, *gates transform do |t| next t if t.name == GENERIC_TAG_NAME @@ -584,7 +595,7 @@ def any? &block # @return [ComplexType] def reduce_class_type new_items = items.flat_map do |type| - next type unless ['Module', 'Class'].include?(type.name) + next type unless %w[Module Class].include?(type.name) next type if type.all_params.empty? type.all_params @@ -602,12 +613,12 @@ def rooted? end # @param name_to_check [String] - def can_root_name?(name_to_check = name) + def can_root_name? name_to_check = name self.class.can_root_name?(name_to_check) end # @param name [String] - def self.can_root_name?(name) + def self.can_root_name? name # name is not lowercase !name.empty? && name != name.downcase end @@ -624,7 +635,6 @@ def self.can_root_name?(name) '::NilClass' => UniqueType::NIL }.freeze - include Logging end end diff --git a/lib/solargraph/convention.rb b/lib/solargraph/convention.rb index 89eac82b7..5e73eb3bf 100644 --- a/lib/solargraph/convention.rb +++ b/lib/solargraph/convention.rb @@ -30,7 +30,7 @@ def self.unregister convention # @param source_map [SourceMap] # @return [Environ] - def self.for_local(source_map) + def self.for_local source_map result = Environ.new @@conventions.each do |conv| result.merge conv.local(source_map) @@ -40,7 +40,7 @@ def self.for_local(source_map) # @param doc_map [DocMap] # @return [Environ] - def self.for_global(doc_map) + def self.for_global doc_map result = Environ.new @@conventions.each do |conv| result.merge conv.global(doc_map) diff --git a/lib/solargraph/convention/data_definition.rb b/lib/solargraph/convention/data_definition.rb index 193364061..960852caa 100644 --- a/lib/solargraph/convention/data_definition.rb +++ b/lib/solargraph/convention/data_definition.rb @@ -93,7 +93,7 @@ def data_definition_node # @param attribute_node [Parser::AST::Node] # @param attribute_name [String] # @return [String, nil] - def attribute_comments(attribute_node, attribute_name) + def attribute_comments attribute_node, attribute_name data_comments = comments_for(attribute_node) return if data_comments.nil? || data_comments.empty? diff --git a/lib/solargraph/convention/data_definition/data_assignment_node.rb b/lib/solargraph/convention/data_definition/data_assignment_node.rb index cffe77494..97ef272cf 100644 --- a/lib/solargraph/convention/data_definition/data_assignment_node.rb +++ b/lib/solargraph/convention/data_definition/data_assignment_node.rb @@ -23,7 +23,7 @@ class << self # s(:args), # s(:send, nil, :bar)))) # @param node [::Parser::AST::Node] - def match?(node) + def match? node return false unless node&.type == :casgn return false if node.children[2].nil? diff --git a/lib/solargraph/convention/data_definition/data_definition_node.rb b/lib/solargraph/convention/data_definition/data_definition_node.rb index 49cf210a7..d14817933 100644 --- a/lib/solargraph/convention/data_definition/data_definition_node.rb +++ b/lib/solargraph/convention/data_definition/data_definition_node.rb @@ -27,7 +27,7 @@ class << self # s(:send, nil, :bar))) # # @param node [Parser::AST::Node] - def match?(node) + def match? node return false unless node&.type == :class data_definition_node?(node.children[1]) @@ -37,7 +37,7 @@ def match?(node) # @param data_node [Parser::AST::Node] # @return [Boolean] - def data_definition_node?(data_node) + def data_definition_node? data_node return false unless data_node.is_a?(::Parser::AST::Node) return false unless data_node&.type == :send return false unless data_node.children[0]&.type == :const @@ -49,7 +49,7 @@ def data_definition_node?(data_node) end # @param node [Parser::AST::Node] - def initialize(node) + def initialize node @node = node end diff --git a/lib/solargraph/convention/struct_definition.rb b/lib/solargraph/convention/struct_definition.rb index 7871dec00..f1d240363 100644 --- a/lib/solargraph/convention/struct_definition.rb +++ b/lib/solargraph/convention/struct_definition.rb @@ -142,7 +142,7 @@ def parse_comments # @param tag [YARD::Tags::Tag, nil] The param tag for this attribute.xtract_ # # @return [String] - def tag_string(tag) + def tag_string tag tag&.types&.join(',') || 'undefined' end @@ -150,8 +150,8 @@ def tag_string(tag) # @param for_setter [Boolean] If true, will return a @param tag instead of a @return tag # # @return [String] The formatted comment for the attribute - def attribute_comment(tag, for_setter) - return "" if tag.nil? + def attribute_comment tag, for_setter + return '' if tag.nil? suffix = "[#{tag_string(tag)}] #{tag.text}" diff --git a/lib/solargraph/convention/struct_definition/struct_assignment_node.rb b/lib/solargraph/convention/struct_definition/struct_assignment_node.rb index 2816de6ed..6dcafd068 100644 --- a/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +++ b/lib/solargraph/convention/struct_definition/struct_assignment_node.rb @@ -24,7 +24,7 @@ class << self # s(:send, nil, :bar)))) # # @param node [Parser::AST::Node] - def match?(node) + def match? node return false unless node&.type == :casgn return false if node.children[2].nil? diff --git a/lib/solargraph/convention/struct_definition/struct_definition_node.rb b/lib/solargraph/convention/struct_definition/struct_definition_node.rb index 8f03f1fcb..51518a687 100644 --- a/lib/solargraph/convention/struct_definition/struct_definition_node.rb +++ b/lib/solargraph/convention/struct_definition/struct_definition_node.rb @@ -27,7 +27,7 @@ class << self # s(:send, nil, :bar))) # # @param node [Parser::AST::Node] - def match?(node) + def match? node return false unless node&.type == :class struct_definition_node?(node.children[1]) @@ -37,7 +37,7 @@ def match?(node) # @param struct_node [Parser::AST::Node] # @return [Boolean] - def struct_definition_node?(struct_node) + def struct_definition_node? struct_node return false unless struct_node.is_a?(::Parser::AST::Node) return false unless struct_node&.type == :send return false unless struct_node.children[0]&.type == :const @@ -49,7 +49,7 @@ def struct_definition_node?(struct_node) end # @param node [Parser::AST::Node] - def initialize(node) + def initialize node @node = node end diff --git a/lib/solargraph/diagnostics/rubocop.rb b/lib/solargraph/diagnostics/rubocop.rb index 5eade7592..23dbbfe63 100644 --- a/lib/solargraph/diagnostics/rubocop.rb +++ b/lib/solargraph/diagnostics/rubocop.rb @@ -33,7 +33,7 @@ def diagnose source, _api_map # a time - it uses 'chdir' to read config files with ERB, # which can conflict with other chdirs. result = Solargraph::CHDIR_MUTEX.synchronize do - redirect_stdout{ runner.run(paths) } + redirect_stdout { runner.run(paths) } end return [] if result.empty? @@ -77,7 +77,7 @@ def offense_to_diagnostic off severity: SEVERITIES[off['severity']], source: 'rubocop', code: off['cop_name'], - message: off['message'].gsub(/^#{off['cop_name']}\:/, '') + message: off['message'].gsub(/^#{off['cop_name']}:/, '') } end @@ -96,22 +96,22 @@ def offense_start_position off # @param off [Hash{String => Hash{String => Integer}}] # @return [Position] def offense_ending_position off - if off['location']['start_line'] != off['location']['last_line'] - Position.new(off['location']['start_line'], 0) - else + if off['location']['start_line'] == off['location']['last_line'] start_line = off['location']['start_line'] - 1 # @type [Integer] last_column = off['location']['last_column'] line = @source.code.lines[start_line] col_off = if line.nil? || line.empty? - 1 - else - 0 - end + 1 + else + 0 + end Position.new( start_line, last_column - col_off ) + else + Position.new(off['location']['start_line'], 0) end end end diff --git a/lib/solargraph/diagnostics/rubocop_helpers.rb b/lib/solargraph/diagnostics/rubocop_helpers.rb index b306f638a..52a205465 100644 --- a/lib/solargraph/diagnostics/rubocop_helpers.rb +++ b/lib/solargraph/diagnostics/rubocop_helpers.rb @@ -13,7 +13,7 @@ module RubocopHelpers # @param version [String, nil] # @raise [InvalidRubocopVersionError] if _version_ is not installed # @return [void] - def require_rubocop(version = nil) + def require_rubocop version = nil begin # @type [String] gem_path = Gem::Specification.find_by_name('rubocop', version).full_gem_path @@ -24,7 +24,7 @@ def require_rubocop(version = nil) # @type [Array] specs = e.specs raise InvalidRubocopVersionError, - "could not find '#{e.name}' (#{e.requirement}) - "\ + "could not find '#{e.name}' (#{e.requirement}) - " \ "did find: [#{specs.map { |s| s.version.version }.join(', ')}]" end require 'rubocop' diff --git a/lib/solargraph/diagnostics/type_check.rb b/lib/solargraph/diagnostics/type_check.rb index 1669586ac..b2ff446c4 100644 --- a/lib/solargraph/diagnostics/type_check.rb +++ b/lib/solargraph/diagnostics/type_check.rb @@ -10,19 +10,20 @@ class TypeCheck < Base def diagnose source, api_map, workspace: nil # return [] unless args.include?('always') || api_map.workspaced?(source.filename) severity = Diagnostics::Severities::ERROR - level = (args.reverse.find { |a| ['normal', 'typed', 'strict', 'strong'].include?(a) }) || :normal + level = args.reverse.find { |a| %w[normal typed strict strong].include?(a) } || :normal # @sg-ignore sensitive typing needs to handle || on nil types - checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym, workspace: workspace) + checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym, + workspace: workspace) checker.problems - .sort { |a, b| a.location.range.start.line <=> b.location.range.start.line } - .map do |problem| - { - range: extract_first_line(problem.location, source), - severity: severity, - source: 'Typecheck', - message: problem.message - } - end + .sort { |a, b| a.location.range.start.line <=> b.location.range.start.line } + .map do |problem| + { + range: extract_first_line(problem.location, source), + severity: severity, + source: 'Typecheck', + message: problem.message + } + end end private diff --git a/lib/solargraph/diagnostics/update_errors.rb b/lib/solargraph/diagnostics/update_errors.rb index b6f9baa89..6da4c5835 100644 --- a/lib/solargraph/diagnostics/update_errors.rb +++ b/lib/solargraph/diagnostics/update_errors.rb @@ -26,7 +26,7 @@ def diagnose source, api_map def combine_ranges code, ranges result = [] lines = [] - ranges.sort{|a, b| a.start.line <=> b.start.line}.each do |rng| + ranges.sort { |a, b| a.start.line <=> b.start.line }.each do |rng| next if rng.nil? || lines.include?(rng.start.line) lines.push rng.start.line next if rng.start.line >= code.lines.length diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 79bb086cc..f3cdfa94a 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -98,12 +98,12 @@ def dependencies out: $stderr @dependencies ||= begin gem_deps = gemspecs - .flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } - .uniq(&:name) + .flat_map { |spec| workspace.fetch_dependencies(spec, out: out) } + .uniq(&:name) stdlib_deps = gemspecs - .flat_map { |spec| workspace.stdlib_dependencies(spec.name) } - .flat_map { |dep_name| workspace.resolve_require(dep_name) } - .compact + .flat_map { |spec| workspace.stdlib_dependencies(spec.name) } + .flat_map { |dep_name| workspace.resolve_require(dep_name) } + .compact existing_gems = gemspecs.map(&:name) (gem_deps + stdlib_deps).reject { |gemspec| existing_gems.include? gemspec.name } end @@ -167,7 +167,7 @@ def load_serialized_gem_pins out: @out end end - existing_pin_count = serialized_pins.length + serialized_pins.length time = Benchmark.measure do gemspecs.each do |gemspec| # only deserializes already-cached gems @@ -179,7 +179,7 @@ def load_serialized_gem_pins out: @out end end end - pins_processed = serialized_pins.length - existing_pin_count + serialized_pins.length milliseconds = (time.real * 1000).round if (milliseconds > 500) && out && gemspecs.any? out.puts "Deserialized #{serialized_pins.length} gem pins from #{PinCache.base_dir} in #{milliseconds} ms" diff --git a/lib/solargraph/equality.rb b/lib/solargraph/equality.rb index edef8fa7a..0a266df79 100644 --- a/lib/solargraph/equality.rb +++ b/lib/solargraph/equality.rb @@ -10,7 +10,7 @@ module Equality # @param other [Object] # @return [Boolean] - def eql?(other) + def eql? other self.class.eql?(other.class) && # @sg-ignore flow sensitive typing should support .class == .class equality_fields.eql?(other.equality_fields) @@ -18,8 +18,8 @@ def eql?(other) # @param other [Object] # @return [Boolean] - def ==(other) - self.eql?(other) + def == other + eql?(other) end def hash diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 790422065..340746a8f 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -13,7 +13,7 @@ class << self # @param pins [Array] # @return [Array] - def self.combine_method_pins_by_path(pins) + def self.combine_method_pins_by_path pins method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } by_path = method_pins.group_by(&:path) by_path.transform_values! do |pins| @@ -47,7 +47,7 @@ def self.combine_method_pins(*pins) # @param rbs_pins [Array] # # @return [Array] - def self.combine(yard_pins, rbs_pins) + def self.combine yard_pins, rbs_pins in_yard = Set.new rbs_store = Solargraph::ApiMap::Store.new(rbs_pins) combined = yard_pins.map do |yard_pin| @@ -57,7 +57,9 @@ def self.combine(yard_pins, rbs_pins) next yard_pin unless rbs_pin && yard_pin.is_a?(Pin::Method) unless rbs_pin - logger.debug { "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" } + logger.debug do + "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" + end next yard_pin end diff --git a/lib/solargraph/language_server/error_codes.rb b/lib/solargraph/language_server/error_codes.rb index 492ca6462..7df65388a 100644 --- a/lib/solargraph/language_server/error_codes.rb +++ b/lib/solargraph/language_server/error_codes.rb @@ -5,16 +5,16 @@ module LanguageServer # The ErrorCode constants for the language server protocol. # module ErrorCodes - PARSE_ERROR = -32700 - INVALID_REQUEST = -32600 - METHOD_NOT_FOUND = -32601 - INVALID_PARAMS = -32602 - INTERNAL_ERROR = -32603 - SERVER_ERROR_START = -32099 - SERVER_ERROR_END = -32000 - SERVER_NOT_INITIALIZED = -32002 - UNKNOWN_ERROR_CODE = -32001 - REQUEST_CANCELLED = -32800 + PARSE_ERROR = -32_700 + INVALID_REQUEST = -32_600 + METHOD_NOT_FOUND = -32_601 + INVALID_PARAMS = -32_602 + INTERNAL_ERROR = -32_603 + SERVER_ERROR_START = -32_099 + SERVER_ERROR_END = -32_000 + SERVER_NOT_INITIALIZED = -32_002 + UNKNOWN_ERROR_CODE = -32_001 + REQUEST_CANCELLED = -32_800 end end end diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index c59f7cd5e..42ef92c59 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -119,7 +119,7 @@ def receive request nil end else - logger.warn "Invalid message received." + logger.warn 'Invalid message received.' logger.debug request nil end @@ -154,7 +154,7 @@ def delete *uris lib.delete(*filenames) end uris.each do |uri| - send_notification "textDocument/publishDiagnostics", { + send_notification 'textDocument/publishDiagnostics', { uri: uri, diagnostics: [] } @@ -209,7 +209,7 @@ def diagnose uri logger.info "Diagnosing #{uri}" begin results = library.diagnose uri_to_file(uri) - send_notification "textDocument/publishDiagnostics", { + send_notification 'textDocument/publishDiagnostics', { uri: uri, diagnostics: results } @@ -354,7 +354,7 @@ def folders # @return [void] def send_notification method, params response = { - jsonrpc: "2.0", + jsonrpc: '2.0', method: method, params: params } @@ -377,7 +377,7 @@ def send_notification method, params def send_request method, params, &block @request_mutex.synchronize do message = { - jsonrpc: "2.0", + jsonrpc: '2.0', method: method, params: params, id: @next_request_id @@ -419,13 +419,13 @@ def register_capabilities methods # @return [void] def unregister_capabilities methods logger.debug "Unregistering capabilities: #{methods}" - unregisterations = methods.select{|m| registered?(m)}.map{ |m| + unregisterations = methods.select { |m| registered?(m) }.map do |m| @registered_capabilities.delete m { id: m, method: m } - } + end return if unregisterations.empty? send_request 'client/unregisterCapability', { unregisterations: unregisterations } end @@ -493,7 +493,7 @@ def locate_pins params params['data']['location']['range']['end']['character'] ) ) - result.concat library.locate_pins(location).select{ |pin| pin.name == params['label'] } + result.concat(library.locate_pins(location).select { |pin| pin.name == params['label'] }) end if params['data']['path'] result.concat library.path_pins(params['data']['path']) @@ -748,9 +748,12 @@ def generate_updater params params['contentChanges'].each do |recvd| chng = check_diff(params['textDocument']['uri'], recvd) changes.push Solargraph::Source::Change.new( - (chng['range'].nil? ? - nil : - Solargraph::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], chng['range']['end']['line'], chng['range']['end']['character']) + (if chng['range'].nil? + nil + else + Solargraph::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], + chng['range']['end']['line'], chng['range']['end']['character']) + end ), chng['text'] ) @@ -813,10 +816,10 @@ def dynamic_capability_options # documentSymbolProvider: true, # workspaceSymbolProvider: true, # workspace: { - # workspaceFolders: { - # supported: true, - # changeNotifications: true - # } + # workspaceFolders: { + # supported: true, + # changeNotifications: true + # } # } 'textDocument/definition' => { definitionProvider: true diff --git a/lib/solargraph/language_server/host/diagnoser.rb b/lib/solargraph/language_server/host/diagnoser.rb index e69ae16f9..8c259c131 100644 --- a/lib/solargraph/language_server/host/diagnoser.rb +++ b/lib/solargraph/language_server/host/diagnoser.rb @@ -56,7 +56,7 @@ def start # @return [void] def tick return if queue.empty? || host.synchronizing? - if !host.options['diagnostics'] + unless host.options['diagnostics'] mutex.synchronize { queue.clear } return end diff --git a/lib/solargraph/language_server/host/dispatch.rb b/lib/solargraph/language_server/host/dispatch.rb index f64b4ac96..11e352115 100644 --- a/lib/solargraph/language_server/host/dispatch.rb +++ b/lib/solargraph/language_server/host/dispatch.rb @@ -45,12 +45,11 @@ def update_libraries uri # @param uri [String] # @return [Library] def library_for uri - result = explicit_library_for(uri) || + explicit_library_for(uri) || implicit_library_for(uri) || generic_library_for(uri) # previous library for already call attach. avoid call twice # result.attach sources.find(uri) if sources.include?(uri) - result end # Find an explicit library match for the given URI. An explicit match diff --git a/lib/solargraph/language_server/host/message_worker.rb b/lib/solargraph/language_server/host/message_worker.rb index b0878b154..440e2c7cc 100644 --- a/lib/solargraph/language_server/host/message_worker.rb +++ b/lib/solargraph/language_server/host/message_worker.rb @@ -20,7 +20,7 @@ class MessageWorker ].freeze # @param host [Host] - def initialize(host) + def initialize host @host = host @mutex = Mutex.new @resource = ConditionVariable.new @@ -44,7 +44,7 @@ def stop # @param message [Hash] The message to handle. Will be forwarded to Host#receive # @return [void] - def queue(message) + def queue message @mutex.synchronize do messages.push(message) @resource.signal diff --git a/lib/solargraph/language_server/host/sources.rb b/lib/solargraph/language_server/host/sources.rb index 766ea634a..72a155d73 100644 --- a/lib/solargraph/language_server/host/sources.rb +++ b/lib/solargraph/language_server/host/sources.rb @@ -13,7 +13,7 @@ class Sources # @param uri [String] # @return [void] - def add_uri(uri) + def add_uri uri queue.push(uri) end diff --git a/lib/solargraph/language_server/message/client/register_capability.rb b/lib/solargraph/language_server/message/client/register_capability.rb index a9af8748e..67469def8 100644 --- a/lib/solargraph/language_server/message/client/register_capability.rb +++ b/lib/solargraph/language_server/message/client/register_capability.rb @@ -5,9 +5,7 @@ module LanguageServer module Message module Client class RegisterCapability < Solargraph::LanguageServer::Message::Base - def process - - end + def process; end end end end diff --git a/lib/solargraph/language_server/message/completion_item/resolve.rb b/lib/solargraph/language_server/message/completion_item/resolve.rb index 85e03ad4f..171ec6a6c 100644 --- a/lib/solargraph/language_server/message/completion_item/resolve.rb +++ b/lib/solargraph/language_server/message/completion_item/resolve.rb @@ -21,9 +21,9 @@ def merge pins docs = pins .reject { |pin| pin.documentation.empty? && pin.return_type.undefined? } result = params - .transform_keys(&:to_sym) - .merge(pins.first.resolve_completion_item) - .merge(documentation: markup_content(join_docs(docs))) + .transform_keys(&:to_sym) + .merge(pins.first.resolve_completion_item) + .merge(documentation: markup_content(join_docs(docs))) result[:detail] = pins.first.detail result end @@ -43,11 +43,9 @@ def markup_content text def join_docs pins result = [] last_link = nil - pins.each_with_index do |pin| + pins.each do |pin| this_link = host.options['enablePages'] ? pin.link_documentation : pin.text_documentation - if this_link && this_link != last_link && this_link != 'undefined' - result.push this_link - end + result.push this_link if this_link && this_link != last_link && this_link != 'undefined' result.push pin.documentation unless result.last && result.last.end_with?(pin.documentation) last_link = this_link end diff --git a/lib/solargraph/language_server/message/extended/check_gem_version.rb b/lib/solargraph/language_server/message/extended/check_gem_version.rb index 4a6a3df4d..8909406a4 100644 --- a/lib/solargraph/language_server/message/extended/check_gem_version.rb +++ b/lib/solargraph/language_server/message/extended/check_gem_version.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true - module Solargraph module LanguageServer module Message @@ -17,8 +16,8 @@ def self.fetcher # @param obj [Gem::SpecFetcher] # @return [Gem::SpecFetcher] - def self.fetcher= obj - @fetcher = obj + class << self + attr_writer :fetcher end GEM_ZERO = Gem::Version.new('0.0.0') @@ -41,11 +40,11 @@ def process ['Update now'] do |result| next unless result == 'Update now' cmd = if host.options['useBundler'] - 'bundle update solargraph' - else - 'gem update solargraph' - end - o, s = Open3.capture2(cmd) + 'bundle update solargraph' + else + 'gem update solargraph' + end + _, s = Open3.capture2(cmd) if s == 0 host.show_message 'Successfully updated the Solargraph gem.', LanguageServer::MessageTypes::INFO host.send_notification '$/solargraph/restart', {} @@ -62,9 +61,9 @@ def process host.show_message(error, MessageTypes::ERROR) if params['verbose'] end set_result({ - installed: current, - available: available - }) + installed: current, + available: available + }) end private diff --git a/lib/solargraph/language_server/message/extended/document_gems.rb b/lib/solargraph/language_server/message/extended/document_gems.rb index f1cfca0b8..403632390 100644 --- a/lib/solargraph/language_server/message/extended/document_gems.rb +++ b/lib/solargraph/language_server/message/extended/document_gems.rb @@ -13,16 +13,16 @@ class DocumentGems < Base def process cmd = [host.command_path, 'gems'] cmd.push '--rebuild' if params['rebuild'] - o, s = Open3.capture2(*cmd) - if s != 0 - host.show_message "An error occurred while building gem documentation.", LanguageServer::MessageTypes::ERROR + _, s = Open3.capture2(*cmd) + if s == 0 set_result({ - status: 'err' - }) + status: 'ok' + }) else + host.show_message 'An error occurred while building gem documentation.', LanguageServer::MessageTypes::ERROR set_result({ - status: 'ok' - }) + status: 'err' + }) end end end diff --git a/lib/solargraph/language_server/message/extended/download_core.rb b/lib/solargraph/language_server/message/extended/download_core.rb index 7310757a1..4e0775c14 100644 --- a/lib/solargraph/language_server/message/extended/download_core.rb +++ b/lib/solargraph/language_server/message/extended/download_core.rb @@ -10,7 +10,8 @@ module Extended # class DownloadCore < Base def process - host.show_message "Downloading cores is deprecated. Solargraph currently uses RBS for core and stdlib documentation", LanguageServer::MessageTypes::INFO + host.show_message 'Downloading cores is deprecated. Solargraph currently uses RBS for core and stdlib documentation', + LanguageServer::MessageTypes::INFO end end end diff --git a/lib/solargraph/language_server/message/extended/search.rb b/lib/solargraph/language_server/message/extended/search.rb index 1f09a8890..312c048bb 100644 --- a/lib/solargraph/language_server/message/extended/search.rb +++ b/lib/solargraph/language_server/message/extended/search.rb @@ -8,7 +8,7 @@ class Search < Base def process results = host.search(params['query']) page = Solargraph::Page.new(host.options['viewsPath']) - content = page.render('search', layout: true, locals: {query: params['query'], results: results}) + content = page.render('search', layout: true, locals: { query: params['query'], results: results }) set_result( content: content ) diff --git a/lib/solargraph/language_server/message/initialize.rb b/lib/solargraph/language_server/message/initialize.rb index 3f3e1338b..f0f54f22e 100644 --- a/lib/solargraph/language_server/message/initialize.rb +++ b/lib/solargraph/language_server/message/initialize.rb @@ -26,20 +26,27 @@ def process } } # FIXME: lsp default is utf-16, may have different position - result[:capabilities][:positionEncoding] = "utf-32" if params.dig("capabilities", "general", "positionEncodings")&.include?("utf-32") + result[:capabilities][:positionEncoding] = 'utf-32' if params.dig('capabilities', 'general', + 'positionEncodings')&.include?('utf-32') result[:capabilities].merge! static_completion unless dynamic_registration_for?('textDocument', 'completion') - result[:capabilities].merge! static_signature_help unless dynamic_registration_for?('textDocument', 'signatureHelp') + result[:capabilities].merge! static_signature_help unless dynamic_registration_for?('textDocument', + 'signatureHelp') # result[:capabilities].merge! static_on_type_formatting unless dynamic_registration_for?('textDocument', 'onTypeFormatting') result[:capabilities].merge! static_hover unless dynamic_registration_for?('textDocument', 'hover') - result[:capabilities].merge! static_document_formatting unless dynamic_registration_for?('textDocument', 'formatting') - result[:capabilities].merge! static_document_symbols unless dynamic_registration_for?('textDocument', 'documentSymbol') + result[:capabilities].merge! static_document_formatting unless dynamic_registration_for?('textDocument', + 'formatting') + result[:capabilities].merge! static_document_symbols unless dynamic_registration_for?('textDocument', + 'documentSymbol') result[:capabilities].merge! static_definitions unless dynamic_registration_for?('textDocument', 'definition') - result[:capabilities].merge! static_type_definitions unless dynamic_registration_for?('textDocument', 'typeDefinition') + result[:capabilities].merge! static_type_definitions unless dynamic_registration_for?('textDocument', + 'typeDefinition') result[:capabilities].merge! static_rename unless dynamic_registration_for?('textDocument', 'rename') result[:capabilities].merge! static_references unless dynamic_registration_for?('textDocument', 'references') result[:capabilities].merge! static_workspace_symbols unless dynamic_registration_for?('workspace', 'symbol') - result[:capabilities].merge! static_folding_range unless dynamic_registration_for?('textDocument', 'foldingRange') - result[:capabilities].merge! static_highlights unless dynamic_registration_for?('textDocument', 'documentHighlight') + result[:capabilities].merge! static_folding_range unless dynamic_registration_for?('textDocument', + 'foldingRange') + result[:capabilities].merge! static_highlights unless dynamic_registration_for?('textDocument', + 'documentHighlight') # @todo Temporarily disabled # result[:capabilities].merge! static_code_action unless dynamic_registration_for?('textDocument', 'codeAction') set_result result @@ -71,7 +78,7 @@ def static_completion def static_code_action { codeActionProvider: true, - codeActionKinds: ["quickfix"] + codeActionKinds: ['quickfix'] } end @@ -144,11 +151,10 @@ def static_type_definitions # @return [Hash{Symbol => Hash{Symbol => Boolean}}] def static_rename { - renameProvider: {prepareProvider: true} + renameProvider: { prepareProvider: true } } end - # @return [Hash{Symbol => Boolean}] def static_references return {} unless host.options['references'] @@ -178,10 +184,10 @@ def static_highlights # enforce strict true/false-ness # @sg-ignore def dynamic_registration_for? section, capability - result = (params['capabilities'] && - params['capabilities'][section] && - params['capabilities'][section][capability] && - params['capabilities'][section][capability]['dynamicRegistration']) + result = params['capabilities'] && + params['capabilities'][section] && + params['capabilities'][section][capability] && + params['capabilities'][section][capability]['dynamicRegistration'] host.allow_registration("#{section}/#{capability}") if result result end diff --git a/lib/solargraph/language_server/message/text_document/completion.rb b/lib/solargraph/language_server/message/text_document/completion.rb index 5b9acec33..d1ec366fc 100644 --- a/lib/solargraph/language_server/message/text_document/completion.rb +++ b/lib/solargraph/language_server/message/text_document/completion.rb @@ -6,7 +6,10 @@ module Message module TextDocument class Completion < Base def process - return set_error(ErrorCodes::REQUEST_CANCELLED, "cancelled by so many request") if host.has_pending_completions? + if host.has_pending_completions? + return set_error(ErrorCodes::REQUEST_CANCELLED, + 'cancelled by so many request') + end line = params['position']['line'] col = params['position']['character'] @@ -19,12 +22,12 @@ def process completion.pins.each do |pin| idx += 1 if last_context != pin.context items.push pin.completion_item.merge({ - textEdit: { - range: completion.range.to_hash, - newText: pin.name.sub(/=$/, ' = ').sub(/:$/, ': ') - }, - sortText: "#{idx.to_s.rjust(4, '0')}#{pin.name}" - }) + textEdit: { + range: completion.range.to_hash, + newText: pin.name.sub(/=$/, ' = ').sub(/:$/, ': ') + }, + sortText: "#{idx.to_s.rjust(4, '0')}#{pin.name}" + }) items.last[:data][:uri] = params['textDocument']['uri'] last_context = pin.context end @@ -32,7 +35,7 @@ def process isIncomplete: false, items: items ) - rescue InvalidOffsetError => e + rescue InvalidOffsetError Logging.logger.info "Completion ignored invalid offset: #{params['textDocument']['uri']}, line #{line}, character #{col}" set_result empty_result end diff --git a/lib/solargraph/language_server/message/text_document/definition.rb b/lib/solargraph/language_server/message/text_document/definition.rb index 96c1e988a..f88583f48 100644 --- a/lib/solargraph/language_server/message/text_document/definition.rb +++ b/lib/solargraph/language_server/message/text_document/definition.rb @@ -28,7 +28,8 @@ def code_location def require_location # @todo Terrible hack lib = host.library_for(params['textDocument']['uri']) - rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']), Solargraph::Range.from_to(@line, @column, @line, @column)) + rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']), + Solargraph::Range.from_to(@line, @column, @line, @column)) dloc = lib.locate_ref(rloc) return nil if dloc.nil? [ diff --git a/lib/solargraph/language_server/message/text_document/document_highlight.rb b/lib/solargraph/language_server/message/text_document/document_highlight.rb index 2aded7641..a0541762a 100644 --- a/lib/solargraph/language_server/message/text_document/document_highlight.rb +++ b/lib/solargraph/language_server/message/text_document/document_highlight.rb @@ -3,7 +3,8 @@ module Solargraph::LanguageServer::Message::TextDocument class DocumentHighlight < Base def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true, only: true) + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character'], strip: true, only: true) result = locs.map do |loc| { range: loc.range.to_hash, diff --git a/lib/solargraph/language_server/message/text_document/formatting.rb b/lib/solargraph/language_server/message/text_document/formatting.rb index 7010de741..c6cc3353a 100644 --- a/lib/solargraph/language_server/message/text_document/formatting.rb +++ b/lib/solargraph/language_server/message/text_document/formatting.rb @@ -43,7 +43,7 @@ def process # @param corrections [String] # @return [void] - def log_corrections(corrections) + def log_corrections corrections corrections = corrections&.strip return if corrections&.empty? @@ -56,7 +56,7 @@ def log_corrections(corrections) # @param file_uri [String] # @return [Hash{String => undefined}] - def config_for(file_uri) + def config_for file_uri conf = host.formatter_config(file_uri) return {} unless conf.is_a?(Hash) @@ -71,10 +71,10 @@ def cli_args file_uri, config args = [ config['cops'] == 'all' ? '-A' : '-a', '--cache', 'false', - '--format', formatter_class(config).name, + '--format', formatter_class(config).name ] - ['except', 'only'].each do |arg| + %w[except only].each do |arg| cops = cop_list(config[arg]) args += ["--#{arg}", cops] if cops end @@ -86,7 +86,7 @@ def cli_args file_uri, config # @param config [Hash{String => String}] # @sg-ignore # @return [Class] - def formatter_class(config) + def formatter_class config if self.class.const_defined?('BlankRubocopFormatter') # @sg-ignore BlankRubocopFormatter @@ -100,7 +100,7 @@ def formatter_class(config) # @param value [Array, String] # # @return [String, nil] - def cop_list(value) + def cop_list value # @type [String] # @sg-ignore Translate to something flow sensitive typing understands value = value.join(',') if value.respond_to?(:join) diff --git a/lib/solargraph/language_server/message/text_document/hover.rb b/lib/solargraph/language_server/message/text_document/hover.rb index 57a9161a3..379403258 100644 --- a/lib/solargraph/language_server/message/text_document/hover.rb +++ b/lib/solargraph/language_server/message/text_document/hover.rb @@ -15,9 +15,7 @@ def process suggestions.each do |pin| parts = [] this_link = host.options['enablePages'] ? pin.link_documentation : pin.text_documentation - if !this_link.nil? && this_link != last_link - parts.push this_link - end + parts.push this_link if !this_link.nil? && this_link != last_link parts.push "`#{pin.detail}`" unless pin.is_a?(Pin::Namespace) || pin.detail.nil? parts.push pin.documentation unless pin.documentation.nil? || pin.documentation.empty? unless parts.empty? @@ -43,8 +41,8 @@ def process # @return [Hash{Symbol => Hash{Symbol => String}}, nil] def contents_or_nil contents stripped = contents - .map(&:strip) - .reject { |c| c.empty? } + .map(&:strip) + .reject { |c| c.empty? } return nil if stripped.empty? { contents: { diff --git a/lib/solargraph/language_server/message/text_document/prepare_rename.rb b/lib/solargraph/language_server/message/text_document/prepare_rename.rb index 4a37410dd..770d126ad 100644 --- a/lib/solargraph/language_server/message/text_document/prepare_rename.rb +++ b/lib/solargraph/language_server/message/text_document/prepare_rename.rb @@ -5,7 +5,8 @@ class PrepareRename < Base def process line = params['position']['line'] col = params['position']['character'] - set_result host.sources.find(params['textDocument']['uri']).cursor_at(Solargraph::Position.new(line, col)).range.to_hash + set_result host.sources.find(params['textDocument']['uri']).cursor_at(Solargraph::Position.new(line, + col)).range.to_hash end end end diff --git a/lib/solargraph/language_server/message/text_document/references.rb b/lib/solargraph/language_server/message/text_document/references.rb index 2de266214..08ae35c9d 100644 --- a/lib/solargraph/language_server/message/text_document/references.rb +++ b/lib/solargraph/language_server/message/text_document/references.rb @@ -3,7 +3,8 @@ module Solargraph::LanguageServer::Message::TextDocument class References < Base def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character']) + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character']) result = locs.map do |loc| { uri: file_to_uri(loc.filename), diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index 997e9595b..66bdce274 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -3,15 +3,16 @@ module Solargraph::LanguageServer::Message::TextDocument class Rename < Base def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true) + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character'], strip: true) changes = {} locs.each do |loc| uri = file_to_uri(loc.filename) changes[uri] ||= [] changes[uri].push({ - range: loc.range.to_hash, - newText: params['newName'] - }) + range: loc.range.to_hash, + newText: params['newName'] + }) end set_result changes: changes end diff --git a/lib/solargraph/language_server/message/text_document/signature_help.rb b/lib/solargraph/language_server/message/text_document/signature_help.rb index a56b56edd..5a460b205 100644 --- a/lib/solargraph/language_server/message/text_document/signature_help.rb +++ b/lib/solargraph/language_server/message/text_document/signature_help.rb @@ -10,8 +10,8 @@ def process col = params['position']['character'] suggestions = host.signatures_at(params['textDocument']['uri'], line, col) set_result({ - signatures: suggestions.flat_map { |pin| pin.signature_help } - }) + signatures: suggestions.flat_map { |pin| pin.signature_help } + }) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here diff --git a/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb b/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb index 32b2c1c50..133cf883d 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb @@ -26,12 +26,13 @@ def process to_delete << change['uri'] need_catalog = true else - set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}" + set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, + "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}" end end - host.create *to_create - host.delete *to_delete + host.create(*to_create) + host.delete(*to_delete) # Force host to catalog libraries after file changes (see castwide/solargraph#139) host.catalog if need_catalog diff --git a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb index e1e83fc1e..8f93c02e7 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb @@ -18,7 +18,7 @@ def add_folders # @return [void] def remove_folders return unless params['event'] && params['event']['removed'] - params['event']['removed'].each do |folder| + params['event']['removed'].each do |_folder| host.remove_folders params['event']['removed'] end end diff --git a/lib/solargraph/language_server/transport/data_reader.rb b/lib/solargraph/language_server/transport/data_reader.rb index 3fc3ed311..c24118f03 100644 --- a/lib/solargraph/language_server/transport/data_reader.rb +++ b/lib/solargraph/language_server/transport/data_reader.rb @@ -33,8 +33,8 @@ def receive data @buffer.concat char if @in_header prepare_to_parse_message if @buffer.end_with?("\r\n\r\n") - else - parse_message_from_buffer if @buffer.bytesize == @content_length + elsif @buffer.bytesize == @content_length + parse_message_from_buffer end end end @@ -56,17 +56,15 @@ def prepare_to_parse_message # @return [void] def parse_message_from_buffer - begin - msg = JSON.parse(@buffer) - @message_handler.call msg unless @message_handler.nil? - rescue JSON::ParserError => e - Solargraph::Logging.logger.warn "Failed to parse request: #{e.message}" - Solargraph::Logging.logger.debug "Buffer: #{@buffer}" - ensure - @buffer.clear - @in_header = true - @content_length = 0 - end + msg = JSON.parse(@buffer) + @message_handler.call msg unless @message_handler.nil? + rescue JSON::ParserError => e + Solargraph::Logging.logger.warn "Failed to parse request: #{e.message}" + Solargraph::Logging.logger.debug "Buffer: #{@buffer}" + ensure + @buffer.clear + @in_header = true + @content_length = 0 end end end diff --git a/lib/solargraph/language_server/uri_helpers.rb b/lib/solargraph/language_server/uri_helpers.rb index c7e55afb8..fcf2e0dcb 100644 --- a/lib/solargraph/language_server/uri_helpers.rb +++ b/lib/solargraph/language_server/uri_helpers.rb @@ -14,7 +14,7 @@ module UriHelpers # @param uri [String] # @return [String] def uri_to_file uri - decode(uri).sub(/^file\:(?:\/\/)?/, '').sub(/^\/([a-z]\:)/i, '\1') + decode(uri).sub(%r{^file:(?://)?}, '').sub(%r{^/([a-z]:)}i, '\1') end # Convert a file path to a URI. @@ -22,7 +22,7 @@ def uri_to_file uri # @param file [String] # @return [String] def file_to_uri file - "file://#{encode(file.gsub(/^([a-z]\:)/i, '/\1'))}" + "file://#{encode(file.gsub(/^([a-z]:)/i, '/\1'))}" end # Encode text to be used as a URI path component in LSP. diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 019d8d91c..85d20a465 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -127,8 +127,8 @@ def create filename, text # @return [Boolean] True if at least one file was added to the workspace. def create_from_disk *filenames sources = filenames - .reject { |filename| File.directory?(filename) || !File.exist?(filename) } - .map { |filename| Solargraph::Source.load_string(File.read(filename), filename) } + .reject { |filename| File.directory?(filename) || !File.exist?(filename) } + .map { |filename| Solargraph::Source.load_string(File.read(filename), filename) } result = workspace.merge(*sources) sources.each { |source| maybe_map source } result @@ -195,7 +195,7 @@ def definitions_at filename, line, column offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column)) # @sg-ignore Need to add nil check here # @type [MatchData, nil] - lft = source.code[0..offset-1].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i) + lft = source.code[0..offset - 1].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i) # @sg-ignore Need to add nil check here # @type [MatchData, nil] rgt = source.code[offset..-1].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i) @@ -264,10 +264,10 @@ def references_from filename, line, column, strip: false, only: false return [] unless pin result = [] files = if only - [api_map.source_map(filename)] - else - (workspace.sources + (@current ? [@current] : [])) - end + [api_map.source_map(filename)] + else + (workspace.sources + (@current ? [@current] : [])) + end files.uniq(&:filename).each do |source| found = source.references(pin.name) found.select! do |loc| @@ -329,8 +329,8 @@ def locate_ref location end workspace.require_paths.each do |path| full = File.join path, pin.name - return_if_match.(full) - return_if_match.(full << ".rb") + return_if_match.call(full) + return_if_match.call(full << '.rb') end nil rescue FileNotFoundError @@ -546,12 +546,12 @@ def find_external_requires source_map new_set = source_map.requires.map(&:name).to_set # return if new_set == source_map_external_require_hash[source_map.filename] _filenames = nil - filenames = ->{ _filenames ||= workspace.filenames.to_set } + filenames = -> { _filenames ||= workspace.filenames.to_set } # @sg-ignore Need to add nil check here source_map_external_require_hash[source_map.filename] = new_set.reject do |path| workspace.require_paths.any? do |base| full = File.join(base, path) - filenames[].include?(full) or filenames[].include?(full << ".rb") + filenames[].include?(full) or filenames[].include?(full << '.rb') end end @external_requires = nil @@ -585,12 +585,9 @@ def read filename # @param error [FileNotFoundError] # @return [nil] def handle_file_not_found filename, error - if workspace.source(filename) - Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap" - nil - else - raise error - end + raise error unless workspace.source(filename) + Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap" + nil end # @param source [Source, nil] @@ -679,12 +676,12 @@ def report_cache_progress gem_name, pending finished = @total - pending # @sg-ignore flow sensitive typing needs better handling of ||= on lvars pct = if @total.zero? - 0 - else - # @sg-ignore flow sensitive typing needs better handling of ||= on lvars - ((finished.to_f / @total.to_f) * 100).to_i - end - message = "#{gem_name}#{pending > 0 ? " (+#{pending})" : ''}" + 0 + else + # @sg-ignore flow sensitive typing needs better handling of ||= on lvars + ((finished.to_f / @total.to_f) * 100).to_i + end + message = "#{gem_name}#{" (+#{pending})" if pending > 0}" # " if @cache_progress @cache_progress.report(message, pct) diff --git a/lib/solargraph/location.rb b/lib/solargraph/location.rb index a7b15e42f..686b072aa 100644 --- a/lib/solargraph/location.rb +++ b/lib/solargraph/location.rb @@ -17,7 +17,7 @@ class Location # @param filename [String, nil] # @param range [Solargraph::Range] def initialize filename, range - raise "Use nil to represent no-file" if filename&.empty? + raise 'Use nil to represent no-file' if filename&.empty? @filename = filename @range = range @@ -28,7 +28,7 @@ def initialize filename, range end # @param other [self] - def <=>(other) + def <=> other return nil unless other.is_a?(Location) if filename == other.filename range <=> other.range @@ -64,14 +64,14 @@ def to_hash # @param node [Parser::AST::Node, nil] # @return [Location, nil] - def self.from_node(node) + def self.from_node node return nil if node.nil? || node.loc.nil? filename = node.loc.expression.source_buffer.name # @sg-ignore flow sensitive typing needs to create separate ranges for postfix if filename = nil if filename.empty? range = Range.from_node(node) # @sg-ignore Need to add nil check here - self.new(filename, range) + new(filename, range) end # @param other [BasicObject] diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 30bdc06c2..60ca51f17 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -23,7 +23,7 @@ module Logging end @@logger = Logger.new(STDERR, level: level) # @sg-ignore Fix cvar issue - @@logger.formatter = proc do |severity, datetime, progname, msg| + @@logger.formatter = proc do |severity, _datetime, _progname, msg| "[#{severity}] #{msg}\n" end diff --git a/lib/solargraph/parser/comment_ripper.rb b/lib/solargraph/parser/comment_ripper.rb index 8bfe166f5..41e68e744 100644 --- a/lib/solargraph/parser/comment_ripper.rb +++ b/lib/solargraph/parser/comment_ripper.rb @@ -26,19 +26,25 @@ def on_comment *args # @sg-ignore Need to add nil check here if @buffer_lines[result[2][0]][0..result[2][1]].strip =~ /^#/ chomped = result[1].chomp - if result[2][0] == 0 && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '').match(/^#\s*frozen_string_literal:/) + if result[2][0] == 0 && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, + replace: '').match(/^#\s*frozen_string_literal:/) chomped = '#' end - @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped) + @comments[result[2][0]] = + Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped) end result end # @param result [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))] # @return [void] - def create_snippet(result) + def create_snippet result chomped = result[1].chomp - @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0] || 0, result[2][1] || 0, result[2][0] || 0, (result[2][1] || 0) + chomped.length), chomped) + @comments[result[2][0]] = + Snippet.new( + Range.from_to(result[2][0] || 0, result[2][1] || 0, result[2][0] || 0, + (result[2][1] || 0) + chomped.length), chomped + ) end # @sg-ignore @override is adding, not overriding diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index e73a46cb2..26fb646b4 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -7,7 +7,7 @@ class FlowSensitiveTyping # @param ivars [Array] # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil] # @param enclosing_compound_statement_pin [Solargraph::Pin::CompoundStatement, nil] - def initialize(locals, ivars, enclosing_breakable_pin, enclosing_compound_statement_pin) + def initialize locals, ivars, enclosing_breakable_pin, enclosing_compound_statement_pin @locals = locals @ivars = ivars @enclosing_breakable_pin = enclosing_breakable_pin @@ -19,7 +19,7 @@ def initialize(locals, ivars, enclosing_breakable_pin, enclosing_compound_statem # @param false_ranges [Array] # # @return [void] - def process_and(and_node, true_ranges = [], false_ranges = []) + def process_and and_node, true_ranges = [], false_ranges = [] return unless and_node.type == :and # @type [Parser::AST::Node] @@ -45,7 +45,7 @@ def process_and(and_node, true_ranges = [], false_ranges = []) # @param false_ranges [Array] # # @return [void] - def process_or(or_node, true_ranges = [], false_ranges = []) + def process_or or_node, true_ranges = [], false_ranges = [] return unless or_node.type == :or # @type [Parser::AST::Node] @@ -74,7 +74,7 @@ def process_or(or_node, true_ranges = [], false_ranges = []) # @param false_presences [Array] # # @return [void] - def process_calls(node, true_presences, false_presences) + def process_calls node, true_presences, false_presences return unless node.type == :send process_isa(node, true_presences, false_presences) @@ -87,7 +87,7 @@ def process_calls(node, true_presences, false_presences) # @param false_ranges [Array] # # @return [void] - def process_if(if_node, true_ranges = [], false_ranges = []) + def process_if if_node, true_ranges = [], false_ranges = [] return if if_node.type != :if # @@ -111,13 +111,9 @@ def process_if(if_node, true_ranges = [], false_ranges = []) rest_of_breakable_body = Range.new(get_node_end_position(if_node), get_node_end_position(enclosing_breakable_pin.node)) - if always_breaks?(then_clause) - false_ranges << rest_of_breakable_body - end + false_ranges << rest_of_breakable_body if always_breaks?(then_clause) - if always_breaks?(else_clause) - true_ranges << rest_of_breakable_body - end + true_ranges << rest_of_breakable_body if always_breaks?(else_clause) end unless enclosing_compound_statement_pin.node.nil? @@ -129,13 +125,9 @@ def process_if(if_node, true_ranges = [], false_ranges = []) # statement, we can assume things about the rest of the # compound statement # - if always_leaves_compound_statement?(then_clause) - false_ranges << rest_of_returnable_body - end + false_ranges << rest_of_returnable_body if always_leaves_compound_statement?(then_clause) - if always_leaves_compound_statement?(else_clause) - true_ranges << rest_of_returnable_body - end + true_ranges << rest_of_returnable_body if always_leaves_compound_statement?(else_clause) end unless then_clause.nil? @@ -166,7 +158,7 @@ def process_if(if_node, true_ranges = [], false_ranges = []) # @param false_ranges [Array] # # @return [void] - def process_while(while_node, true_ranges = [], false_ranges = []) + def process_while while_node, true_ranges = [], false_ranges = [] return if while_node.type != :while # @@ -210,7 +202,7 @@ class << self # @param downcast_not_type [ComplexType, nil] # # @return [void] - def add_downcast_var(pin, presence:, downcast_type:, downcast_not_type:) + def add_downcast_var pin, presence:, downcast_type:, downcast_not_type: new_pin = pin.downcast(exclude_return_type: downcast_not_type, intersection_return_type: downcast_type, source: :flow_sensitive_typing, @@ -228,7 +220,7 @@ def add_downcast_var(pin, presence:, downcast_type:, downcast_not_type:) # @param presences [Array] # # @return [void] - def process_facts(facts_by_pin, presences) + def process_facts facts_by_pin, presences # # Add specialized vars for the rest of the block # @@ -251,7 +243,7 @@ def process_facts(facts_by_pin, presences) # @param false_ranges [Array] # # @return [void] - def process_expression(expression_node, true_ranges, false_ranges) + def process_expression expression_node, true_ranges, false_ranges process_calls(expression_node, true_ranges, false_ranges) process_and(expression_node, true_ranges, false_ranges) process_or(expression_node, true_ranges, false_ranges) @@ -263,7 +255,7 @@ def process_expression(expression_node, true_ranges, false_ranges) # @return [Array(String, String), nil] Tuple of rgument to # function, then receiver of function if it's a variable, # otherwise nil if no simple variable receiver - def parse_call(call_node, method_name) + def parse_call call_node, method_name return unless call_node&.type == :send && call_node.children[1] == method_name # Check if conditional node follows this pattern: # s(:send, @@ -282,7 +274,7 @@ def parse_call(call_node, method_name) # or like this: # (lvar :repr) # @sg-ignore Need to look at Tuple#include? handling - variable_name = call_receiver.children[0].to_s if [:lvar, :ivar].include?(call_receiver&.type) + variable_name = call_receiver.children[0].to_s if %i[lvar ivar].include?(call_receiver&.type) return unless variable_name [call_arg, variable_name] @@ -290,7 +282,7 @@ def parse_call(call_node, method_name) # @param isa_node [Parser::AST::Node] # @return [Array(String, String), nil] - def parse_isa(isa_node) + def parse_isa isa_node call_type_name, variable_name = parse_call(isa_node, :is_a?) return unless call_type_name @@ -304,7 +296,7 @@ def parse_isa(isa_node) # @sg-ignore Solargraph::Parser::FlowSensitiveTyping#find_var # return type could not be inferred # @return [Solargraph::Pin::LocalVariable, Solargraph::Pin::InstanceVariable, nil] - def find_var(variable_name, position) + def find_var variable_name, position if variable_name.start_with?('@') # @sg-ignore flow sensitive typing needs to handle attrs ivars.find { |ivar| ivar.name == variable_name && (!ivar.presence || ivar.presence.include?(position)) } @@ -319,7 +311,7 @@ def find_var(variable_name, position) # @param false_presences [Array] # # @return [void] - def process_isa(isa_node, true_presences, false_presences) + def process_isa isa_node, true_presences, false_presences isa_type_name, variable_name = parse_isa(isa_node) return if variable_name.nil? || variable_name.empty? # @sg-ignore Need to add nil check here @@ -343,7 +335,7 @@ def process_isa(isa_node, true_presences, false_presences) # @param nilp_node [Parser::AST::Node] # @return [Array(String, String), nil] - def parse_nilp(nilp_node) + def parse_nilp nilp_node parse_call(nilp_node, :nil?) end @@ -352,7 +344,7 @@ def parse_nilp(nilp_node) # @param false_presences [Array] # # @return [void] - def process_nilp(nilp_node, true_presences, false_presences) + def process_nilp nilp_node, true_presences, false_presences nilp_arg, variable_name = parse_nilp(nilp_node) return if variable_name.nil? || variable_name.empty? # if .nil? got an argument, move on, this isn't the situation @@ -380,7 +372,7 @@ def process_nilp(nilp_node, true_presences, false_presences) # @param bang_node [Parser::AST::Node] # @return [Array(String, String), nil] - def parse_bang(bang_node) + def parse_bang bang_node parse_call(bang_node, :!) end @@ -389,7 +381,7 @@ def parse_bang(bang_node) # @param false_presences [Array] # # @return [void] - def process_bang(bang_node, true_presences, false_presences) + def process_bang bang_node, true_presences, false_presences # pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("!2") # => s(:send, # s(:int, 2), :!) @@ -405,7 +397,7 @@ def process_bang(bang_node, true_presences, false_presences) # @param var_node [Parser::AST::Node] # # @return [String, nil] Variable name referenced - def parse_variable(var_node) + def parse_variable var_node return if var_node.children.length != 1 var_node.children[0]&.to_s @@ -415,8 +407,8 @@ def parse_variable(var_node) # @param node [Parser::AST::Node] # @param true_presences [Array] # @param false_presences [Array] - def process_variable(node, true_presences, false_presences) - return unless [:lvar, :ivar, :cvar, :gvar].include?(node.type) + def process_variable node, true_presences, false_presences + return unless %i[lvar ivar cvar gvar].include?(node.type) variable_name = parse_variable(node) return if variable_name.nil? @@ -443,7 +435,7 @@ def process_variable(node, true_presences, false_presences) # @param node [Parser::AST::Node] # # @return [String, nil] - def type_name(node) + def type_name node # e.g., # s(:const, nil, :Baz) return unless node&.type == :const @@ -462,22 +454,18 @@ def type_name(node) # @param clause_node [Parser::AST::Node, nil] # @sg-ignore need boolish support for ? methods - def always_breaks?(clause_node) + def always_breaks? clause_node clause_node&.type == :break end # @param clause_node [Parser::AST::Node, nil] - def always_leaves_compound_statement?(clause_node) + def always_leaves_compound_statement? clause_node # https://docs.ruby-lang.org/en/2.2.0/keywords_rdoc.html # @sg-ignore Need to look at Tuple#include? handling - [:return, :raise, :next, :redo, :retry].include?(clause_node&.type) + %i[return raise next redo retry].include?(clause_node&.type) end - attr_reader :locals - - attr_reader :ivars - - attr_reader :enclosing_breakable_pin, :enclosing_compound_statement_pin + attr_reader :locals, :ivars, :enclosing_breakable_pin, :enclosing_compound_statement_pin end end end diff --git a/lib/solargraph/parser/node_processor.rb b/lib/solargraph/parser/node_processor.rb index a43fb326c..d3579df80 100644 --- a/lib/solargraph/parser/node_processor.rb +++ b/lib/solargraph/parser/node_processor.rb @@ -43,7 +43,7 @@ def self.process node, region = Region.new, pins = [], locals = [], ivars = [] pins.push Pin::Namespace.new( location: region.source.location, name: '', - source: :parser, + source: :parser ) end return [pins, locals, ivars] unless Parser.is_ast_node?(node) diff --git a/lib/solargraph/parser/node_processor/base.rb b/lib/solargraph/parser/node_processor/base.rb index e11078e07..48fd070f6 100644 --- a/lib/solargraph/parser/node_processor/base.rb +++ b/lib/solargraph/parser/node_processor/base.rb @@ -58,13 +58,13 @@ def position # @sg-ignore downcast output of Enumerable#select # @return [Solargraph::Pin::Breakable, nil] def enclosing_breakable_pin - pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location&.range&.contain?(position)}.last + pins.select { |pin| pin.is_a?(Pin::Breakable) && pin.location&.range&.contain?(position) }.last end # @todo downcast output of Enumerable#select # @return [Solargraph::Pin::CompoundStatement, nil] def enclosing_compound_statement_pin - pins.select{|pin| pin.is_a?(Pin::CompoundStatement) && pin.location&.range&.contain?(position)}.last + pins.select { |pin| pin.is_a?(Pin::CompoundStatement) && pin.location&.range&.contain?(position) }.last end # @param subregion [Region] @@ -80,14 +80,14 @@ def process_children subregion = region # @param node [Parser::AST::Node] # @return [Solargraph::Location] - def get_node_location(node) + def get_node_location node range = Parser.node_range(node) Location.new(region.filename, range) end # @param node [Parser::AST::Node] # @return [String, nil] - def comments_for(node) + def comments_for node region.source.comments_for(node) end diff --git a/lib/solargraph/parser/parser_gem/class_methods.rb b/lib/solargraph/parser/parser_gem/class_methods.rb index 3f70d14df..dfaf7925a 100644 --- a/lib/solargraph/parser/parser_gem/class_methods.rb +++ b/lib/solargraph/parser/parser_gem/class_methods.rb @@ -54,7 +54,7 @@ def map source # @param name [String] # @return [Array] def references source, name - if name.end_with?("=") + if name.end_with?('=') reg = /#{Regexp.escape name[0..-2]}\s*=/ # @param code [String] # @param offset [Integer] @@ -98,17 +98,17 @@ def inner_node_references name, top # @return [Source::Chain] def chain *args - NodeChainer.chain *args + NodeChainer.chain(*args) end # @return [Source::Chain] def chain_string *args - NodeChainer.load_string *args + NodeChainer.load_string(*args) end # @return [Array(Array, Array)] def process_node *args - Solargraph::Parser::NodeProcessor.process *args + Solargraph::Parser::NodeProcessor.process(*args) end # @param node [Parser::AST::Node, nil] diff --git a/lib/solargraph/parser/parser_gem/flawed_builder.rb b/lib/solargraph/parser/parser_gem/flawed_builder.rb index acf665e16..341042f22 100644 --- a/lib/solargraph/parser/parser_gem/flawed_builder.rb +++ b/lib/solargraph/parser/parser_gem/flawed_builder.rb @@ -10,7 +10,7 @@ class FlawedBuilder < ::Parser::Builders::Default # @param token [::Parser::AST::Node] # @return [String] # @sg-ignore - def string_value(token) + def string_value token value(token) end end diff --git a/lib/solargraph/parser/parser_gem/node_chainer.rb b/lib/solargraph/parser/parser_gem/node_chainer.rb index bc04c2855..5803424d0 100644 --- a/lib/solargraph/parser/parser_gem/node_chainer.rb +++ b/lib/solargraph/parser/parser_gem/node_chainer.rb @@ -22,7 +22,7 @@ def initialize node, filename = nil, parent = nil # @return [Source::Chain] def chain links = generate_links(@node) - Chain.new(links, @node, (Parser.is_ast_node?(@node) && @node.type == :splat)) + Chain.new(links, @node, Parser.is_ast_node?(@node) && @node.type == :splat) end class << self @@ -39,7 +39,7 @@ def chain node, filename = nil, parent = nil # @param starting_line [Integer] # # @return [Source::Chain] - def load_string(code, filename, starting_line) + def load_string code, filename, starting_line node = Parser.parse(code.sub(/\.$/, ''), filename, starting_line) chain = NodeChainer.new(node).chain chain.links.push(Chain::Link.new) if code.end_with?('.') @@ -94,13 +94,13 @@ def generate_links n elsif n.type == :const const = unpack_name(n) result.push Chain::Constant.new(const) - elsif [:lvar, :lvasgn].include?(n.type) + elsif %i[lvar lvasgn].include?(n.type) result.push Chain::Call.new(n.children[0].to_s, Location.from_node(n)) - elsif [:ivar, :ivasgn].include?(n.type) + elsif %i[ivar ivasgn].include?(n.type) result.push Chain::InstanceVariable.new(n.children[0].to_s, n, Location.from_node(n)) - elsif [:cvar, :cvasgn].include?(n.type) + elsif %i[cvar cvasgn].include?(n.type) result.push Chain::ClassVariable.new(n.children[0].to_s) - elsif [:gvar, :gvasgn].include?(n.type) + elsif %i[gvar gvasgn].include?(n.type) result.push Chain::GlobalVariable.new(n.children[0].to_s) elsif n.type == :or_asgn # @bar ||= 123 translates to: @@ -113,13 +113,14 @@ def generate_links n or_link = Chain::Or.new([lhs_chain, rhs_chain]) # this is just for a call chain, so we don't need to record the assignment result.push(or_link) - elsif [:class, :module, :def, :defs].include?(n.type) + elsif %i[class module def defs].include?(n.type) # @todo Undefined or what? result.push Chain::UNDEFINED_CALL elsif n.type == :and result.concat generate_links(n.children.last) elsif n.type == :or - result.push Chain::Or.new([NodeChainer.chain(n.children[0], @filename), NodeChainer.chain(n.children[1], @filename, n)]) + result.push Chain::Or.new([NodeChainer.chain(n.children[0], @filename), + NodeChainer.chain(n.children[1], @filename, n)]) elsif n.type == :if then_clause = if n.children[1] NodeChainer.chain(n.children[1], @filename, n) @@ -132,7 +133,7 @@ def generate_links n Source::Chain.new([Source::Chain::Literal.new('nil', nil)], n) end result.push Chain::If.new([then_clause, else_clause]) - elsif [:begin, :kwbegin].include?(n.type) + elsif %i[begin kwbegin].include?(n.type) result.concat generate_links(n.children.last) elsif n.type == :block_pass block_variable_name_node = n.children[0] @@ -140,12 +141,10 @@ def generate_links n # anonymous block forwarding (e.g., "&") # added in Ruby 3.1 - https://bugs.ruby-lang.org/issues/11256 result.push Chain::BlockVariable.new(nil) + elsif block_variable_name_node.type == :sym + result.push Chain::BlockSymbol.new("#{block_variable_name_node.children[0]}") else - if block_variable_name_node.type == :sym - result.push Chain::BlockSymbol.new("#{block_variable_name_node.children[0].to_s}") - else - result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0].to_s}") - end + result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0]}") end elsif n.type == :hash result.push Chain::Hash.new('::Hash', n, hash_is_splatted?(n)) @@ -154,7 +153,7 @@ def generate_links n result.push Source::Chain::Array.new(chained_children, n) else lit = infer_literal_node_type(n) - result.push (lit ? Chain::Literal.new(lit, n) : Chain::Link.new) + result.push(lit ? Chain::Literal.new(lit, n) : Chain::Link.new) end result end @@ -163,7 +162,9 @@ def generate_links n def hash_is_splatted? node return false unless Parser.is_ast_node?(node) && node.type == :hash return false unless Parser.is_ast_node?(node.children.last) && node.children.last.type == :kwsplat - return false if Parser.is_ast_node?(node.children.last.children[0]) && node.children.last.children[0].type == :hash + if Parser.is_ast_node?(node.children.last.children[0]) && node.children.last.children[0].type == :hash + return false + end true end diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index 9b7d94827..c73af64dc 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -12,17 +12,17 @@ module NodeMethods # @param node [Parser::AST::Node] # @return [String] - def unpack_name(node) - pack_name(node).join("::") + def unpack_name node + pack_name(node).join('::') end # @param node [Parser::AST::Node] # @return [Array] - def pack_name(node) + def pack_name node # @type [Array] parts = [] if node.is_a?(AST::Node) - node.children.each { |n| + node.children.each do |n| if n.is_a?(AST::Node) if n.type == :cbase parts = [''] + pack_name(n) @@ -32,7 +32,7 @@ def pack_name(node) else parts.push n unless n.nil? end - } + end end parts end @@ -41,7 +41,7 @@ def pack_name(node) # @return [String, nil] def infer_literal_node_type node return nil unless node.is_a?(AST::Node) - if node.type == :str || node.type == :dstr + if %i[str dstr].include?(node.type) return '::String' elsif node.type == :array return '::Array' @@ -53,30 +53,30 @@ def infer_literal_node_type node return '::Integer' elsif node.type == :float return '::Float' - elsif node.type == :sym || node.type == :dsym + elsif %i[sym dsym].include?(node.type) return '::Symbol' elsif node.type == :regexp return '::Regexp' elsif node.type == :irange return '::Range' - elsif node.type == :true || node.type == :false + elsif %i[true false].include?(node.type) return '::Boolean' # @todo Support `nil` keyword in types - # elsif node.type == :nil - # return 'NilClass' + # elsif node.type == :nil + # return 'NilClass' end nil end # @param node [Parser::AST::Node] # @return [Position] - def get_node_start_position(node) + def get_node_start_position node Position.new(node.loc.line, node.loc.column) end # @param node [Parser::AST::Node] # @return [Position] - def get_node_end_position(node) + def get_node_end_position node Position.new(node.loc.last_line, node.loc.last_column) end @@ -86,19 +86,15 @@ def get_node_end_position(node) # @return [String] def drill_signature node, signature return signature unless node.is_a?(AST::Node) - if node.type == :const or node.type == :cbase - unless node.children[0].nil? - signature += drill_signature(node.children[0], signature) - end + if %i[const cbase].include?(node.type) + signature += drill_signature(node.children[0], signature) unless node.children[0].nil? signature += '::' unless signature.empty? signature += node.children[1].to_s - elsif node.type == :lvar or node.type == :ivar or node.type == :cvar + elsif %i[lvar ivar cvar].include?(node.type) signature += '.' unless signature.empty? signature += node.children[0].to_s elsif node.type == :send - unless node.children[0].nil? - signature += drill_signature(node.children[0], signature) - end + signature += drill_signature(node.children[0], signature) unless node.children[0].nil? signature += '.' unless signature.empty? signature += node.children[1].to_s end @@ -112,7 +108,9 @@ def convert_hash node # @sg-ignore Translate to something flow sensitive typing understands return convert_hash(node.children[0]) if node.type == :kwsplat # @sg-ignore Translate to something flow sensitive typing understands - return convert_hash(node.children[0]) if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat + if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat + return convert_hash(node.children[0]) + end # @sg-ignore Translate to something flow sensitive typing understands return {} unless node.type == :hash result = {} @@ -151,7 +149,7 @@ def splatted_call? node end # @param nodes [Enumerable] - def any_splatted_call?(nodes) + def any_splatted_call? nodes nodes.any? { |n| splatted_call?(n) } end @@ -174,7 +172,7 @@ def call_nodes_from node result.concat call_nodes_from(node.children.first) # @sg-ignore Need to add nil check here node.children[2..-1].each { |child| result.concat call_nodes_from(child) } - elsif [:super, :zsuper].include?(node.type) + elsif %i[super zsuper].include?(node.type) result.push node node.children.each { |child| result.concat call_nodes_from(child) } else @@ -205,29 +203,31 @@ def returns_from_method_body node # @return [Array] low-level value nodes in # value position. Does not include explicit return # statements - def value_position_nodes_only(node) + def value_position_nodes_only node DeepInference.value_position_nodes_only(node).map { |n| n || NIL_NODE } end # @param cursor [Solargraph::Source::Cursor] # @return [Parser::AST::Node, nil] def find_recipient_node cursor - return repaired_find_recipient_node(cursor) if cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '(' + if cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '(' + return repaired_find_recipient_node(cursor) + end source = cursor.source position = cursor.position offset = cursor.offset tree = if source.synchronized? - # @sg-ignore Need to add nil check here - match = source.code[0..offset-1].match(/,\s*\z/) - if match - # @sg-ignore Need to add nil check here - source.tree_at(position.line, position.column - match[0].length) - else - source.tree_at(position.line, position.column) - end - else - source.tree_at(position.line, position.column - 1) - end + # @sg-ignore Need to add nil check here + match = source.code[0..offset - 1].match(/,\s*\z/) + if match + # @sg-ignore Need to add nil check here + source.tree_at(position.line, position.column - match[0].length) + else + source.tree_at(position.line, position.column) + end + else + source.tree_at(position.line, position.column - 1) + end # @type [AST::Node, nil] prev = nil tree.each do |node| @@ -237,12 +237,10 @@ def find_recipient_node cursor if !args.empty? # @sg-ignore Need to add nil check here return node if prev && args.include?(prev) - else - if source.synchronized? - return node if source.code[0..offset-1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/ - else - return node if source.code[0..offset-1] =~ /\([^(]*\z/ - end + elsif source.synchronized? + return node if source.code[0..offset - 1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/ + elsif source.code[0..offset - 1] =~ /\([^(]*\z/ + return node end end prev = node @@ -255,7 +253,7 @@ def find_recipient_node cursor def repaired_find_recipient_node cursor cursor = cursor.source.cursor_at([cursor.position.line, cursor.position.column - 1]) node = cursor.source.tree_at(cursor.position.line, cursor.position.column).first - return node if node && node.type == :send + node if node && node.type == :send end # @@ -312,11 +310,11 @@ def repaired_find_recipient_node cursor # statements in value positions. module DeepInference class << self - CONDITIONAL_ALL_BUT_FIRST = [:if, :unless] + CONDITIONAL_ALL_BUT_FIRST = %i[if unless] ONLY_ONE_CHILD = [:return] FIRST_TWO_CHILDREN = [:rescue] - COMPOUND_STATEMENTS = [:begin, :kwbegin] - SKIPPABLE = [:def, :defs, :class, :sclass, :module] + COMPOUND_STATEMENTS = %i[begin kwbegin] + SKIPPABLE = %i[def defs class sclass module] FUNCTION_VALUE = [:block] CASE_STATEMENT = [:case] @@ -336,7 +334,7 @@ def from_method_body node # @return [Array] low-level value nodes in # value position. Does not include explicit return # statements - def value_position_nodes_only(node) + def value_position_nodes_only node from_value_position_statement(node, include_explicit_returns: false) end @@ -370,7 +368,9 @@ def from_value_position_statement node, include_explicit_returns: true # @todo any explicit returns actually return from # scope in which the proc is run. This asssumes # that the function is executed here. - result.concat explicit_return_values_from_compound_statement(node.children[2]) if include_explicit_returns + if include_explicit_returns + result.concat explicit_return_values_from_compound_statement(node.children[2]) + end elsif CASE_STATEMENT.include?(node.type) # @sg-ignore Need to add nil check here node.children[1..-1].each do |cc| @@ -406,7 +406,7 @@ def from_value_position_statement node, include_explicit_returns: true # @return [Array] def from_value_position_compound_statement parent result = [] - nodes = parent.children.select{|n| n.is_a?(AST::Node)} + nodes = parent.children.select { |n| n.is_a?(AST::Node) } nodes.each_with_index do |node, idx| if node.type == :block result.concat explicit_return_values_from_compound_statement(node.children[2]) @@ -431,7 +431,10 @@ def from_value_position_compound_statement parent # value position. we already have the explicit values # from above; now we need to also gather the value # position nodes - result.concat from_value_position_statement(nodes.last, include_explicit_returns: false) if idx == nodes.length - 1 + if idx == nodes.length - 1 + result.concat from_value_position_statement(nodes.last, + include_explicit_returns: false) + end end result end @@ -447,7 +450,7 @@ def from_value_position_compound_statement parent def explicit_return_values_from_compound_statement parent return [] unless parent.is_a?(::Parser::AST::Node) result = [] - nodes = parent.children.select{|n| n.is_a?(::Parser::AST::Node)} + nodes = parent.children.select { |n| n.is_a?(::Parser::AST::Node) } nodes.each do |node| next if SKIPPABLE.include?(node.type) if node.type == :return diff --git a/lib/solargraph/parser/parser_gem/node_processors/args_node.rb b/lib/solargraph/parser/parser_gem/node_processors/args_node.rb index ef7630921..9a22b8edd 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/args_node.rb @@ -14,17 +14,17 @@ def process node.children.each do |u| loc = get_node_location(u) locals.push Solargraph::Pin::Parameter.new( - location: loc, - closure: callable, - comments: comments_for(node), - name: u.children[0].to_s, - assignment: u.children[1], - asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil, - # @sg-ignore Need to add nil check here - presence: callable.location.range, - decl: get_decl(u), - source: :parser - ) + location: loc, + closure: callable, + comments: comments_for(node), + name: u.children[0].to_s, + assignment: u.children[1], + asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil, + # @sg-ignore Need to add nil check here + presence: callable.location.range, + decl: get_decl(u), + source: :parser + ) callable.parameters.push locals.last end end @@ -36,7 +36,7 @@ def process # @param callable [Pin::Callable] # @return [void] - def forward(callable) + def forward callable loc = get_node_location(node) locals.push Solargraph::Pin::Parameter.new( location: loc, diff --git a/lib/solargraph/parser/parser_gem/node_processors/block_node.rb b/lib/solargraph/parser/parser_gem/node_processors/block_node.rb index b16bde064..dadc1e3ad 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/block_node.rb @@ -37,7 +37,7 @@ def other_class_eval? node.children[0].type == :send && node.children[0].children[1] == :class_eval && # @sg-ignore Need to add nil check here - [:cbase, :const].include?(node.children[0].children[0]&.type) + %i[cbase const].include?(node.children[0].children[0]&.type) end end end diff --git a/lib/solargraph/parser/parser_gem/node_processors/def_node.rb b/lib/solargraph/parser/parser_gem/node_processors/def_node.rb index 1b9fd442d..f45f5544d 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/def_node.rb @@ -21,7 +21,7 @@ def process scope: scope, visibility: scope == :instance && name == 'initialize' ? :private : region.visibility, node: node, - source: :parser, + source: :parser ) if region.visibility == :module_function pins.push Solargraph::Pin::Method.new( @@ -34,7 +34,7 @@ def process visibility: :public, parameters: methpin.parameters, node: methpin.node, - source: :parser, + source: :parser ) pins.push Solargraph::Pin::Method.new( location: methpin.location, @@ -46,7 +46,7 @@ def process visibility: :private, parameters: methpin.parameters, node: methpin.node, - source: :parser, + source: :parser ) else pins.push methpin diff --git a/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb b/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb index 5f40457e9..6c9c1d7e2 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb @@ -11,13 +11,13 @@ def process s_visi = region.visibility s_visi = :public if s_visi == :module_function || region.scope != :class loc = get_node_location(node) - if node.children[0].is_a?(AST::Node) && node.children[0].type == :self - closure = region.closure - else - closure = Solargraph::Pin::Namespace.new( - name: unpack_name(node.children[0]) - ) - end + closure = if node.children[0].is_a?(AST::Node) && node.children[0].type == :self + region.closure + else + Solargraph::Pin::Namespace.new( + name: unpack_name(node.children[0]) + ) + end pins.push Solargraph::Pin::Method.new( location: loc, closure: closure, @@ -26,7 +26,7 @@ def process scope: :class, visibility: s_visi, node: node, - source: :parser, + source: :parser ) process_children region.update(closure: pins.last, scope: :class) end diff --git a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb index bf8f95635..0b9a75e77 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/if_node.rb @@ -18,7 +18,7 @@ def process location: get_node_location(condition_node), closure: region.closure, node: condition_node, - source: :parser, + source: :parser ) NodeProcessor.process(condition_node, region, pins, locals, ivars) end @@ -28,7 +28,7 @@ def process location: get_node_location(then_node), closure: region.closure, node: then_node, - source: :parser, + source: :parser ) NodeProcessor.process(then_node, region, pins, locals, ivars) end @@ -39,7 +39,7 @@ def process location: get_node_location(else_node), closure: region.closure, node: else_node, - source: :parser, + source: :parser ) NodeProcessor.process(else_node, region, pins, locals, ivars) end diff --git a/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb b/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb index 0da611e22..0c4099d41 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb @@ -24,7 +24,8 @@ def process if named_path.is_a?(Pin::Method) ivars.push Solargraph::Pin::InstanceVariable.new( location: loc, - closure: Pin::Namespace.new(type: :module, closure: region.closure.closure, name: region.closure.name), + closure: Pin::Namespace.new(type: :module, closure: region.closure.closure, + name: region.closure.name), name: node.children[0].to_s, comments: comments_for(node), assignment: node.children[1], diff --git a/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb b/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb index ab23ffa0e..aab8106aa 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb @@ -88,7 +88,7 @@ def process_vasgn_target asgn, operator, argument argument ] send_node = node.updated(:send, send_children) - new_asgn = node.updated(asgn.type, [variable_name, send_node]) + new_asgn = node.updated(asgn.type, [variable_name, send_node]) NodeProcessor.process(new_asgn, region, pins, locals, ivars) end end diff --git a/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb b/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb index d3d2cef4f..2c8ef1c7b 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb @@ -31,9 +31,7 @@ def process elsif sclass.is_a?(::Parser::AST::Node) && sclass.type == :const names = [region.closure.namespace, region.closure.name] also = NodeMethods.unpack_name(sclass) - if also != region.closure.name - names << also - end + names << also if also != region.closure.name name = names.reject(&:empty?).join('::') closure = Solargraph::Pin::Namespace.new(name: name, location: region.closure.location, source: :parser) else @@ -42,7 +40,7 @@ def process pins.push Solargraph::Pin::Singleton.new( location: get_node_location(node), closure: closure, - source: :parser, + source: :parser ) process_children region.update(visibility: :public, scope: :class, closure: pins.last) end diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 86c762c98..81404fcbf 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -14,16 +14,17 @@ def process method_name = node.children[1] # :nocov: unless method_name.instance_of?(Symbol) - Solargraph.assert_or_log(:parser_method_name, "Expected method name to be a Symbol, got #{method_name.class} for node #{node.inspect}") + Solargraph.assert_or_log(:parser_method_name, + "Expected method name to be a Symbol, got #{method_name.class} for node #{node.inspect}") return process_children end # :nocov: if node.children[0].nil? - if [:private, :public, :protected].include?(method_name) + if %i[private public protected].include?(method_name) process_visibility elsif method_name == :module_function process_module_function - elsif [:attr_reader, :attr_writer, :attr_accessor].include?(method_name) + elsif %i[attr_reader attr_writer attr_accessor].include?(method_name) process_attribute elsif method_name == :include process_include @@ -44,7 +45,10 @@ def process return if process_private_class_method end elsif method_name == :require && node.children[0].to_s == '(const nil :Bundler)' - pins.push Pin::Reference::Require.new(Solargraph::Location.new(region.filename, Solargraph::Range.from_to(0, 0, 0, 0)), 'bundler/require', source: :parser) + pins.push Pin::Reference::Require.new( + Solargraph::Location.new(region.filename, + Solargraph::Range.from_to(0, 0, 0, 0)), 'bundler/require', source: :parser + ) end process_children end @@ -53,7 +57,7 @@ def process # @return [void] def process_visibility - if (node.children.length > 2) + if node.children.length > 2 # @sg-ignore Need to add nil check here node.children[2..-1].each do |child| # @sg-ignore Variable type could not be inferred for method_name @@ -61,13 +65,14 @@ def process_visibility visibility = node.children[1] # :nocov: unless visibility.instance_of?(Symbol) - Solargraph.assert_or_log(:parser_visibility, "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") + Solargraph.assert_or_log(:parser_visibility, + "Expected visibility name to be a Symbol, got #{visibility.class} for node #{node.inspect}") return process_children end # :nocov: - if child.is_a?(::Parser::AST::Node) && (child.type == :sym || child.type == :str) + if child.is_a?(::Parser::AST::Node) && %i[sym str].include?(child.type) name = child.children[0].to_s - matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == region.closure.full_context.namespace && pin.context.scope == (region.scope || :instance)} + matches = pins.select { |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == region.closure.full_context.namespace && pin.context.scope == (region.scope || :instance) } matches.each do |pin| # @todo Smelly instance variable access pin.instance_variable_set(:@visibility, visibility) @@ -89,7 +94,7 @@ def process_attribute loc = get_node_location(node) clos = region.closure cmnt = comments_for(node) - if node.children[1] == :attr_reader || node.children[1] == :attr_accessor + if %i[attr_reader attr_accessor].include?(node.children[1]) pins.push Solargraph::Pin::Method.new( location: loc, closure: clos, @@ -101,56 +106,55 @@ def process_attribute source: :parser ) end - if node.children[1] == :attr_writer || node.children[1] == :attr_accessor - method_pin = Solargraph::Pin::Method.new( - location: loc, - closure: clos, - name: "#{a.children[0]}=", - comments: cmnt, - scope: region.scope || :instance, - visibility: region.visibility, - attribute: true, - source: :parser - ) - pins.push method_pin - method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :parser) - if method_pin.return_type.defined? - pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.items.map(&:rooted_tags), 'value') - end + next unless %i[attr_writer attr_accessor].include?(node.children[1]) + method_pin = Solargraph::Pin::Method.new( + location: loc, + closure: clos, + name: "#{a.children[0]}=", + comments: cmnt, + scope: region.scope || :instance, + visibility: region.visibility, + attribute: true, + source: :parser + ) + pins.push method_pin + method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, + source: :parser) + if method_pin.return_type.defined? + pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', + pins.last.return_type.items.map(&:rooted_tags), 'value') end end end # @return [void] def process_include - if node.children[2].is_a?(AST::Node) && node.children[2].type == :const - cp = region.closure - # @sg-ignore Need to add nil check here - node.children[2..-1].each do |i| - type = region.scope == :class ? Pin::Reference::Extend : Pin::Reference::Include - pins.push type.new( - location: get_node_location(i), - closure: cp, - name: unpack_name(i), - source: :parser - ) - end + return unless node.children[2].is_a?(AST::Node) && node.children[2].type == :const + cp = region.closure + # @sg-ignore Need to add nil check here + node.children[2..-1].each do |i| + type = region.scope == :class ? Pin::Reference::Extend : Pin::Reference::Include + pins.push type.new( + location: get_node_location(i), + closure: cp, + name: unpack_name(i), + source: :parser + ) end end # @return [void] def process_prepend - if node.children[2].is_a?(AST::Node) && node.children[2].type == :const - cp = region.closure - # @sg-ignore Need to add nil check here - node.children[2..-1].each do |i| - pins.push Pin::Reference::Prepend.new( - location: get_node_location(i), - closure: cp, - name: unpack_name(i), - source: :parser - ) - end + return unless node.children[2].is_a?(AST::Node) && node.children[2].type == :const + cp = region.closure + # @sg-ignore Need to add nil check here + node.children[2..-1].each do |i| + pins.push Pin::Reference::Prepend.new( + location: get_node_location(i), + closure: cp, + name: unpack_name(i), + source: :parser + ) end end @@ -179,18 +183,16 @@ def process_extend # @return [void] def process_require - if node.children[2].is_a?(AST::Node) && node.children[2].type == :str - path = node.children[2].children[0].to_s - pins.push Pin::Reference::Require.new(get_node_location(node), path, source: :parser) - end + return unless node.children[2].is_a?(AST::Node) && node.children[2].type == :str + path = node.children[2].children[0].to_s + pins.push Pin::Reference::Require.new(get_node_location(node), path, source: :parser) end # @return [void] def process_autoload - if node.children[3].is_a?(AST::Node) && node.children[3].type == :str - path = node.children[3].children[0].to_s - pins.push Pin::Reference::Require.new(get_node_location(node), path, source: :parser) - end + return unless node.children[3].is_a?(AST::Node) && node.children[3].type == :str + path = node.children[3].children[0].to_s + pins.push Pin::Reference::Require.new(get_node_location(node), path, source: :parser) end # @return [void] @@ -198,55 +200,55 @@ def process_module_function if node.children[2].nil? # @todo Smelly instance variable access region.instance_variable_set(:@visibility, :module_function) - elsif node.children[2].type == :sym || node.children[2].type == :str + elsif %i[sym str].include?(node.children[2].type) # @sg-ignore Need to add nil check here node.children[2..-1].each do |x| cn = x.children[0].to_s # @type [Pin::Method, nil] ref = pins.find { |p| p.is_a?(Pin::Method) && p.namespace == region.closure.full_context.namespace && p.name == cn } - unless ref.nil? - pins.delete ref - mm = Solargraph::Pin::Method.new( - location: ref.location, - closure: ref.closure, - name: ref.name, - parameters: ref.parameters, - comments: ref.comments, - scope: :class, - visibility: :public, - node: ref.node, + next if ref.nil? + pins.delete ref + mm = Solargraph::Pin::Method.new( + location: ref.location, + closure: ref.closure, + name: ref.name, + parameters: ref.parameters, + comments: ref.comments, + scope: :class, + visibility: :public, + node: ref.node, + source: :parser + ) + cm = Solargraph::Pin::Method.new( + location: ref.location, + closure: ref.closure, + name: ref.name, + parameters: ref.parameters, + comments: ref.comments, + scope: :instance, + visibility: :private, + node: ref.node, + source: :parser + ) + pins.push mm, cm + ivars.select { |pin| pin.is_a?(Pin::InstanceVariable) && pin.closure.path == ref.path }.each do |ivar| + ivars.delete ivar + ivars.push Solargraph::Pin::InstanceVariable.new( + location: ivar.location, + closure: cm, + name: ivar.name, + comments: ivar.comments, + assignment: ivar.assignment, + source: :parser + ) + ivars.push Solargraph::Pin::InstanceVariable.new( + location: ivar.location, + closure: mm, + name: ivar.name, + comments: ivar.comments, + assignment: ivar.assignment, source: :parser ) - cm = Solargraph::Pin::Method.new( - location: ref.location, - closure: ref.closure, - name: ref.name, - parameters: ref.parameters, - comments: ref.comments, - scope: :instance, - visibility: :private, - node: ref.node, - source: :parser) - pins.push mm, cm - ivars.select{|pin| pin.is_a?(Pin::InstanceVariable) && pin.closure.path == ref.path}.each do |ivar| - ivars.delete ivar - ivars.push Solargraph::Pin::InstanceVariable.new( - location: ivar.location, - closure: cm, - name: ivar.name, - comments: ivar.comments, - assignment: ivar.assignment, - source: :parser - ) - ivars.push Solargraph::Pin::InstanceVariable.new( - location: ivar.location, - closure: mm, - name: ivar.name, - comments: ivar.comments, - assignment: ivar.assignment, - source: :parser - ) - end end end elsif node.children[2].type == :def @@ -256,17 +258,19 @@ def process_module_function # @return [void] def process_private_constant - if node.children[2] && (node.children[2].type == :sym || node.children[2].type == :str) - cn = node.children[2].children[0].to_s - ref = pins.select{|p| [Solargraph::Pin::Namespace, Solargraph::Pin::Constant].include?(p.class) && p.namespace == region.closure.full_context.namespace && p.name == cn}.first - # HACK: Smelly instance variable access - ref.instance_variable_set(:@visibility, :private) unless ref.nil? - end + return unless node.children[2] && %i[sym str].include?(node.children[2].type) + cn = node.children[2].children[0].to_s + ref = pins.select do |p| + [Solargraph::Pin::Namespace, + Solargraph::Pin::Constant].include?(p.class) && p.namespace == region.closure.full_context.namespace && p.name == cn + end.first + # HACK: Smelly instance variable access + ref.instance_variable_set(:@visibility, :private) unless ref.nil? end # @return [void] def process_alias_method - loc = get_node_location(node) + get_node_location(node) pins.push Solargraph::Pin::MethodAlias.new( location: get_node_location(node), closure: region.closure, @@ -279,8 +283,10 @@ def process_alias_method # @return [Boolean] def process_private_class_method - if node.children[2].type == :sym || node.children[2].type == :str - ref = pins.select { |p| p.is_a?(Pin::Method) && p.namespace == region.closure.full_context.namespace && p.name == node.children[2].children[0].to_s }.first + if %i[sym str].include?(node.children[2].type) + ref = pins.select do |p| + p.is_a?(Pin::Method) && p.namespace == region.closure.full_context.namespace && p.name == node.children[2].children[0].to_s + end.first # HACK: Smelly instance variable access ref.instance_variable_set(:@visibility, :private) unless ref.nil? false diff --git a/lib/solargraph/parser/parser_gem/node_processors/until_node.rb b/lib/solargraph/parser/parser_gem/node_processors/until_node.rb index bcfae287d..2e091f41d 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/until_node.rb @@ -18,7 +18,7 @@ def process closure: region.closure, node: node, comments: comments_for(node), - source: :parser, + source: :parser ) process_children region end diff --git a/lib/solargraph/parser/parser_gem/node_processors/when_node.rb b/lib/solargraph/parser/parser_gem/node_processors/when_node.rb index b2b11dec1..915eb57e6 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/when_node.rb @@ -12,7 +12,7 @@ def process location: get_node_location(node), closure: region.closure, node: node, - source: :parser, + source: :parser ) process_children end diff --git a/lib/solargraph/parser/parser_gem/node_processors/while_node.rb b/lib/solargraph/parser/parser_gem/node_processors/while_node.rb index 38ccc53b5..6c4fe33d8 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/while_node.rb @@ -22,7 +22,7 @@ def process closure: region.closure, node: node, comments: comments_for(node), - source: :parser, + source: :parser ) process_children region end diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index d0e53d741..2b0ac5cbb 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -46,7 +46,8 @@ def presence_certain? # @param docstring [YARD::Docstring, nil] # @param directives [::Array, nil] # @param combine_priority [::Numeric, nil] See attr_reader for combine_priority - def initialize location: nil, type_location: nil, closure: nil, source: nil, name: '', comments: '', docstring: nil, directives: nil, combine_priority: nil + def initialize location: nil, type_location: nil, closure: nil, source: nil, name: '', comments: '', + docstring: nil, directives: nil, combine_priority: nil @location = location @type_location = type_location @closure = closure @@ -60,7 +61,6 @@ def initialize location: nil, type_location: nil, closure: nil, source: nil, nam # @type [ComplexType, ComplexType::UniqueType, nil] @binder = nil - assert_source_provided assert_location_provided end @@ -69,12 +69,16 @@ def initialize location: nil, type_location: nil, closure: nil, source: nil, nam def assert_location_provided return unless best_location.nil? && %i[yardoc source rbs].include?(source) - Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}") + Solargraph.assert_or_log(:best_location, + "Neither location nor type_location provided - #{path} #{source} #{self.class}") end # @return [Pin::Closure, nil] def closure - Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure + unless @closure + Solargraph.assert_or_log(:closure, + "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") + end @closure end @@ -82,7 +86,7 @@ def closure # @param attrs [Hash{::Symbol => Object}] # # @return [self] - def combine_with(other, attrs={}) + def combine_with other, attrs = {} priority_choice = choose_priority(other) return priority_choice unless priority_choice.nil? @@ -101,7 +105,9 @@ def combine_with(other, attrs={}) combine_priority: combine_priority }.merge(attrs) assert_same_macros(other) - logger.debug { "Base#combine_with(path=#{path}) - other.comments=#{other.comments.inspect}, self.comments = #{self.comments}" } + logger.debug do + "Base#combine_with(path=#{path}) - other.comments=#{other.comments.inspect}, self.comments = #{comments}" + end out = self.class.new(**new_attrs) out.reset_generated! out @@ -110,7 +116,7 @@ def combine_with(other, attrs={}) # @param other [self] # @return [self, nil] Returns either the pin chosen based on priority or nil # A nil return means that the combination process must proceed - def choose_priority(other) + def choose_priority other if combine_priority.nil? && !other.combine_priority.nil? return other elsif other.combine_priority.nil? && !combine_priority.nil? @@ -130,7 +136,7 @@ def choose_priority(other) # @param attr [::Symbol] # @sg-ignore # @return [undefined] - def choose_longer(other, attr) + def choose_longer other, attr # @type [undefined] val1 = send(attr) # @type [undefined] @@ -143,22 +149,22 @@ def choose_longer(other, attr) # @param other [self] # # @return [::Array, nil] - def combine_directives(other) - return self.directives if other.directives.empty? + def combine_directives other + return directives if other.directives.empty? return other.directives if directives.empty? (directives + other.directives).uniq end # @param other [self] # @return [Pin::Closure, nil] - def combine_closure(other) + def combine_closure other choose_pin_attr_with_same_name(other, :closure) end # @param other [self] # @sg-ignore @type should override probed type # @return [String] - def combine_name(other) + def combine_name other if needs_consistent_name? || other.needs_consistent_name? assert_same(other, :name) else @@ -196,7 +202,7 @@ def needs_consistent_name? # @param other [self] # @return [ComplexType] - def combine_return_type(other) + def combine_return_type other if return_type.undefined? other.return_type elsif other.return_type.undefined? @@ -211,7 +217,9 @@ def combine_return_type(other) return_type else all_items = return_type.items + other.return_type.items - if all_items.any? { |item| item.selfy? } && all_items.any? { |item| item.rooted_tag == context.reduce_class_type.rooted_tag } + if all_items.any? { |item| item.selfy? } && all_items.any? do |item| + item.rooted_tag == context.reduce_class_type.rooted_tag + end # assume this was a declaration that should have said 'self' all_items.delete_if { |item| item.rooted_tag == context.reduce_class_type.rooted_tag } end @@ -234,14 +242,14 @@ def dodgy_return_type_source? # # @sg-ignore # @return [undefined, nil] - def choose(other, attr) + def choose other, attr results = [self, other].map(&attr).compact # true and false are different classes and can't be sorted - return true if results.any? { |r| r == true || r == false } + return true if results.any? { |r| [true, false].include?(r) } return results.first if results.any? { |r| r.is_a? AST::Node } results.min - rescue - STDERR.puts("Problem handling #{attr} for \n#{self.inspect}\n and \n#{other.inspect}\n\n#{self.send(attr).inspect} vs #{other.send(attr).inspect}") + rescue StandardError + warn("Problem handling #{attr} for \n#{inspect}\n and \n#{other.inspect}\n\n#{send(attr).inspect} vs #{other.send(attr).inspect}") raise end @@ -249,7 +257,7 @@ def choose(other, attr) # @param attr [::Symbol] # @sg-ignore # @return [undefined] - def choose_node(other, attr) + def choose_node other, attr if other.object_id < attr.object_id other.send(attr) else @@ -261,9 +269,9 @@ def choose_node(other, attr) # @param attr [::Symbol] # @sg-ignore # @return [undefined] - def prefer_rbs_location(other, attr) + def prefer_rbs_location other, attr if rbs_location? && !other.rbs_location? - self.send(attr) + send(attr) elsif !rbs_location? && other.rbs_location? other.send(attr) else @@ -278,8 +286,8 @@ def rbs_location? # @param other [self] # @return [void] - def assert_same_macros(other) - return unless self.source == :yardoc && other.source == :yardoc + def assert_same_macros other + return unless source == :yardoc && other.source == :yardoc assert_same_count(other, :macros) # @param [YARD::Tags::MacroDirective] assert_same_array_content(other, :macros) { |macro| macro.tag.name } @@ -289,7 +297,7 @@ def assert_same_macros(other) # @param attr [::Symbol] # @return [void] # @todo strong typechecking should complain when there are no block-related tags - def assert_same_array_content(other, attr, &block) + def assert_same_array_content other, attr, &block arr1 = send(attr) raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable) # @type arr1 [::Enumerable] @@ -303,7 +311,7 @@ def assert_same_array_content(other, attr, &block) values2 = arr2.map(&block) # @sg-ignore return arr1 if values1 == values2 - Solargraph.assert_or_log("combine_with_#{attr}".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}", "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self values = #{values1}\nother values =#{attr} = #{values2}") arr1 end @@ -312,15 +320,15 @@ def assert_same_array_content(other, attr, &block) # @param attr [::Symbol] # # @return [::Enumerable] - def assert_same_count(other, attr) + def assert_same_count other, attr # @type [::Enumerable] - arr1 = self.send(attr) + arr1 = send(attr) raise "Expected #{attr} on #{self} to be an Enumerable, got #{arr1.class}" unless arr1.is_a?(::Enumerable) # @type [::Enumerable] arr2 = other.send(attr) raise "Expected #{attr} on #{other} to be an Enumerable, got #{arr2.class}" unless arr2.is_a?(::Enumerable) return arr1 if arr1.count == arr2.count - Solargraph.assert_or_log("combine_with_#{attr}".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}", "Inconsistent #{attr.inspect} count value between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{arr1.inspect}\nother.#{attr} = #{arr2.inspect}") arr1 end @@ -330,16 +338,16 @@ def assert_same_count(other, attr) # # @sg-ignore # @return [undefined] - def assert_same(other, attr) + def assert_same other, attr if other.nil? - Solargraph.assert_or_log("combine_with_#{attr}_nil".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}_nil", "Other was passed in nil in assert_same on #{self}") return send(attr) end val1 = send(attr) val2 = other.send(attr) return val1 if val1 == val2 - Solargraph.assert_or_log("combine_with_#{attr}".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}", "Inconsistent #{attr.inspect} values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}") val1 end @@ -348,15 +356,17 @@ def assert_same(other, attr) # @param attr [::Symbol] # @sg-ignore # @return [undefined] - def choose_pin_attr_with_same_name(other, attr) + def choose_pin_attr_with_same_name other, attr # @type [Pin::Base, nil] val1 = send(attr) # @type [Pin::Base, nil] val2 = other.send(attr) - raise "Expected pin for #{attr} on\n#{self.inspect},\ngot #{val1.inspect}" unless val1.nil? || val1.is_a?(Pin::Base) - raise "Expected pin for #{attr} on\n#{other.inspect},\ngot #{val2.inspect}" unless val2.nil? || val2.is_a?(Pin::Base) + raise "Expected pin for #{attr} on\n#{inspect},\ngot #{val1.inspect}" unless val1.nil? || val1.is_a?(Pin::Base) + unless val2.nil? || val2.is_a?(Pin::Base) + raise "Expected pin for #{attr} on\n#{other.inspect},\ngot #{val2.inspect}" + end if val1&.name != val2&.name - Solargraph.assert_or_log("combine_with_#{attr}_name".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}_name", "Inconsistent #{attr.inspect} name values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}") end choose_pin_attr(other, attr) @@ -367,14 +377,14 @@ def choose_pin_attr_with_same_name(other, attr) # # @sg-ignore Missing @return tag for Solargraph::Pin::Base#choose_pin_attr # @return [undefined] - def choose_pin_attr(other, attr) + def choose_pin_attr other, attr # @type [Pin::Base, nil] val1 = send(attr) # @type [Pin::Base, nil] val2 = other.send(attr) if val1.class != val2.class # :nocov: - Solargraph.assert_or_log("combine_with_#{attr}_class".to_sym, + Solargraph.assert_or_log(:"combine_with_#{attr}_class", "Inconsistent #{attr.inspect} class values between \nself =#{inspect} and \nother=#{other.inspect}:\n\n self.#{attr} = #{val1.inspect}\nother.#{attr} = #{val2.inspect}") return val1 # :nocov: @@ -409,7 +419,7 @@ def comments # @param context [ComplexType] # @param resolved_generic_values [Hash{String => ComplexType}] # @return [self] - def resolve_generics_from_context(generics_to_resolve, return_type_context = nil, resolved_generic_values: {}) + def resolve_generics_from_context generics_to_resolve, return_type_context = nil, resolved_generic_values: {} proxy return_type.resolve_generics_from_context(generics_to_resolve, return_type_context, resolved_generic_values: resolved_generic_values) @@ -418,7 +428,7 @@ def resolve_generics_from_context(generics_to_resolve, return_type_context = nil # @yieldparam [ComplexType] # @yieldreturn [ComplexType] # @return [self] - def transform_types(&transform) + def transform_types &transform proxy return_type.transform(&transform) end @@ -439,7 +449,7 @@ def all_rooted? # @param generics_to_erase [::Array] # @return [self] - def erase_generics(generics_to_erase) + def erase_generics generics_to_erase return self if generics_to_erase.empty? transform_types { |t| t.erase_generics(generics_to_erase) } end @@ -490,7 +500,8 @@ def nearly? other # @sg-ignore Translate to something flow sensitive typing understands (comments == other.comments || # @sg-ignore Translate to something flow sensitive typing understands - (((maybe_directives? == false && other.maybe_directives? == false) || compare_directives(directives, other.directives)) && + (((maybe_directives? == false && other.maybe_directives? == false) || compare_directives(directives, + other.directives)) && # @sg-ignore Translate to something flow sensitive typing understands compare_docstring_tags(docstring, other.docstring)) ) @@ -573,7 +584,8 @@ def probe api_map # @param api_map [ApiMap] # @return [ComplexType, ComplexType::UniqueType] def infer api_map - Solargraph.assert_or_log(:pin_infer, 'WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead.') + Solargraph.assert_or_log(:pin_infer, + 'WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead.') type = typify(api_map) return type unless type.undefined? probe api_map @@ -666,7 +678,7 @@ def desc # @return [String] def inspect - "#<#{self.class} `#{self.inner_desc}`#{all_location_text} via #{source.inspect}>" + "#<#{self.class} `#{inner_desc}`#{all_location_text} via #{source.inspect}>" end # @return [String] @@ -693,9 +705,7 @@ def all_location_text # @return [ComplexType, ComplexType::UniqueType, nil] attr_writer :return_type - attr_writer :docstring - - attr_writer :directives + attr_writer :docstring, :directives private @@ -754,7 +764,7 @@ def compare_tags tag1, tag2 def collect_macros return [] unless maybe_directives? parse = Solargraph::Source.parse_docstring(comments) - parse.directives.select{ |d| d.tag.tag_name == 'macro' } + parse.directives.select { |d| d.tag.tag_name == 'macro' } end end end diff --git a/lib/solargraph/pin/base_variable.rb b/lib/solargraph/pin/base_variable.rb index 1e021f86b..b3ee3d814 100644 --- a/lib/solargraph/pin/base_variable.rb +++ b/lib/solargraph/pin/base_variable.rb @@ -46,6 +46,7 @@ class BaseVariable < Base # @see https://en.wikipedia.org/wiki/Intersection_type#TypeScript_example # @param presence [Range, nil] # @param presence_certain [Boolean] + # @param [Hash{Symbol => Object}] splat def initialize assignment: nil, assignments: [], mass_assignment: nil, presence: nil, presence_certain: false, return_type: nil, intersection_return_type: nil, exclude_return_type: nil, @@ -83,29 +84,29 @@ def reset_generated! super end - def combine_with(other, attrs={}) + def combine_with other, attrs = {} new_assignments = combine_assignments(other) new_attrs = attrs.merge({ - # default values don't exist in RBS parameters; it just - # tells you if the arg is optional or not. Prefer a - # provided value if we have one here since we can't rely on - # it from RBS so we can infer from it and typecheck on it. - assignment: choose(other, :assignment), - assignments: new_assignments, - mass_assignment: combine_mass_assignment(other), - return_type: combine_return_type(other), - intersection_return_type: combine_types(other, :intersection_return_type), - exclude_return_type: combine_types(other, :exclude_return_type), - presence: combine_presence(other), - presence_certain: combine_presence_certain(other) - }) + # default values don't exist in RBS parameters; it just + # tells you if the arg is optional or not. Prefer a + # provided value if we have one here since we can't rely on + # it from RBS so we can infer from it and typecheck on it. + assignment: choose(other, :assignment), + assignments: new_assignments, + mass_assignment: combine_mass_assignment(other), + return_type: combine_return_type(other), + intersection_return_type: combine_types(other, :intersection_return_type), + exclude_return_type: combine_types(other, :exclude_return_type), + presence: combine_presence(other), + presence_certain: combine_presence_certain(other) + }) super(other, new_attrs) end # @param other [self] # # @return [Array(AST::Node, Integer), nil] - def combine_mass_assignment(other) + def combine_mass_assignment other # @todo pick first non-nil arbitrarily - we don't yet support # mass assignment merging mass_assignment || other.mass_assignment @@ -117,7 +118,7 @@ def combine_mass_assignment(other) # @param other [self] # # @return [Boolean] - def combine_presence_certain(other) + def combine_presence_certain other presence_certain? || other.presence_certain? end @@ -129,7 +130,7 @@ def assignment # @param other [self] # # @return [::Array] - def combine_assignments(other) + def combine_assignments other (other.assignments + assignments).uniq end @@ -161,7 +162,7 @@ def variable? # @param parent_node [Parser::AST::Node] # @param api_map [ApiMap] # @return [::Array] - def return_types_from_node(parent_node, api_map) + def return_types_from_node parent_node, api_map types = [] value_position_nodes_only(parent_node).each do |node| # Nil nodes may not have a location @@ -242,7 +243,7 @@ def presence_certain? # @param other_loc [Location] # @sg-ignore flow sensitive typing needs to handle attrs - def starts_at?(other_loc) + def starts_at? other_loc location&.filename == other_loc.filename && presence && # @sg-ignore flow sensitive typing needs to handle attrs @@ -254,7 +255,7 @@ def starts_at?(other_loc) # @param other [self] # # @return [Range, nil] - def combine_presence(other) + def combine_presence other return presence || other.presence if presence.nil? || other.presence.nil? # @sg-ignore flow sensitive typing needs to handle attrs @@ -263,14 +264,14 @@ def combine_presence(other) # @param other [self] # @return [Pin::Closure, nil] - def combine_closure(other) - return closure if self.closure == other.closure + def combine_closure other + return closure if closure == other.closure # choose first defined, as that establishes the scope of the variable if closure.nil? || other.closure.nil? Solargraph.assert_or_log(:varible_closure_missing) do - "One of the local variables being combined is missing a closure: " \ - "#{self.inspect} vs #{other.inspect}" + 'One of the local variables being combined is missing a closure: ' \ + "#{inspect} vs #{other.inspect}" end return closure || other.closure end @@ -290,7 +291,7 @@ def combine_closure(other) # @param other_closure [Pin::Closure] # @param other_loc [Location] - def visible_at?(other_closure, other_loc) + def visible_at? other_closure, other_loc # @sg-ignore flow sensitive typing needs to handle attrs location.filename == other_loc.filename && # @sg-ignore flow sensitive typing needs to handle attrs @@ -315,7 +316,7 @@ def presence_certain? # @param raw_return_type [ComplexType, ComplexType::UniqueType] # # @return [ComplexType, ComplexType::UniqueType] - def adjust_type(api_map, raw_return_type) + def adjust_type api_map, raw_return_type qualified_exclude = exclude_return_type&.qualify(api_map, *(closure&.gates || [''])) minus_exclusions = raw_return_type.exclude qualified_exclude, api_map qualified_intersection = intersection_return_type&.qualify(api_map, *(closure&.gates || [''])) @@ -324,14 +325,14 @@ def adjust_type(api_map, raw_return_type) # @param other [self] # @return [Pin::Closure, nil] - def combine_closure(other) - return closure if self.closure == other.closure + def combine_closure other + return closure if closure == other.closure # choose first defined, as that establishes the scope of the variable if closure.nil? || other.closure.nil? Solargraph.assert_or_log(:varible_closure_missing) do - "One of the local variables being combined is missing a closure: " \ - "#{self.inspect} vs #{other.inspect}" + 'One of the local variables being combined is missing a closure: ' \ + "#{inspect} vs #{other.inspect}" end return closure || other.closure end @@ -381,7 +382,7 @@ def visible_in_closure? viewing_closure # @param other [self] # @return [ComplexType, nil] - def combine_return_type(other) + def combine_return_type other combine_types(other, :return_type) end @@ -389,7 +390,7 @@ def combine_return_type(other) # @param attr [::Symbol] # # @return [ComplexType, nil] - def combine_types(other, attr) + def combine_types other, attr # @type [ComplexType, nil] type1 = send(attr) # @type [ComplexType, nil] diff --git a/lib/solargraph/pin/block.rb b/lib/solargraph/pin/block.rb index 39f0d6495..1ad503317 100644 --- a/lib/solargraph/pin/block.rb +++ b/lib/solargraph/pin/block.rb @@ -15,6 +15,7 @@ class Block < Callable # @param node [Parser::AST::Node, nil] # @param context [ComplexType, nil] # @param args [::Array] + # @param [Hash{Symbol => Object}] splat def initialize receiver: nil, args: [], context: nil, node: nil, **splat super(**splat, parameters: args) @receiver = receiver @@ -44,7 +45,7 @@ def context # @param parameters [::Array] # # @return [::Array] - def destructure_yield_types(yield_types, parameters) + def destructure_yield_types yield_types, parameters # yielding a tuple into a block will destructure the tuple if yield_types.length == 1 yield_type = yield_types.first @@ -55,7 +56,7 @@ def destructure_yield_types(yield_types, parameters) # @param api_map [ApiMap] # @return [::Array] - def typify_parameters(api_map) + def typify_parameters api_map chain = Parser.chain(receiver, filename, node) # @sg-ignore Need to add nil check here clip = api_map.clip_at(location.filename, location.range.start) diff --git a/lib/solargraph/pin/callable.rb b/lib/solargraph/pin/callable.rb index 034cef03f..937237f7c 100644 --- a/lib/solargraph/pin/callable.rb +++ b/lib/solargraph/pin/callable.rb @@ -14,6 +14,7 @@ class Callable < Closure # @param block [Signature, nil] # @param return_type [ComplexType, nil] # @param parameters [::Array] + # @param [Hash{Symbol => Object}] splat def initialize block: nil, return_type: nil, parameters: [], **splat super(**splat) @block = block @@ -36,7 +37,7 @@ def method_namespace # @param other [self] # # @return [Pin::Signature, nil] - def combine_blocks(other) + def combine_blocks other if block.nil? other.block elsif other.block.nil? @@ -51,10 +52,10 @@ def combine_blocks(other) # @param attrs [Hash{Symbol => Object}] # # @return [self] - def combine_with(other, attrs={}) + def combine_with other, attrs = {} new_attrs = { block: combine_blocks(other), - return_type: combine_return_type(other), + return_type: combine_return_type(other) }.merge(attrs) new_attrs[:parameters] = choose_parameters(other).clone.freeze unless new_attrs.key?(:parameters) super(other, new_attrs) @@ -72,8 +73,10 @@ def generics # @param other [self] # # @return [Array] - def choose_parameters(other) - raise "Trying to combine two pins with different arities - \nself =#{inspect}, \nother=#{other.inspect}, \n\n self.arity=#{self.arity}, \nother.arity=#{other.arity}" if other.arity != arity + def choose_parameters other + if other.arity != arity + raise "Trying to combine two pins with different arities - \nself =#{inspect}, \nother=#{other.inspect}, \n\n self.arity=#{arity}, \nother.arity=#{other.arity}" + end # @param param [Pin::Parameter] # @param other_param [Pin::Parameter] parameters.zip(other.parameters).map do |param, other_param| @@ -133,12 +136,12 @@ def full_type_arity # @param resolved_generic_values [Hash{String => ComplexType}] # # @return [self] - def resolve_generics_from_context(generics_to_resolve, + def resolve_generics_from_context generics_to_resolve, arg_types = nil, return_type_context = nil, yield_arg_types = nil, yield_return_type_context = nil, - resolved_generic_values: {}) + resolved_generic_values: {} callable = super(generics_to_resolve, return_type_context, resolved_generic_values: resolved_generic_values) callable.parameters = callable.parameters.each_with_index.map do |param, i| if arg_types.nil? @@ -149,10 +152,12 @@ def resolve_generics_from_context(generics_to_resolve, resolved_generic_values: resolved_generic_values) end end - callable.block = block.resolve_generics_from_context(generics_to_resolve, - yield_arg_types, - yield_return_type_context, - resolved_generic_values: resolved_generic_values) if callable.block? + if callable.block? + callable.block = block.resolve_generics_from_context(generics_to_resolve, + yield_arg_types, + yield_return_type_context, + resolved_generic_values: resolved_generic_values) + end callable end @@ -171,7 +176,7 @@ def typify api_map # @sg-ignore Need to add nil check here # @return [String] def method_name - raise "closure was nil in #{self.inspect}" if closure.nil? + raise "closure was nil in #{inspect}" if closure.nil? # @sg-ignore Need to add nil check here @method_name ||= closure.name end @@ -185,12 +190,12 @@ def method_name # @param resolved_generic_values [Hash{String => ComplexType}] # # @return [self] - def resolve_generics_from_context_until_complete(generics_to_resolve, + def resolve_generics_from_context_until_complete generics_to_resolve, arg_types = nil, return_type_context = nil, yield_arg_types = nil, yield_return_type_context = nil, - resolved_generic_values: {}) + resolved_generic_values: {} # See # https://github.com/soutaro/steep/tree/master/lib/steep/type_inference # and @@ -208,7 +213,7 @@ def resolve_generics_from_context_until_complete(generics_to_resolve, resolved_generic_values: resolved_generic_values) if last_resolved_generic_values == resolved_generic_values # erase anything unresolved - return new_pin.erase_generics(self.generics) + return new_pin.erase_generics(generics) end new_pin.resolve_generics_from_context_until_complete(generics_to_resolve, arg_types, @@ -221,7 +226,7 @@ def resolve_generics_from_context_until_complete(generics_to_resolve, # @yieldparam [ComplexType] # @yieldreturn [ComplexType] # @return [self] - def transform_types(&transform) + def transform_types &transform # @todo 'super' alone should work here I think, but doesn't typecheck at level typed callable = super(&transform) callable.block = block.transform_types(&transform) if block? @@ -259,7 +264,9 @@ def mandatory_positional_param_count # @return [String] def parameters_to_rbs # @sg-ignore Need to add nil check here - rbs_generics + '(' + parameters.map { |param| param.to_rbs }.join(', ') + ') ' + (block.nil? ? '' : '{ ' + block.to_rbs + ' } ') + rbs_generics + '(' + parameters.map { |param| + param.to_rbs + }.join(', ') + ') ' + (block.nil? ? '' : '{ ' + block.to_rbs + ' } ') end def to_rbs diff --git a/lib/solargraph/pin/closure.rb b/lib/solargraph/pin/closure.rb index 44265dcab..b01d760df 100644 --- a/lib/solargraph/pin/closure.rb +++ b/lib/solargraph/pin/closure.rb @@ -9,7 +9,8 @@ class Closure < CompoundStatement # @param scope [::Symbol] :class or :instance # @param generics [::Array, nil] # @param generic_defaults [Hash{String => ComplexType}] - def initialize scope: :class, generics: nil, generic_defaults: {}, **splat + # @param [Hash{Symbol => Object}] splat + def initialize scope: :class, generics: nil, generic_defaults: {}, **splat super(**splat) @scope = scope @generics = generics @@ -25,10 +26,10 @@ def generic_defaults # @param attrs [Hash{Symbol => Object}] # # @return [self] - def combine_with(other, attrs={}) + def combine_with other, attrs = {} new_attrs = { scope: assert_same(other, :scope), - generics: generics.empty? ? other.generics : generics, + generics: generics.empty? ? other.generics : generics }.merge(attrs) super(other, new_attrs) end diff --git a/lib/solargraph/pin/common.rb b/lib/solargraph/pin/common.rb index 658c983e2..d52502afe 100644 --- a/lib/solargraph/pin/common.rb +++ b/lib/solargraph/pin/common.rb @@ -18,7 +18,7 @@ module Common # @param value [Pin::Closure] # @return [void] - def closure=(value) + def closure= value @closure = value # remove cached values generated from closure reset_generated! @@ -26,7 +26,10 @@ def closure=(value) # @return [Pin::Closure, nil] def closure - Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure + unless @closure + Solargraph.assert_or_log(:closure, + "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") + end @closure end diff --git a/lib/solargraph/pin/constant.rb b/lib/solargraph/pin/constant.rb index 94a968e7e..8da79f682 100644 --- a/lib/solargraph/pin/constant.rb +++ b/lib/solargraph/pin/constant.rb @@ -34,11 +34,9 @@ def path # @return [ComplexType] def generate_complex_type tags = docstring.tags(:return).map(&:types).flatten.reject(&:nil?) - if tags.empty? - tags = docstring.tags(:type).map(&:types).flatten.reject(&:nil?) - end + tags = docstring.tags(:type).map(&:types).flatten.reject(&:nil?) if tags.empty? return ComplexType::UNDEFINED if tags.empty? - ComplexType.try_parse *tags + ComplexType.try_parse(*tags) end end end diff --git a/lib/solargraph/pin/conversions.rb b/lib/solargraph/pin/conversions.rb index 5ad3573f7..25d585ea0 100644 --- a/lib/solargraph/pin/conversions.rb +++ b/lib/solargraph/pin/conversions.rb @@ -73,7 +73,13 @@ def detail # This property is not cached in an instance variable because it can # change when pins get proxied. detail = String.new - detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined? + unless return_type.undefined? + detail += "=#{if probed? + '~' + else + (proxied? ? '^' : '>') + end} #{return_type}" + end detail.strip! return nil if detail.empty? detail @@ -117,7 +123,7 @@ def generate_link # @return [String] def escape_brackets text # text.gsub(/(\<|\>)/, "\\#{$1}") - text.gsub("<", '\<').gsub(">", '\>') + text.gsub('<', '\<').gsub('>', '\>') end end end diff --git a/lib/solargraph/pin/delegated_method.rb b/lib/solargraph/pin/delegated_method.rb index 917e3a4e6..83273705f 100644 --- a/lib/solargraph/pin/delegated_method.rb +++ b/lib/solargraph/pin/delegated_method.rb @@ -15,6 +15,7 @@ class DelegatedMethod < Pin::Method # @param receiver [Source::Chain, nil] the source code used to resolve the receiver for this delegated method. # @param name [String, nil] # @param receiver_method_name [String, nil] the method name that will be called on the receiver (defaults to :name). + # @param [Hash{Symbol => Object}] splat def initialize(method: nil, receiver: nil, name: method&.name, receiver_method_name: name, **splat) raise ArgumentError, 'either :method or :receiver is required' if (method && receiver) || (!method && !receiver) # @sg-ignore Need to add nil check here @@ -35,7 +36,6 @@ def location @resolved_method&.send(:location) end - def type_location return super if super @@ -59,7 +59,7 @@ def type_location end # @param api_map [ApiMap] - def resolvable?(api_map) + def resolvable? api_map resolve_method(api_map) !!@resolved_method end @@ -112,7 +112,7 @@ def resolve_method api_map # # @param chain [Source::Chain] # @return [String] - def print_chain(chain) + def print_chain chain out = +'' chain.links.each_with_index do |link, index| if index > 0 diff --git a/lib/solargraph/pin/documenting.rb b/lib/solargraph/pin/documenting.rb index cbeaf2a0d..94bd8a551 100644 --- a/lib/solargraph/pin/documenting.rb +++ b/lib/solargraph/pin/documenting.rb @@ -62,7 +62,7 @@ def to_s # @return [String] def to_code - "\n```ruby\n#{Documenting.normalize_indentation(@plaintext)}#{@plaintext.end_with?("\n") ? '' : "\n"}```\n\n" + "\n```ruby\n#{Documenting.normalize_indentation(@plaintext)}#{"\n" unless @plaintext.end_with?("\n")}```\n\n" end # @return [String] @@ -78,7 +78,8 @@ def documentation # line and at least two spaces of indentation. This is a common # convention in Ruby core documentation, e.g., String#split. sections = [DocSection.new(false)] - Documenting.normalize_indentation(Documenting.strip_html_comments(docstring.to_s.gsub("\t", ' '))).lines.each do |l| + Documenting.normalize_indentation(Documenting.strip_html_comments(docstring.to_s.gsub("\t", + ' '))).lines.each do |l| if l.start_with?(' ') # Code block sections.push DocSection.new(true) unless sections.last.code? diff --git a/lib/solargraph/pin/local_variable.rb b/lib/solargraph/pin/local_variable.rb index 7aaab4615..580775bd7 100644 --- a/lib/solargraph/pin/local_variable.rb +++ b/lib/solargraph/pin/local_variable.rb @@ -16,9 +16,9 @@ def probe api_map super end - def combine_with(other, attrs={}) + def combine_with other, attrs = {} # keep this as a parameter - return other.combine_with(self, attrs) if other.is_a?(Parameter) && !self.is_a?(Parameter) + return other.combine_with(self, attrs) if other.is_a?(Parameter) && !is_a?(Parameter) super end diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index e6d68c151..9ceec972f 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -23,6 +23,7 @@ class Method < Callable # @param signatures [::Array, nil] # @param anon_splat [Boolean] # @param context [ComplexType, ComplexType::UniqueType, nil] + # @param [Hash{Symbol => Object}] splat def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false, context: nil, **splat super(**splat) @@ -38,7 +39,7 @@ def initialize visibility: :public, explicit: true, block: :undefined, node: nil # @param other [Pin::Method] # @return [::Symbol] - def combine_visibility(other) + def combine_visibility other if dodgy_visibility_source? && !other.dodgy_visibility_source? other.visibility elsif other.dodgy_visibility_source? && !dodgy_visibility_source? @@ -48,16 +49,16 @@ def combine_visibility(other) end end - def combine_with(other, attrs = {}) + def combine_with other, attrs = {} priority_choice = choose_priority(other) return priority_choice unless priority_choice.nil? sigs = combine_signatures(other) parameters = if sigs.length > 0 - [].freeze - else - choose(other, :parameters).clone.freeze - end + [].freeze + else + choose(other, :parameters).clone.freeze + end new_attrs = { visibility: combine_visibility(other), explicit: explicit? || other.explicit?, @@ -77,7 +78,7 @@ def == other super && other.node == node end - def transform_types(&transform) + def transform_types &transform # @todo 'super' alone should work here I think, but doesn't typecheck at level typed m = super(&transform) m.signatures = m.signatures.map do |sig| @@ -92,14 +93,12 @@ def transform_types(&transform) def reset_generated! super unless signatures.empty? - return_type = nil @block = :undefined - parameters = [] + [] end block&.reset_generated! @signatures&.each(&:reset_generated!) - signature_help = nil - documentation = nil + nil end def all_rooted? @@ -108,7 +107,7 @@ def all_rooted? # @param signature [Pin::Signature] # @return [Pin::Method] - def with_single_signature(signature) + def with_single_signature signature m = proxy signature.return_type m.reset_generated! # @todo populating the single parameters/return_type/block @@ -150,7 +149,7 @@ def return_type # @param parameters [::Array] # @param return_type [ComplexType, nil] # @return [Signature] - def generate_signature(parameters, return_type) + def generate_signature parameters, return_type # @type [Pin::Signature, nil] block = nil yieldparam_tags = docstring.tags(:yieldparam) @@ -194,7 +193,11 @@ def signatures top_type = generate_complex_type result = [] result.push generate_signature(parameters, top_type) if top_type.defined? - result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty? + unless overloads.empty? + result.concat(overloads.map do |meth| + generate_signature(meth.parameters, meth.return_type) + end) + end result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty? result end @@ -214,12 +217,18 @@ def detail # change when pins get proxied. detail = String.new detail += if signatures.length > 1 - "(*) " - else - "(#{signatures.first.parameters.map(&:full).join(', ')}) " unless signatures.first.parameters.empty? - end.to_s + '(*) ' + else + "(#{signatures.first.parameters.map(&:full).join(', ')}) " unless signatures.first.parameters.empty? + end.to_s # @sg-ignore Need to add nil check here - detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined? + unless return_type.undefined? + detail += "=#{if probed? + '~' + else + (proxied? ? '^' : '>') + end} #{return_type}" + end detail.strip! return nil if detail.empty? detail @@ -258,7 +267,7 @@ def to_rbs end def path - @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}" + @path ||= "#{namespace}#{scope == :instance ? '#' : '.'}#{name}" end # @return [String] @@ -268,10 +277,14 @@ def method_name def typify api_map # @sg-ignore Need to add nil check here - logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context.rooted_tags}, return_type=#{return_type.rooted_tags}) - starting" } + logger.debug do + "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context.rooted_tags}, return_type=#{return_type.rooted_tags}) - starting" + end decl = super unless decl.undefined? - logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context}) => #{decl.rooted_tags.inspect} - decl found" } + logger.debug do + "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context}) => #{decl.rooted_tags.inspect} - decl found" + end return decl end type = see_reference(api_map) || typify_from_super(api_map) @@ -320,7 +333,7 @@ def documentation method_docs += "Block Returns:\n" lines = [] yieldreturn_tags.each do |r| - l = "*" + l = '*' l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty? l += " #{r.text}" lines.push l @@ -333,7 +346,7 @@ def documentation method_docs += "Returns:\n" lines = [] return_tags.each do |r| - l = "*" + l = '*' l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty? l += " #{r.text}" lines.push l @@ -397,7 +410,7 @@ def overloads end, closure: self, return_type: ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)), - source: :overloads, + source: :overloads ) end @overloads @@ -416,13 +429,13 @@ def resolve_ref_tag api_map return self unless docstring.ref_tags.any? docstring.ref_tags.each do |tag| ref = if tag.owner.to_s.start_with?(/[#.]/) - api_map.get_methods(namespace) - .select { |pin| pin.path.end_with?(tag.owner.to_s) } - .first - else - # @todo Resolve relative namespaces - api_map.get_path_pins(tag.owner.to_s).first - end + api_map.get_methods(namespace) + .select { |pin| pin.path.end_with?(tag.owner.to_s) } + .first + else + # @todo Resolve relative namespaces + api_map.get_path_pins(tag.owner.to_s).first + end next unless ref docstring.add_tag(*ref.docstring.tags(:param)) @@ -438,11 +451,7 @@ def rest_of_stack api_map protected - attr_writer :block - - attr_writer :signature_help - - attr_writer :documentation + attr_writer :block, :signature_help, :documentation, :return_type # @sg-ignore Need to add nil check here def dodgy_visibility_source? @@ -450,22 +459,22 @@ def dodgy_visibility_source? # e.g. activesupport did not understand 'private' markings # inside 'class << self' blocks, but YARD did OK at it # @sg-ignore Need to add nil check here - source == :rbs && scope == :class && type_location&.filename&.include?('generated') && return_type.undefined? || + (source == :rbs && scope == :class && type_location&.filename&.include?('generated') && return_type.undefined?) || # YARD's RBS generator seems to miss a lot of should-be protected instance methods - source == :rbs && scope == :instance && namespace.start_with?('YARD::') || + (source == :rbs && scope == :instance && namespace.start_with?('YARD::')) || # private on attr_readers seems to be broken in Prism's auto-generator script - source == :rbs && scope == :instance && namespace.start_with?('Prism::') || + (source == :rbs && scope == :instance && namespace.start_with?('Prism::')) || # The RBS for the RBS gem itself seems to use private as a # 'is this a public API' concept, more aggressively than the # actual code. Let's respect that and ignore the actual .rb file. - source == :yardoc && scope == :instance && namespace.start_with?('RBS::') + (source == :yardoc && scope == :instance && namespace.start_with?('RBS::')) end private # @param other [Pin::Method] # @return [Array] - def combine_signatures(other) + def combine_signatures other all_undefined = signatures.all? { |sig| !sig.return_type&.defined? } other_all_undefined = other.signatures.all? { |sig| !sig.return_type&.defined? } if all_undefined && !other_all_undefined @@ -497,7 +506,7 @@ def combine_signatures_by_type_arity(*signature_pins) # @param same_type_arity_signatures [Array] # # @return [Array] - def combine_same_type_arity_signatures(same_type_arity_signatures) + def combine_same_type_arity_signatures same_type_arity_signatures # This is an O(n^2) operation, so bail out if n is not small return same_type_arity_signatures if same_type_arity_signatures.length > 10 @@ -505,8 +514,6 @@ def combine_same_type_arity_signatures(same_type_arity_signatures) # @param new_signature [Pin::Signature] same_type_arity_signatures.reduce([]) do |old_signatures, new_signature| next [new_signature] if old_signatures.empty? - - found_merge = false old_signatures.flat_map do |old_signature| potential_new_signature = old_signature.combine_with(new_signature) @@ -560,7 +567,7 @@ def clean_param name # @param name [String] # # @return [ComplexType] - def param_type_from_name(tag, name) + def param_type_from_name tag, name # @param t [YARD::Tags::Tag] param = tag.tags(:param).select { |t| t.name == name }.first return ComplexType::UNDEFINED unless param @@ -571,7 +578,7 @@ def param_type_from_name(tag, name) def generate_complex_type tags = docstring.tags(:return).map(&:types).flatten.compact return ComplexType::UNDEFINED if tags.empty? - ComplexType.try_parse *tags + ComplexType.try_parse(*tags) end # @param api_map [ApiMap] @@ -635,7 +642,7 @@ def method_body_node return nil if node.nil? return node.children[1].children.last if node.type == :DEFN return node.children[2].children.last if node.type == :DEFS - return node.children[2] if node.type == :def || node.type == :DEFS + return node.children[2] if %i[def DEFS].include?(node.type) return node.children[3] if node.type == :defs nil end @@ -648,7 +655,7 @@ def infer_from_return_nodes api_map has_nil = false return ComplexType::NIL if method_body_node.nil? returns_from_method_body(method_body_node).each do |n| - if n.nil? || [:NIL, :nil].include?(n.type) + if n.nil? || %i[NIL nil].include?(n.type) has_nil = true next end @@ -688,7 +695,7 @@ def infer_from_iv api_map # # @param name [String] # @return [::Array(String, ::Symbol)] - def parse_overload_param(name) + def parse_overload_param name # @todo this needs to handle mandatory vs not args, kwargs, blocks, etc if name.start_with?('**') [name[2..-1], :kwrestarg] @@ -711,10 +718,6 @@ def concat_example_tags .join("\n") .concat("```\n") end - - protected - - attr_writer :return_type end end end diff --git a/lib/solargraph/pin/namespace.rb b/lib/solargraph/pin/namespace.rb index f41a7ae2b..f090716c8 100644 --- a/lib/solargraph/pin/namespace.rb +++ b/lib/solargraph/pin/namespace.rb @@ -20,6 +20,7 @@ class Namespace < Closure # @param visibility [::Symbol] :public or :private # @param gates [::Array] # @param name [String] + # @param [Hash{Symbol => Object}] splat def initialize type: :class, visibility: :public, gates: [''], name: '', **splat # super(location, namespace, name, comments) super(**splat, name: name) @@ -36,11 +37,11 @@ def initialize type: :class, visibility: :public, gates: [''], name: '', **splat parts = name.split('::') name = parts.pop closure_name = if [Solargraph::Pin::ROOT_PIN, nil].include?(closure) - '' - else - # @sg-ignore Need to add nil check here - closure.full_context.namespace + '::' - end + '' + else + # @sg-ignore Need to add nil check here + closure.full_context.namespace + '::' + end closure_name += parts.join('::') @closure = Pin::Namespace.new(name: closure_name, gates: [parts.join('::')], source: :namespace) @context = nil @@ -55,7 +56,7 @@ def reset_generated! end def to_rbs - "#{@type.to_s} #{return_type.all_params.first.to_rbs}#{rbs_generics}".strip + "#{@type} #{return_type.all_params.first.to_rbs}#{rbs_generics}".strip end def inner_desc @@ -97,7 +98,7 @@ def path end def return_type - @return_type ||= ComplexType.try_parse( (type == :class ? '::Class' : '::Module') + "<::#{path}>") + @return_type ||= ComplexType.try_parse((type == :class ? '::Class' : '::Module') + "<::#{path}>") end # @return [Array] @@ -111,10 +112,10 @@ def typify api_map def gates @gates ||= if path.empty? - @open_gates - else - [path] + @open_gates - end + @open_gates + else + [path] + @open_gates + end end end end diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index 05a8cb490..4aa8f0e19 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -15,6 +15,7 @@ class Parameter < LocalVariable # @param decl [::Symbol] :arg, :optarg, :kwarg, :kwoptarg, :restarg, :kwrestarg, :block, :blockarg # @param asgn_code [String, nil] + # @param [Hash{Symbol => Object}] splat def initialize decl: :arg, asgn_code: nil, **splat super(**splat) @asgn_code = asgn_code @@ -29,7 +30,7 @@ def location super || closure&.type_location end - def combine_with(other, attrs={}) + def combine_with other, attrs = {} # Parameters can be combined with local variables new_attrs = if other.is_a?(Parameter) { @@ -45,7 +46,7 @@ def combine_with(other, attrs={}) super(other, new_attrs.merge(attrs)) end - def combine_return_type(other) + def combine_return_type other out = super if out&.undefined? # allow our return_type method to provide a better type @@ -56,12 +57,12 @@ def combine_return_type(other) end def keyword? - [:kwarg, :kwoptarg].include?(decl) + %i[kwarg kwoptarg].include?(decl) end def kwrestarg? # @sg-ignore flow sensitive typing needs to handle attrs - decl == :kwrestarg || (assignment && [:HASH, :hash].include?(assignment.type)) + decl == :kwrestarg || (assignment && %i[HASH hash].include?(assignment.type)) end def needs_consistent_name? @@ -70,21 +71,21 @@ def needs_consistent_name? # @return [String] def arity_decl - name = (self.name || '(anon)') - type = (return_type&.to_rbs || 'untyped') + name = self.name || '(anon)' + return_type&.to_rbs || 'untyped' case decl when :arg - "" + '' when :optarg - "?" + '?' when :kwarg "#{name}:" when :kwoptarg "?#{name}:" when :restarg - "*" + '*' when :kwrestarg - "**" + '**' else "(unknown decl: #{decl})" end @@ -112,11 +113,11 @@ def positional? end def rest? - decl == :restarg || decl == :kwrestarg + %i[restarg kwrestarg].include?(decl) end def block? - [:block, :blockarg].include?(decl) + %i[block blockarg].include?(decl) end def to_rbs @@ -217,7 +218,7 @@ def typify api_map # @param atype [ComplexType] # @param api_map [ApiMap] - def compatible_arg?(atype, api_map) + def compatible_arg? atype, api_map # make sure we get types from up the method # inheritance chain if we don't have them on this pin ptype = typify api_map @@ -226,7 +227,7 @@ def compatible_arg?(atype, api_map) return true if atype.conforms_to?(api_map, ptype, :method_call, - [:allow_empty_params, :allow_undefined]) + %i[allow_empty_params allow_undefined]) ptype.generic? end @@ -259,9 +260,7 @@ def param_tag # @return [ComplexType] def typify_block_param api_map block_pin = closure - if block_pin.is_a?(Pin::Block) && block_pin.receiver && index - return block_pin.typify_parameters(api_map)[index] - end + return block_pin.typify_parameters(api_map)[index] if block_pin.is_a?(Pin::Block) && block_pin.receiver && index ComplexType::UNDEFINED end @@ -279,11 +278,14 @@ def typify_method_param api_map found = p break end - if found.nil? and !index.nil? - found = params[index] if params[index] && (params[index].name.nil? || params[index].name.empty?) + if found.nil? and !index.nil? && params[index] && (params[index].name.nil? || params[index].name.empty?) + found = params[index] end # @sg-ignore Need to add nil check here - return ComplexType.try_parse(*found.types).qualify(api_map, *meth.closure.gates) unless found.nil? || found.types.nil? + unless found.nil? || found.types.nil? + return ComplexType.try_parse(*found.types).qualify(api_map, + *meth.closure.gates) + end end ComplexType::UNDEFINED end diff --git a/lib/solargraph/pin/proxy_type.rb b/lib/solargraph/pin/proxy_type.rb index e856c8833..fd37bab85 100644 --- a/lib/solargraph/pin/proxy_type.rb +++ b/lib/solargraph/pin/proxy_type.rb @@ -7,6 +7,7 @@ class ProxyType < Base # @param gates [Array, nil] Namespaces to try while resolving non-rooted types # @param binder [ComplexType, ComplexType::UniqueType, nil] # @param gates [Array, nil] + # @param [Hash{Symbol => Object}] splat def initialize return_type: ComplexType::UNDEFINED, binder: nil, gates: nil, **splat super(**splat) @gates = gates @@ -22,6 +23,7 @@ def context # @param closure [Pin::Namespace, nil] Used as the closure for this pin # @param binder [ComplexType, ComplexType::UniqueType, nil] # @return [ProxyType] + # @param [Hash{Symbol => Object}] kwargs def self.anonymous context, closure: nil, binder: nil, **kwargs unless closure parts = context.namespace.split('::') diff --git a/lib/solargraph/pin/reference.rb b/lib/solargraph/pin/reference.rb index 13e603d6e..69c30e187 100644 --- a/lib/solargraph/pin/reference.rb +++ b/lib/solargraph/pin/reference.rb @@ -13,6 +13,7 @@ class Reference < Base attr_reader :generic_values # @param generic_values [Array] + # @param [Hash{Symbol => Object}] splat def initialize generic_values: [], **splat super(**splat) @generic_values = generic_values diff --git a/lib/solargraph/pin/search.rb b/lib/solargraph/pin/search.rb index 67bd74d24..532b9774e 100644 --- a/lib/solargraph/pin/search.rb +++ b/lib/solargraph/pin/search.rb @@ -46,7 +46,7 @@ def do_query # @param b [self] # @sg-ignore https://github.com/castwide/solargraph/pull/1050 .sort { |a, b| b.match <=> a.match } - .map(&:pin) + .map(&:pin) end # @param str1 [String] diff --git a/lib/solargraph/pin/signature.rb b/lib/solargraph/pin/signature.rb index 0a6dbbafb..5e6d72c9e 100644 --- a/lib/solargraph/pin/signature.rb +++ b/lib/solargraph/pin/signature.rb @@ -49,16 +49,15 @@ def typify api_map method_stack = closure.rest_of_stack api_map logger.debug { "Signature#typify(self=#{self}) - method_stack: #{method_stack}" } method_stack.each do |pin| - sig = pin.signatures.find { |s| s.arity == self.arity } + sig = pin.signatures.find { |s| s.arity == arity } next unless sig # @sg-ignore Need to add nil check here - unless sig.return_type.undefined? - # @sg-ignore Need to add nil check here - qualified = sig.return_type.qualify(api_map, closure.namespace) - # @sg-ignore Need to add nil check here - logger.debug { "Signature#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" } - return qualified - end + next if sig.return_type.undefined? + # @sg-ignore Need to add nil check here + qualified = sig.return_type.qualify(api_map, closure.namespace) + # @sg-ignore Need to add nil check here + logger.debug { "Signature#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" } + return qualified end out = super logger.debug { "Signature#typify(self=#{self}) => #{out}" } diff --git a/lib/solargraph/pin/symbol.rb b/lib/solargraph/pin/symbol.rb index 18178e9b9..f28fb2a71 100644 --- a/lib/solargraph/pin/symbol.rb +++ b/lib/solargraph/pin/symbol.rb @@ -5,6 +5,7 @@ module Pin class Symbol < Base # @param location [Solargraph::Location, nil] # @param name [String] + # @param [Hash{Symbol => Object}] kwargs def initialize(location, name, **kwargs) # @sg-ignore "Unrecognized keyword argument kwargs to Solargraph::Pin::Base#initialize" super(location: location, name: name, **kwargs) diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 11c825787..5263ccb8f 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -408,6 +408,7 @@ def exist? *path # @return [void] # @param path_segments [Array] + # @param [Object, nil] out def uncache_by_prefix *path_segments, out: nil path = File.join(*path_segments) glob = "#{path}*" @@ -538,40 +539,40 @@ def yard_gem_path gemspec # @param gemspec [Gem::Specification] # @return [Array, nil] - def deserialize_yard_gem(gemspec) + def deserialize_yard_gem gemspec load(yard_gem_path(gemspec)) end # @param gemspec [Gem::Specification] # @param pins [Array] # @return [void] - def serialize_yard_gem(gemspec, pins) + def serialize_yard_gem gemspec, pins save(yard_gem_path(gemspec), pins) end # @param gemspec [Gem::Specification] # @return [Boolean] - def has_yard?(gemspec) + def has_yard? gemspec exist?(yard_gem_path(gemspec)) end # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [String] - def rbs_collection_path(gemspec, hash) + def rbs_collection_path gemspec, hash File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") end # @param gemspec [Gem::Specification] # @return [String] - def rbs_collection_path_prefix(gemspec) + def rbs_collection_path_prefix gemspec File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-") end # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [Array, nil] - def deserialize_rbs_collection_gem(gemspec, hash) + def deserialize_rbs_collection_gem gemspec, hash load(rbs_collection_path(gemspec, hash)) end @@ -579,20 +580,20 @@ def deserialize_rbs_collection_gem(gemspec, hash) # @param hash [String, nil] # @param pins [Array]n # @return [void] - def serialize_rbs_collection_gem(gemspec, hash, pins) + def serialize_rbs_collection_gem gemspec, hash, pins save(rbs_collection_path(gemspec, hash), pins) end # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [String] - def combined_path(gemspec, hash) + def combined_path gemspec, hash File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser") end # @param gemspec [Gem::Specification] # @return [String] - def combined_path_prefix(gemspec) + def combined_path_prefix gemspec File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-") end @@ -600,7 +601,7 @@ def combined_path_prefix(gemspec) # @param hash [String, nil] # @param pins [Array] # @return [void] - def serialize_combined_gem(gemspec, hash, pins) + def serialize_combined_gem gemspec, hash, pins save(combined_path(gemspec, hash), pins) end @@ -614,7 +615,7 @@ def deserialize_combined_gem gemspec, hash # @param gemspec [Gem::Specification] # @param hash [String, nil] # @return [Boolean] - def has_rbs_collection?(gemspec, hash) + def has_rbs_collection? gemspec, hash exist?(rbs_collection_path(gemspec, hash)) end diff --git a/lib/solargraph/position.rb b/lib/solargraph/position.rb index 53c7b61ba..1f8aa2805 100644 --- a/lib/solargraph/position.rb +++ b/lib/solargraph/position.rb @@ -26,7 +26,7 @@ def initialize line, character end # @param other [Position] - def <=>(other) + def <=> other return nil unless other.is_a?(Position) if line == other.line character <=> other.character diff --git a/lib/solargraph/range.rb b/lib/solargraph/range.rb index 50bec73d8..337e1db0d 100644 --- a/lib/solargraph/range.rb +++ b/lib/solargraph/range.rb @@ -24,7 +24,7 @@ def initialize start, ending end # @param other [BasicObject] - def <=>(other) + def <=> other return nil unless other.is_a?(Range) if start == other.start ending <=> other.ending @@ -36,7 +36,7 @@ def <=>(other) # Get a hash of the range. This representation is suitable for use in # the language server protocol. # - # @return [Hash] + # @return [Hash{Symbol => Position}] def to_hash { start: start.to_hash, @@ -86,9 +86,8 @@ def self.from_to l1, c1, l2, c2 # @param node [::Parser::AST::Node] # @return [Range, nil] def self.from_node node - if node&.loc && node.loc.expression - from_expr(node.loc.expression) - end + return unless node&.loc && node.loc.expression + from_expr(node.loc.expression) end # Get a range from a Parser range, usually found in diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index a04568587..cb2bede54 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -16,11 +16,7 @@ class RbsMap # @type [Hash{String => RbsMap}] @@rbs_maps_hash = {} - attr_reader :library - - attr_reader :rbs_collection_paths - - attr_reader :rbs_collection_config_path + attr_reader :library, :rbs_collection_paths, :rbs_collection_config_path # @param library [String] # @param version [String, nil] diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index b958895fb..9383ac441 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -23,7 +23,7 @@ def initialize visibility = :public end # @param loader [RBS::EnvironmentLoader] - def initialize(loader:) + def initialize loader: @loader = loader @pins = [] load_environment_to_pins(loader) @@ -44,9 +44,9 @@ def type_aliases # @param loader [RBS::EnvironmentLoader] # @return [void] - def load_environment_to_pins(loader) + def load_environment_to_pins loader environment = RBS::Environment.from_loader(loader).resolve_type_names - cursor = pins.length + pins.length if environment.declarations.empty? Solargraph.logger.info "No RBS declarations found in environment for core_root #{loader.core_root.inspect}, libraries #{loader.libs} and directories #{loader.dirs}" return @@ -86,7 +86,7 @@ def convert_decl_to_pin decl, closure # @param module_pin [Pin::Namespace] # @return [void] def convert_self_types_to_pins decl, module_pin - decl.self_types.each { |self_type| context = convert_self_type_to_pins(self_type, module_pin) } + decl.self_types.each { |self_type| convert_self_type_to_pins(self_type, module_pin) } end # @param decl [RBS::AST::Declarations::Module::Self] @@ -256,7 +256,7 @@ def module_decl_to_pin decl # @param base [String, nil] Optional conversion of tag to base # # @return [Solargraph::Pin::Constant] - def create_constant(name, tag, comments, decl, base = nil) + def create_constant name, tag, comments, decl, base = nil parts = name.split('::') if parts.length > 1 name = parts.last @@ -296,7 +296,7 @@ def module_alias_decl_to_pin decl new_name = decl.new_name.relative!.to_s old_name = decl.old_name.relative!.to_s - pins.push create_constant(new_name, old_name, decl.comment&.string, decl, 'Module') + pins.push create_constant(new_name, old_name, decl.comment&.string, decl, 'Module') end # @param decl [RBS::AST::Declarations::Constant] @@ -323,7 +323,6 @@ def global_decl_to_pin decl pins.push pin end - # Visibility overrides that will allow the Solargraph project # and plugins to pass typechecking using SOLARGRAPH_ASSERTS=on, # so that we can detect any regressions/issues elsewhere in the @@ -339,27 +338,27 @@ def global_decl_to_pin decl # allow that to be extended via .solargraph.yml # @type [Hash{Array(String, Symbol, String) => Symbol} VISIBILITY_OVERRIDE = { - ["Rails::Engine", :instance, "run_tasks_blocks"] => :protected, + ['Rails::Engine', :instance, 'run_tasks_blocks'] => :protected, # Should have been marked as both instance and class method in module -e.g., 'module_function' - ["Kernel", :instance, "pretty_inspect"] => :private, + ['Kernel', :instance, 'pretty_inspect'] => :private, # marked incorrectly in RBS - ["WEBrick::HTTPUtils::FormData", :instance, "next_data"] => :protected, - ["Rails::Command", :class, "command_type"] => :private, - ["Rails::Command", :class, "lookup_paths"] => :private, - ["Rails::Command", :class, "file_lookup_paths"] => :private, - ["Rails::Railtie", :instance, "run_console_blocks"] => :protected, - ["Rails::Railtie", :instance, "run_generators_blocks"] => :protected, - ["Rails::Railtie", :instance, "run_runner_blocks"] => :protected, - ["Rails::Railtie", :instance, "run_tasks_blocks"] => :protected, - ["ActionController::Base", :instance, "_protected_ivars"] => :private, - ["ActionView::Template", :instance, "method_name"] => :public, - ["Module", :instance, "ruby2_keywords"] => :private, - ["Nokogiri::XML::Node", :instance, "coerce"] => :protected, - ["Nokogiri::XML::Document", :class, "empty_doc?"] => :private, - ["Nokogiri::Decorators::Slop", :instance, "respond_to_missing?"] => :public, - ["RuboCop::Cop::RangeHelp", :instance, "source_range"] => :private, - ["AST::Node", :instance, "original_dup"] => :private, - ["Rainbow::Presenter", :instance, "wrap_with_sgr"] => :private, + ['WEBrick::HTTPUtils::FormData', :instance, 'next_data'] => :protected, + ['Rails::Command', :class, 'command_type'] => :private, + ['Rails::Command', :class, 'lookup_paths'] => :private, + ['Rails::Command', :class, 'file_lookup_paths'] => :private, + ['Rails::Railtie', :instance, 'run_console_blocks'] => :protected, + ['Rails::Railtie', :instance, 'run_generators_blocks'] => :protected, + ['Rails::Railtie', :instance, 'run_runner_blocks'] => :protected, + ['Rails::Railtie', :instance, 'run_tasks_blocks'] => :protected, + ['ActionController::Base', :instance, '_protected_ivars'] => :private, + ['ActionView::Template', :instance, 'method_name'] => :public, + ['Module', :instance, 'ruby2_keywords'] => :private, + ['Nokogiri::XML::Node', :instance, 'coerce'] => :protected, + ['Nokogiri::XML::Document', :class, 'empty_doc?'] => :private, + ['Nokogiri::Decorators::Slop', :instance, 'respond_to_missing?'] => :public, + ['RuboCop::Cop::RangeHelp', :instance, 'source_range'] => :private, + ['AST::Node', :instance, 'original_dup'] => :private, + ['Rainbow::Presenter', :instance, 'wrap_with_sgr'] => :private } # @param decl [RBS::AST::Members::MethodDefinition, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrWriter, RBS::AST::Members::AttrAccessor] @@ -368,12 +367,14 @@ def global_decl_to_pin decl # @param scope [Symbol] :instance or :class # @param name [String] The name of the method # @return [Symbol] - def calculate_method_visibility(decl, context, closure, scope, name) + def calculate_method_visibility decl, context, closure, scope, name override_key = [closure.path, scope, name] visibility = VISIBILITY_OVERRIDE[override_key] simple_override_key = [closure.path, scope] visibility ||= VISIBILITY_OVERRIDE[simple_override_key] - visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(decl.name) + if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(decl.name) + visibility ||= :private + end if decl.kind == :singleton_instance # this is a 'module function' visibility ||= :private @@ -417,24 +418,23 @@ def method_def_to_pin decl, closure, context pin.instance_variable_set(:@return_type, ComplexType::VOID) end end - if decl.singleton? - final_scope = :class - name = decl.name.to_s - visibility = calculate_method_visibility(decl, context, closure, final_scope, name) - pin = Solargraph::Pin::Method.new( - name: name, - closure: closure, - comments: decl.comment&.string, - type_location: location_decl_to_pin_location(decl.location), - visibility: visibility, - scope: final_scope, - signatures: [], - generics: generics, - source: :rbs - ) - pin.signatures.concat method_def_to_sigs(decl, pin) - pins.push pin - end + return unless decl.singleton? + final_scope = :class + name = decl.name.to_s + visibility = calculate_method_visibility(decl, context, closure, final_scope, name) + pin = Solargraph::Pin::Method.new( + name: name, + closure: closure, + comments: decl.comment&.string, + type_location: location_decl_to_pin_location(decl.location), + visibility: visibility, + scope: final_scope, + signatures: [], + generics: generics, + source: :rbs + ) + pin.signatures.concat method_def_to_sigs(decl, pin) + pins.push pin end # @param decl [RBS::AST::Members::MethodDefinition] @@ -459,7 +459,7 @@ def method_def_to_sigs decl, pin # @param location [RBS::Location, nil] # @return [Solargraph::Location, nil] - def location_decl_to_pin_location(location) + def location_decl_to_pin_location location return nil if location&.name.nil? # @sg-ignore flow sensitive typing should handle return nil if location&.name.nil? @@ -478,7 +478,8 @@ def parts_of_function type, pin type_location = pin.type_location if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) return [ - [Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, type_location: type_location)], + [Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, + type_location: type_location)], ComplexType.try_parse(method_type_to_tag(type)).force_rooted ] end @@ -489,7 +490,8 @@ def parts_of_function type, pin # @sg-ignore RBS generic type understanding issue name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" # @sg-ignore RBS generic type understanding issue - parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs, type_location: type_location) + parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, + return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs, type_location: type_location) end type.type.optional_positionals.each do |param| # @sg-ignore RBS generic type understanding issue @@ -510,12 +512,13 @@ def parts_of_function type, pin rooted: true, parameters_type: :list) parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs, type_location: type_location, - return_type: rest_positional_type,) + return_type: rest_positional_type) end type.type.trailing_positionals.each do |param| # @sg-ignore RBS generic type understanding issue name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" - parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs, type_location: type_location) + parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs, + type_location: type_location) end type.type.required_keywords.each do |orig, param| # @sg-ignore RBS generic type understanding issue @@ -549,7 +552,7 @@ def parts_of_function type, pin # @param closure [Pin::Namespace] # @param context [Context] # @return [void] - def attr_reader_to_pin(decl, closure, context) + def attr_reader_to_pin decl, closure, context name = decl.name.to_s final_scope = decl.kind == :instance ? :instance : :class visibility = calculate_method_visibility(decl, context, closure, final_scope, name) @@ -565,7 +568,9 @@ def attr_reader_to_pin(decl, closure, context) ) rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags pin.docstring.add_tag(YARD::Tags::Tag.new(:return, '', rooted_tag)) - logger.debug { "Conversions#attr_reader_to_pin(name=#{name.inspect}, visibility=#{visibility.inspect}) => #{pin.inspect}" } + logger.debug do + "Conversions#attr_reader_to_pin(name=#{name.inspect}, visibility=#{visibility.inspect}) => #{pin.inspect}" + end pins.push pin end @@ -573,9 +578,9 @@ def attr_reader_to_pin(decl, closure, context) # @param closure [Pin::Namespace] # @param context [Context] # @return [void] - def attr_writer_to_pin(decl, closure, context) + def attr_writer_to_pin decl, closure, context final_scope = decl.kind == :instance ? :instance : :class - name = "#{decl.name.to_s}=" + name = "#{decl.name}=" visibility = calculate_method_visibility(decl, context, closure, final_scope, name) type_location = location_decl_to_pin_location(decl.location) pin = Solargraph::Pin::Method.new( @@ -606,7 +611,7 @@ def attr_writer_to_pin(decl, closure, context) # @param closure [Pin::Namespace] # @param context [Context] # @return [void] - def attr_accessor_to_pin(decl, closure, context) + def attr_accessor_to_pin decl, closure, context attr_reader_to_pin(decl, closure, context) attr_writer_to_pin(decl, closure, context) end @@ -614,7 +619,7 @@ def attr_accessor_to_pin(decl, closure, context) # @param decl [RBS::AST::Members::InstanceVariable] # @param closure [Pin::Namespace] # @return [void] - def ivar_to_pin(decl, closure) + def ivar_to_pin decl, closure pin = Solargraph::Pin::InstanceVariable.new( name: decl.name.to_s, closure: closure, @@ -630,7 +635,7 @@ def ivar_to_pin(decl, closure) # @param decl [RBS::AST::Members::ClassVariable] # @param closure [Pin::Namespace] # @return [void] - def cvar_to_pin(decl, closure) + def cvar_to_pin decl, closure name = decl.name.to_s pin = Solargraph::Pin::ClassVariable.new( name: name, @@ -647,7 +652,7 @@ def cvar_to_pin(decl, closure) # @param decl [RBS::AST::Members::ClassInstanceVariable] # @param closure [Pin::Namespace] # @return [void] - def civar_to_pin(decl, closure) + def civar_to_pin decl, closure name = decl.name.to_s pin = Solargraph::Pin::InstanceVariable.new( name: name, @@ -711,7 +716,7 @@ def alias_to_pin decl, closure original: decl.old_name.to_s, closure: closure, scope: final_scope, - source: :rbs, + source: :rbs ) end @@ -736,7 +741,7 @@ def method_type_to_tag type # @param type_name [RBS::TypeName] # @param type_args [Enumerable] # @return [ComplexType::UniqueType] - def build_type(type_name, type_args = []) + def build_type type_name, type_args = [] base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s params = type_args.map { |a| other_type_to_tag(a) }.map do |t| ComplexType.try_parse(t).force_rooted @@ -751,7 +756,7 @@ def build_type(type_name, type_args = []) # @param type_name [RBS::TypeName] # @param type_args [Enumerable] # @return [String] - def type_tag(type_name, type_args = []) + def type_tag type_name, type_args = [] build_type(type_name, type_args).tags end @@ -783,7 +788,7 @@ def other_type_to_tag type 'void' elsif type.is_a?(RBS::Types::Variable) "#{Solargraph::ComplexType::GENERIC_TAG_NAME}<#{type.name}>" - elsif type.is_a?(RBS::Types::ClassInstance) #&& !type.args.empty? + elsif type.is_a?(RBS::Types::ClassInstance) # && !type.args.empty? type_tag(type.name, type.args) elsif type.is_a?(RBS::Types::Bases::Instance) 'self' diff --git a/lib/solargraph/rbs_map/core_fills.rb b/lib/solargraph/rbs_map/core_fills.rb index 3bb32a0da..809e802a1 100644 --- a/lib/solargraph/rbs_map/core_fills.rb +++ b/lib/solargraph/rbs_map/core_fills.rb @@ -38,7 +38,7 @@ module CoreFills source: :core_fill), # RBS does not define Class with a generic, so all calls to # generic() return an 'untyped'. We can do better: - Override.method_return('Class#allocate', 'self', source: :core_fill), + Override.method_return('Class#allocate', 'self', source: :core_fill) ] # @todo I don't see any direct link in RBS to build this from - @@ -46,26 +46,32 @@ module CoreFills # against concrete classes INCLUDES = [ Solargraph::Pin::Reference::Include.new(name: '_ToAry', - closure: Solargraph::Pin::Namespace.new(name: 'Array', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'Array', + source: :core_fill), generic_values: ['generic'], source: :core_fill), Solargraph::Pin::Reference::Include.new(name: '_ToAry', - closure: Solargraph::Pin::Namespace.new(name: 'Set', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'Set', + source: :core_fill), generic_values: ['generic'], source: :core_fill), Solargraph::Pin::Reference::Include.new(name: '_Each', - closure: Solargraph::Pin::Namespace.new(name: 'Array', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'Array', + source: :core_fill), generic_values: ['generic'], source: :core_fill), Solargraph::Pin::Reference::Include.new(name: '_Each', - closure: Solargraph::Pin::Namespace.new(name: 'Set', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'Set', + source: :core_fill), generic_values: ['generic'], source: :core_fill), Solargraph::Pin::Reference::Include.new(name: '_ToS', - closure: Solargraph::Pin::Namespace.new(name: 'Object', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'Object', + source: :core_fill), source: :core_fill), Solargraph::Pin::Reference::Include.new(name: '_ToS', - closure: Solargraph::Pin::Namespace.new(name: 'String', source: :core_fill), + closure: Solargraph::Pin::Namespace.new(name: 'String', + source: :core_fill), source: :core_fill) ] @@ -74,7 +80,8 @@ module CoreFills errnos = [] Errno.constants.each do |const| errnos.push Solargraph::Pin::Namespace.new(type: :class, name: const.to_s, closure: errno, source: :core_fill) - errnos.push Solargraph::Pin::Reference::Superclass.new(closure: errnos.last, name: 'SystemCallError', source: :core_fill) + errnos.push Solargraph::Pin::Reference::Superclass.new(closure: errnos.last, name: 'SystemCallError', + source: :core_fill) end ERRNOS = errnos diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index a92410895..e6ebcf90f 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -12,7 +12,6 @@ class StdlibMap < RbsMap # @type [Hash{String => RbsMap}] @stdlib_maps_hash = {} - # @param rebuild [Boolean] build pins regardless of whether we # have cached them already # @param library [String] diff --git a/lib/solargraph/server_methods.rb b/lib/solargraph/server_methods.rb index bdac3f19c..86cd107d0 100644 --- a/lib/solargraph/server_methods.rb +++ b/lib/solargraph/server_methods.rb @@ -7,7 +7,7 @@ module ServerMethods # @return [Integer] def available_port socket = Socket.new(:INET, :STREAM, 0) - socket.bind(Addrinfo.tcp("127.0.0.1", 0)) + socket.bind(Addrinfo.tcp('127.0.0.1', 0)) port = socket.local_address.ip_port socket.close port diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index e2c626528..14f57801f 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -16,7 +16,7 @@ def self.exit_on_failure? map %w[--version -v] => :version - desc "--version, -v", "Print the version" + desc '--version, -v', 'Print the version' # @return [void] def version puts Solargraph::VERSION @@ -31,15 +31,15 @@ def socket port = options[:port] port = available_port if port.zero? Backport.run do - Signal.trap("INT") do + Signal.trap('INT') do Backport.stop end - Signal.trap("TERM") do + Signal.trap('TERM') do Backport.stop end # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}" + warn "Solargraph is listening PORT=#{port} PID=#{Process.pid}" end end @@ -48,15 +48,15 @@ def socket def stdio require 'backport' Backport.run do - Signal.trap("INT") do + Signal.trap('INT') do Backport.stop end - Signal.trap("TERM") do + Signal.trap('TERM') do Backport.stop end # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter - STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}" + warn "Solargraph is listening on stdio PID=#{Process.pid}" end end @@ -64,11 +64,11 @@ def stdio option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true # @param directory [String] # @return [void] - def config(directory = '.') + def config directory = '.' matches = [] if options[:extensions] Gem::Specification.each do |g| - if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/) + if g.name.match(/^solargraph-[A-Za-z0-9_-]*?-ext/) require g.name matches.push g.name end @@ -84,7 +84,7 @@ def config(directory = '.') File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| file.puts conf.to_yaml end - STDOUT.puts "Configuration file initialized." + STDOUT.puts 'Configuration file initialized.' end desc 'clear', 'Delete all cached documentation' @@ -93,7 +93,7 @@ def config(directory = '.') ) # @return [void] def clear - puts "Deleting all cached documentation (gems, core and stdlib)" + puts 'Deleting all cached documentation (gems, core and stdlib)' Solargraph::PinCache.clear end map 'clear-cache' => :clear @@ -109,7 +109,7 @@ def cache gem, version = nil # ' end - desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation" + desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation' long_desc %( Specify one or more gem names to clear. 'core' or 'stdlib' may also be specified to clear cached system documentation. @@ -174,7 +174,7 @@ def gems *names if names.empty? workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else - $stderr.puts("Caching these gems: #{names}") + warn("Caching these gems: #{names}") names.each do |name| if name == 'core' PinCache.cache_core(out: $stdout) if !PinCache.core? || options[:rebuild] @@ -195,7 +195,7 @@ def gems *names # @sg-ignore Need to add nil check here warn e.backtrace.join("\n") end - $stderr.puts "Documentation cached for #{names.count} gems." + warn "Documentation cached for #{names.count} gems." end end @@ -212,7 +212,7 @@ def reporters Type checking levels are normal, typed, strict, and strong. ) - option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal' + option :level, type: :string, aliases: %i[mode m l], desc: 'Type checking level', default: 'normal' option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.' # @return [void] def typecheck *files @@ -231,19 +231,22 @@ def typecheck *files files.map! { |file| File.realpath(file) } end filecount = 0 - time = Benchmark.measure { + time = Benchmark.measure do files.each do |file| - checker = TypeChecker.new(file, api_map: api_map, rules: rules, level: options[:level].to_sym, workspace: workspace) + checker = TypeChecker.new(file, api_map: api_map, rules: rules, level: options[:level].to_sym, + workspace: workspace) problems = checker.problems next if problems.empty? problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line } - puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n") + puts problems.map { |prob| + "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" + }.join("\n") filecount += 1 probcount += problems.length end - } + end puts "Typecheck finished in #{time.real} seconds." - puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}." + puts "#{probcount} problem#{'s' if probcount != 1} found#{" in #{filecount} of #{files.length} files" if files.length != 1}." # " exit 1 if probcount > 0 end @@ -262,26 +265,26 @@ def scan directory = File.realpath(options[:directory]) # @type [Solargraph::ApiMap, nil] api_map = nil - time = Benchmark.measure { + time = Benchmark.measure do api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) # @sg-ignore flow sensitive typing should be able to handle redefinition api_map.pins.each do |pin| - begin - puts pin_description(pin) if options[:verbose] - pin.typify api_map - pin.probe api_map - rescue StandardError => e - # @todo to add nil check here - # @todo should warn on nil dereference below - STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}" - STDERR.puts "[#{e.class}]: #{e.message}" - # @todo Need to add nil check here - # @todo flow sensitive typing should be able to handle redefinition - STDERR.puts e.backtrace.join("\n") - exit 1 - end + puts pin_description(pin) if options[:verbose] + pin.typify api_map + pin.probe api_map + rescue StandardError => e + # @todo to add nil check here + # @todo should warn on nil dereference below + warn "Error testing #{pin_description(pin)} #{if pin.location + "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" + end}" + warn "[#{e.class}]: #{e.message}" + # @todo Need to add nil check here + # @todo flow sensitive typing should be able to handle redefinition + warn e.backtrace.join("\n") + exit 1 end - } + end # @sg-ignore Need to add nil check here puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds." end @@ -298,10 +301,13 @@ def list desc 'pin [PATH]', 'Describe a pin', hide: true option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false - option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations', default: false + option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations', + default: false option :references, type: :boolean, desc: 'Show references', default: false - option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', default: false - option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses', default: false + option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', + default: false + option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses', + default: false # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method' # @return [void] def pin path @@ -326,7 +332,7 @@ def pin path pin = pins.first case pin when nil - $stderr.puts "Pin not found for path '#{path}'" + warn "Pin not found for path '#{path}'" exit 1 when Pin::Namespace if options[:references] @@ -360,15 +366,15 @@ def pin path # @return [String] def pin_description pin desc = if pin.path.nil? || pin.path.empty? - if pin.closure - # @sg-ignore Need to add nil check here - "#{pin.closure.path} | #{pin.name}" - else - "#{pin.context.namespace} | #{pin.name}" - end - else - pin.path - end + if pin.closure + # @sg-ignore Need to add nil check here + "#{pin.closure.path} | #{pin.name}" + else + "#{pin.context.namespace} | #{pin.name}" + end + else + pin.path + end # @sg-ignore Need to add nil check here desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location desc @@ -376,7 +382,7 @@ def pin_description pin # @param type [ComplexType, ComplexType::UniqueType] # @return [void] - def print_type(type) + def print_type type if options[:rbs] puts type.to_rbs else @@ -386,7 +392,7 @@ def print_type(type) # @param pin [Solargraph::Pin::Base] # @return [void] - def print_pin(pin) + def print_pin pin if options[:rbs] puts pin.to_rbs else diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 615269d73..1451692a5 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -66,7 +66,7 @@ def at range def from_to l1, c1, l2, c2 b = Solargraph::Position.line_char_to_offset(code, l1, c1) e = Solargraph::Position.line_char_to_offset(code, l2, c2) - code[b..e-1] + code[b..e - 1] end # Get the nearest node that contains the specified index. @@ -74,7 +74,7 @@ def from_to l1, c1, l2, c2 # @param line [Integer] # @param column [Integer] # @return [AST::Node] - def node_at(line, column) + def node_at line, column tree_at(line, column).first end @@ -84,7 +84,7 @@ def node_at(line, column) # @param line [Integer] # @param column [Integer] # @return [Array] - def tree_at(line, column) + def tree_at line, column position = Position.new(line, column) stack = [] inner_tree_at node, position, stack @@ -140,7 +140,7 @@ def string_at? position # @sg-ignore Need to add nil check here return true if node.type == :str && range.include?(position) && range.start != position # @sg-ignore Need to add nil check here - return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position + return true if %i[STR str].include?(node.type) && range.include?(position) && range.start != position if node.type == :dstr inner = node_at(position.line, position.column) next if inner.nil? @@ -151,7 +151,7 @@ def string_at? position # @sg-ignore Need to add nil check here inner_code = at(Solargraph::Range.new(inner_range.start, position)) # @sg-ignore Need to add nil check here - return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') || + return true if (inner.type == :dstr && inner_range.ending.character <= position.character && !inner_code.end_with?('}')) || # @sg-ignore Need to add nil check here (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}')) end @@ -171,7 +171,7 @@ def string_ranges def comment_at? position comment_ranges.each do |range| return true if range.include?(position) || - (range.ending.line == position.line && range.ending.column < position.column) + (range.ending.line == position.line && range.ending.column < position.column) break if range.ending.line > position.line end false @@ -190,13 +190,13 @@ def error_ranges # @param node [Parser::AST::Node] # @return [String] - def code_for(node) + def code_for node rng = Range.from_node(node) # @sg-ignore Need to add nil check here b = Position.line_char_to_offset(code, rng.start.line, rng.start.column) # @sg-ignore Need to add nil check here e = Position.line_char_to_offset(code, rng.ending.line, rng.ending.column) - frag = code[b..e-1].to_s + frag = code[b..e - 1].to_s frag.strip.gsub(/,$/, '') end @@ -224,8 +224,8 @@ def location end FOLDING_NODE_TYPES = %i[ - class sclass module def defs if str dstr array while unless kwbegin hash block - ].freeze + class sclass module def defs if str dstr array while unless kwbegin hash block + ].freeze # Get an array of ranges that can be folded, e.g., the range of a class # definition or an if condition. @@ -293,9 +293,9 @@ def inner_folding_ranges top, result = [], parent = nil # @sg-ignore Translate to something flow sensitive typing understands range = Range.from_node(top) # @sg-ignore Need to add nil check here - if result.empty? || range.start.line > result.last.start.line + if (result.empty? || range.start.line > result.last.start.line) && !(range.ending.line - range.start.line < 2) # @sg-ignore Need to add nil check here - result.push range unless range.ending.line - range.start.line < 2 + result.push range end end # @sg-ignore Translate to something flow sensitive typing understands @@ -312,7 +312,7 @@ def stringify_comment_array comments ctxt = String.new('') started = false skip = nil - comments.lines.each { |l| + comments.lines.each do |l| # Trim the comment and minimum leading whitespace p = l.force_encoding('UTF-8').encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '') if p.strip.empty? @@ -325,7 +325,7 @@ def stringify_comment_array comments ctxt.concat p[skip..-1] end started = true - } + end ctxt end @@ -372,11 +372,11 @@ def string_nodes_in n result = [] if Parser.is_ast_node?(n) # @sg-ignore Translate to something flow sensitive typing understands - if n.type == :str || n.type == :dstr || n.type == :STR || n.type == :DSTR + if %i[str dstr STR DSTR].include?(n.type) result.push n else # @sg-ignore Translate to something flow sensitive typing understands - n.children.each{ |c| result.concat string_nodes_in(c) } + n.children.each { |c| result.concat string_nodes_in(c) } end end result @@ -390,13 +390,12 @@ def inner_tree_at node, position, stack return if node.nil? here = Range.from_node(node) # @sg-ignore Need to add nil check here - if here.contain?(position) - stack.unshift node - node.children.each do |c| - next unless Parser.is_ast_node?(c) - next if c.loc.expression.nil? - inner_tree_at(c, position, stack) - end + return unless here.contain?(position) + stack.unshift node + node.children.each do |c| + next unless Parser.is_ast_node?(c) + next if c.loc.expression.nil? + inner_tree_at(c, position, stack) end end @@ -425,7 +424,7 @@ def finalize @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename, 0) @parsed = true @repaired = @code - rescue Parser::SyntaxError, EncodingError => e + rescue Parser::SyntaxError, EncodingError @node = nil @comments = {} @parsed = false @@ -440,7 +439,7 @@ def finalize begin @node, @comments = Solargraph::Parser.parse_with_comments(@repaired, filename, 0) @parsed = true - rescue Parser::SyntaxError, EncodingError => e + rescue Parser::SyntaxError, EncodingError @node = nil @comments = {} @parsed = false @@ -453,7 +452,7 @@ def finalize # @param val [String] # @return [String] - def code=(val) + def code= val @code_lines = nil @finalized = false @code = val diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index 77c260d53..b083edc27 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -119,7 +119,9 @@ def define api_map, name_pin, locals pins = link.resolve(api_map, working_pin, locals) type = infer_from_definitions(pins, working_pin, api_map, locals) if type.undefined? - logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" } + logger.debug do + "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" + end return [] end # We continue to use the context from the head pin, in case @@ -128,7 +130,9 @@ def define api_map, name_pin, locals # for the binder, as this is chaining off of it, and the # binder is now the lhs of the rhs we are evaluating. working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain) - logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" } + logger.debug do + "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" + end end links.last.last_context = working_pin links.last.resolve(api_map, working_pin, locals) @@ -151,7 +155,9 @@ def infer api_map, name_pin, locals end # @todo Missed nil violation out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible - logger.debug { "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" } + logger.debug do + "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" + end @@inference_cache[cache_key] = out end @@ -162,13 +168,15 @@ def infer api_map, name_pin, locals def infer_uncached api_map, name_pin, locals pins = define(api_map, name_pin, locals) if pins.empty? - logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" } + logger.debug do + "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" + end return ComplexType::UNDEFINED end type = infer_from_definitions(pins, links.last.last_context, api_map, locals) out = maybe_nil(type) logger.debug do - "Chain#infer_uncached(links=#{self.links.map(&:desc)}, " \ + "Chain#infer_uncached(links=#{links.map(&:desc)}, " \ "locals=#{locals.map(&:desc)}, " \ "name_pin=#{name_pin}, " \ "name_pin.closure=#{name_pin&.closure.inspect}, " \ @@ -252,17 +260,13 @@ def infer_from_definitions pins, name_pin, api_map, locals end # Limit method inference recursion - if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method) - return ComplexType::UNDEFINED - end + return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method) @@inference_depth += 1 # @param pin [Pin::Base] unresolved_pins.each do |pin| # Avoid infinite recursion - if @@inference_stack.include?(pin.identity) - next - end + next if @@inference_stack.include?(pin.identity) @@inference_stack.push(pin.identity) type = pin.probe(api_map) diff --git a/lib/solargraph/source/chain/call.rb b/lib/solargraph/source/chain/call.rb index eb83ead98..d1e14e365 100644 --- a/lib/solargraph/source/chain/call.rb +++ b/lib/solargraph/source/chain/call.rb @@ -69,9 +69,7 @@ def resolve api_map, name_pin, locals [stack.first].compact end # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array - if !api_map.loose_unions && pin_groups.any? { |pins| pins.empty? } - pin_groups = [] - end + pin_groups = [] if !api_map.loose_unions && pin_groups.any? { |pins| pins.empty? } pins = pin_groups.flatten.uniq(&:path) return [] if pins.empty? inferred_pins(pins, api_map, name_pin, locals) @@ -128,38 +126,42 @@ def inferred_pins pins, api_map, name_pin, locals if ol.block && with_block? block_atypes = ol.block.parameters.map(&:return_type) # @todo Need to add nil check here - if block.links.map(&:class) == [BlockSymbol] - # like the bar in foo(&:bar) - blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals) - else - blocktype = block_call_type(api_map, name_pin, locals) - end + blocktype = if block.links.map(&:class) == [BlockSymbol] + # like the bar in foo(&:bar) + block_symbol_call_type(api_map, name_pin.context, block_atypes, locals) + else + block_call_type(api_map, name_pin, locals) + end end # @type new_signature_pin [Pin::Signature] - new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype) + new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, + blocktype) new_return_type = new_signature_pin.return_type - if head? - # If we're at the head of the chain, we called a - # method somewhere that marked itself as returning - # self. Given we didn't invoke this on an object, - # this must be a method in this same class - so we - # use our own self type - self_type = name_pin.context - else - # if we're past the head in the chain, whatever the - # type of the lhs side is what 'self' will be in its - # declaration - we can't just use the type of the - # method pin, as this might be a subclass of the - # place where the method is defined - self_type = name_pin.binder - end + self_type = if head? + # If we're at the head of the chain, we called a + # method somewhere that marked itself as returning + # self. Given we didn't invoke this on an object, + # this must be a method in this same class - so we + # use our own self type + name_pin.context + else + # if we're past the head in the chain, whatever the + # type of the lhs side is what 'self' will be in its + # declaration - we can't just use the type of the + # method pin, as this might be a subclass of the + # place where the method is defined + name_pin.binder + end # This same logic applies to the YARD work done by # 'with_params()'. # # qualify(), however, happens in the namespace where # the docs were written - from the method pin. # @todo Need to add nil check here - type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates) if new_return_type.defined? + if new_return_type.defined? + type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, + *p.gates) + end type ||= ComplexType::UNDEFINED end break if type.defined? @@ -177,8 +179,10 @@ def inferred_pins pins, api_map, name_pin, locals end p end - logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" } - out = result.map do |pin| + logger.debug do + "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" + end + result.map do |pin| if pin.path == 'Class#new' && name_pin.binder.tag != 'Class' reduced_context = name_pin.binder.reduce_class_type pin.proxy(reduced_context) @@ -234,7 +238,7 @@ def process_directive pin, api_map, context, locals # @param locals [::Array] # @return [Pin::ProxyType] def inner_process_macro pin, macro, api_map, context, locals - vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) } + vals = arguments.map { |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) } txt = macro.tag.text.clone # @sg-ignore Need to add nil check here if txt.empty? && macro.tag.name @@ -260,7 +264,7 @@ def inner_process_macro pin, macro, api_map, context, locals # @param context [ComplexType] # @return [ComplexType, nil] def extra_return_type docstring, context - if docstring.has_tag?('return_single_parameter') #&& context.subtypes.one? + if docstring.has_tag?('return_single_parameter') # && context.subtypes.one? return context.subtypes.first || ComplexType::UNDEFINED elsif docstring.has_tag?('return_value_parameter') && context.value_types.one? return context.value_types.first @@ -270,7 +274,7 @@ def extra_return_type docstring, context # @param name_pin [Pin::Base] # @return [Pin::Method, nil] - def find_method_pin(name_pin) + def find_method_pin name_pin method_pin = name_pin until method_pin.is_a?(Pin::Method) # @sg-ignore Need to support this in flow sensitive typing @@ -287,7 +291,7 @@ def super_pins api_map, name_pin method_pin = find_method_pin(name_pin) return [] if method_pin.nil? pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope) - pins.reject{|p| p.path == name_pin.path} + pins.reject { |p| p.path == name_pin.path } end # @param api_map [ApiMap] @@ -324,7 +328,7 @@ def fix_block_pass # @param block_parameter_types [::Array] # @param locals [::Array] # @return [ComplexType, nil] - def block_symbol_call_type(api_map, context, block_parameter_types, locals) + def block_symbol_call_type api_map, context, block_parameter_types, locals # Ruby's shorthand for sending the passed in method name # to the first yield parameter with no arguments # @sg-ignore Need to add nil check here @@ -342,7 +346,7 @@ def block_symbol_call_type(api_map, context, block_parameter_types, locals) # @param api_map [ApiMap] # @return [Pin::Block, nil] - def find_block_pin(api_map) + def find_block_pin api_map # @sg-ignore Need to add nil check here node_location = Solargraph::Location.from_node(block.node) return if node_location.nil? @@ -356,7 +360,7 @@ def find_block_pin(api_map) # @param block_parameter_types [::Array] # @param locals [::Array] # @return [ComplexType, nil] - def block_call_type(api_map, name_pin, locals) + def block_call_type api_map, name_pin, locals return nil unless with_block? block_pin = find_block_pin(api_map) diff --git a/lib/solargraph/source/chain/class_variable.rb b/lib/solargraph/source/chain/class_variable.rb index a804d89e5..f50028ffa 100644 --- a/lib/solargraph/source/chain/class_variable.rb +++ b/lib/solargraph/source/chain/class_variable.rb @@ -5,7 +5,7 @@ class Source class Chain class ClassVariable < Link def resolve api_map, name_pin, locals - api_map.get_class_variable_pins(name_pin.context.namespace).select{|p| p.name == word} + api_map.get_class_variable_pins(name_pin.context.namespace).select { |p| p.name == word } end end end diff --git a/lib/solargraph/source/chain/global_variable.rb b/lib/solargraph/source/chain/global_variable.rb index 0842803a9..335b8e42c 100644 --- a/lib/solargraph/source/chain/global_variable.rb +++ b/lib/solargraph/source/chain/global_variable.rb @@ -5,7 +5,7 @@ class Source class Chain class GlobalVariable < Link def resolve api_map, name_pin, locals - api_map.get_global_variable_pins.select{|p| p.name == word} + api_map.get_global_variable_pins.select { |p| p.name == word } end end end diff --git a/lib/solargraph/source/chain/if.rb b/lib/solargraph/source/chain/if.rb index db0b2481c..9a18c7948 100644 --- a/lib/solargraph/source/chain/if.rb +++ b/lib/solargraph/source/chain/if.rb @@ -21,7 +21,8 @@ def initialize links def resolve api_map, name_pin, locals types = @links.map { |link| link.infer(api_map, name_pin, locals) } - [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')), source: :chain)] + [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')), + source: :chain)] end end end diff --git a/lib/solargraph/source/chain/instance_variable.rb b/lib/solargraph/source/chain/instance_variable.rb index 2cb1a0ef8..60df51bfe 100644 --- a/lib/solargraph/source/chain/instance_variable.rb +++ b/lib/solargraph/source/chain/instance_variable.rb @@ -18,7 +18,9 @@ def initialize word, node, location # type ::Array<::Solargraph::Pin::BaseVariable, ::NilClass> # for Solargraph::Source::Chain::InstanceVariable#resolve def resolve api_map, name_pin, locals - ivars = api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select{|p| p.name == word} + ivars = api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select do |p| + p.name == word + end out = api_map.var_at_location(ivars, word, name_pin, location) [out].compact end diff --git a/lib/solargraph/source/chain/link.rb b/lib/solargraph/source/chain/link.rb index 344f7affd..e58030792 100644 --- a/lib/solargraph/source/chain/link.rb +++ b/lib/solargraph/source/chain/link.rb @@ -55,7 +55,7 @@ def to_s end def inspect - "#<#{self.class} - `#{self.desc}`>" + "#<#{self.class} - `#{desc}`>" end def head? @@ -88,7 +88,7 @@ def desc end def inspect - "#<#{self.class} - `#{self.desc}`>" + "#<#{self.class} - `#{desc}`>" end include Logging diff --git a/lib/solargraph/source/chain/literal.rb b/lib/solargraph/source/chain/literal.rb index 03c6149c1..243c96b87 100644 --- a/lib/solargraph/source/chain/literal.rb +++ b/lib/solargraph/source/chain/literal.rb @@ -23,7 +23,7 @@ def initialize type, node elsif node.type == :false @value = false # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check - elsif [:int, :sym].include?(node.type) + elsif %i[int sym].include?(node.type) # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check @value = node.children.first end diff --git a/lib/solargraph/source/chain/variable.rb b/lib/solargraph/source/chain/variable.rb index 975fbf6f5..8bf424e3b 100644 --- a/lib/solargraph/source/chain/variable.rb +++ b/lib/solargraph/source/chain/variable.rb @@ -5,7 +5,9 @@ class Source class Chain class Variable < Link def resolve api_map, name_pin, locals - api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select{|p| p.name == word} + api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select do |p| + p.name == word + end end end end diff --git a/lib/solargraph/source/chain/z_super.rb b/lib/solargraph/source/chain/z_super.rb index 5b0106c92..0021c4dbf 100644 --- a/lib/solargraph/source/chain/z_super.rb +++ b/lib/solargraph/source/chain/z_super.rb @@ -22,7 +22,7 @@ def initialize word, with_block = false # @param name_pin [Pin::Base] # @param locals [::Array] def resolve api_map, name_pin, locals - return super_pins(api_map, name_pin) + super_pins(api_map, name_pin) end end end diff --git a/lib/solargraph/source/change.rb b/lib/solargraph/source/change.rb index eb7a550c0..97df1c140 100644 --- a/lib/solargraph/source/change.rb +++ b/lib/solargraph/source/change.rb @@ -82,7 +82,7 @@ def commit text, insert start_offset = Position.to_offset(text, range.start) # @sg-ignore Need to add nil check here end_offset = Position.to_offset(text, range.ending) - (start_offset == 0 ? '' : text[0..start_offset-1].to_s) + normalize(insert) + text[end_offset..-1].to_s + (start_offset == 0 ? '' : text[0..start_offset - 1].to_s) + normalize(insert) + text[end_offset..-1].to_s end end end diff --git a/lib/solargraph/source/cursor.rb b/lib/solargraph/source/cursor.rb index 5ee9ac4b8..0b3b40606 100644 --- a/lib/solargraph/source/cursor.rb +++ b/lib/solargraph/source/cursor.rb @@ -39,11 +39,13 @@ def word # @return [String] def start_of_word @start_of_word ||= begin - match = source.code[0..offset-1].to_s.match(start_word_pattern) + match = source.code[0..offset - 1].to_s.match(start_word_pattern) result = (match ? match[0] : '') # Including the preceding colon if the word appears to be a symbol # @sg-ignore Need to add nil check here - result = ":#{result}" if source.code[0..offset-result.length-1].end_with?(':') and !source.code[0..offset-result.length-1].end_with?('::') + if source.code[0..offset - result.length - 1].end_with?(':') and !source.code[0..offset - result.length - 1].end_with?('::') + result = ":#{result}" + end result end end @@ -62,7 +64,7 @@ def end_of_word # @return [Boolean] def start_of_constant? - source.code[offset-2, 2] == '::' + source.code[offset - 2, 2] == '::' end # The range of the word at the current position. @@ -126,20 +128,18 @@ def node # @return [Position] def node_position - @node_position ||= begin - if start_of_word.empty? - # @sg-ignore Need to add nil check here - match = source.code[0, offset].match(/\s*(\.|:+)\s*$/) - if match - # @sg-ignore Need to add nil check here - Position.from_offset(source.code, offset - match[0].length) - else - position - end - else - position - end - end + @node_position ||= if start_of_word.empty? + # @sg-ignore Need to add nil check here + match = source.code[0, offset].match(/\s*(\.|:+)\s*$/) + if match + # @sg-ignore Need to add nil check here + Position.from_offset(source.code, offset - match[0].length) + else + position + end + else + position + end end # @return [Parser::AST::Node, nil] diff --git a/lib/solargraph/source/encoding_fixes.rb b/lib/solargraph/source/encoding_fixes.rb index 2ed70037c..0bb8d7f7e 100644 --- a/lib/solargraph/source/encoding_fixes.rb +++ b/lib/solargraph/source/encoding_fixes.rb @@ -10,13 +10,12 @@ module EncodingFixes # @param string [String] # @return [String] def normalize string - begin - string.dup.force_encoding('UTF-8') - rescue ::Encoding::CompatibilityError, ::Encoding::UndefinedConversionError, ::Encoding::InvalidByteSequenceError => e - # @todo Improve error handling - Solargraph::Logging.logger.warn "Normalize error: #{e.message}" - string - end + string.dup.force_encoding('UTF-8') + rescue ::Encoding::CompatibilityError, ::Encoding::UndefinedConversionError, + ::Encoding::InvalidByteSequenceError => e + # @todo Improve error handling + Solargraph::Logging.logger.warn "Normalize error: #{e.message}" + string end end end diff --git a/lib/solargraph/source/source_chainer.rb b/lib/solargraph/source/source_chainer.rb index aeffbeeec..5c235058a 100644 --- a/lib/solargraph/source/source_chainer.rb +++ b/lib/solargraph/source/source_chainer.rb @@ -32,10 +32,22 @@ def initialize source, position # @return [Source::Chain] def chain # Special handling for files that end with an integer and a period - return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/ + if phrase =~ /^[0-9]+\.$/ + return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])), + Chain::UNDEFINED_CALL]) + end # @sg-ignore Need to add nil check here - return Chain.new([Chain::Literal.new('Symbol', phrase[1..].to_sym)]) if phrase.start_with?(':') && !phrase.start_with?('::') - return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i) + if phrase.start_with?(':') && !phrase.start_with?('::') + return Chain.new([Chain::Literal.new('Symbol', + phrase[1..].to_sym)]) + end + if end_of_phrase.strip == '::' && source.code[Position.to_offset( + source.code, position + )].to_s.match?(/[a-z]/i) + return SourceChainer.chain(source, + Position.new(position.line, + position.character + 1)) + end begin return Chain.new([]) if phrase.end_with?('..') # @type [::Parser::AST::Node, nil] @@ -52,7 +64,12 @@ def chain elsif source.repaired? node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) else - node, parent = source.tree_at(fixed_position.line, fixed_position.column)[0..2] unless source.error_ranges.any?{|r| r.nil? || r.include?(fixed_position)} + unless source.error_ranges.any? do |r| + r.nil? || r.include?(fixed_position) + end + node, parent = source.tree_at(fixed_position.line, + fixed_position.column)[0..2] + end # Exception for positions that chain literal nodes in unsynchronized sources node = nil unless source.synchronized? || !Parser.infer_literal_node_type(node).nil? node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil? @@ -86,13 +103,13 @@ def chain # @sg-ignore Need to add nil check here # @return [String] def phrase - @phrase ||= source.code[signature_data..offset-1] + @phrase ||= source.code[signature_data..offset - 1] end # @sg-ignore Need to add nil check here # @return [String] def fixed_phrase - @fixed_phrase ||= phrase[0..-(end_of_phrase.length+1)] + @fixed_phrase ||= phrase[0..-(end_of_phrase.length + 1)] end # @return [Position] @@ -144,7 +161,7 @@ def get_signature_data_at index brackets = 0 squares = 0 parens = 0 - index -=1 + index -= 1 in_whitespace = false while index >= 0 pos = Position.from_offset(@source.code, index) @@ -155,21 +172,19 @@ def get_signature_data_at index if brackets.zero? and parens.zero? and squares.zero? and [' ', "\r", "\n", "\t"].include?(char) in_whitespace = true else - if brackets.zero? and parens.zero? and squares.zero? and in_whitespace + # @sg-ignore Need to add nil check here + if brackets.zero? and parens.zero? and squares.zero? and in_whitespace && !(char == '.' or @source.code[index + 1..-1].strip.start_with?('.')) + @source.code[index + 1..-1] # @sg-ignore Need to add nil check here - unless char == '.' or @source.code[index+1..-1].strip.start_with?('.') - old = @source.code[index+1..-1] - # @sg-ignore Need to add nil check here - nxt = @source.code[index+1..-1].lstrip - # @sg-ignore Need to add nil check here - index += (@source.code[index+1..-1].length - @source.code[index+1..-1].lstrip.length) - break - end + @source.code[index + 1..-1].lstrip + # @sg-ignore Need to add nil check here + index += (@source.code[index + 1..-1].length - @source.code[index + 1..-1].lstrip.length) + break end if char == ')' - parens -=1 + parens -= 1 elsif char == ']' - squares -=1 + squares -= 1 elsif char == '}' brackets -= 1 elsif char == '(' @@ -185,9 +200,7 @@ def get_signature_data_at index break if char == '$' if char == '@' index -= 1 - if @source.code[index, 1] == '@' - index -= 1 - end + index -= 1 if @source.code[index, 1] == '@' break end elsif parens == 1 || brackets == 1 || squares == 1 diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index 714970f21..b22a058f9 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -62,7 +62,9 @@ def pins_by_class klass # # @return [Integer] def api_hash - @api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select { |pin| pin.namespace.to_s > '' } + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node) + locals).hash + @api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select do |pin| + pin.namespace.to_s > '' + end + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node) + locals).hash end # @return [String, nil] @@ -144,7 +146,7 @@ def references name # @param location [Location] # @return [Array] - def locals_at(location) + def locals_at location return [] if location.filename != filename closure = locate_closure_pin(location.range.start.line, location.range.start.character) locals.select { |pin| pin.visible_at?(closure, location) } @@ -209,7 +211,9 @@ def _locate_pin line, character, *klasses # there's probably a better way to handle it next if pin.is_a?(Pin::Method) && pin.attribute? # @sg-ignore Need to add nil check here - found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position) + found = pin if (klasses.empty? || klasses.any? do |kls| + pin.is_a?(kls) + end) && pin.location.range.contain?(position) # @sg-ignore Need to add nil check here break if pin.location.range.start.line > line end diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index b2b4d2b5d..2e48b0c89 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -13,7 +13,9 @@ def initialize api_map, cursor @cursor = cursor closure_pin = closure # @sg-ignore Need to add nil check here - closure_pin.rebind(api_map) if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver).contain?(cursor.range.start) + if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver).contain?(cursor.range.start) + closure_pin.rebind(api_map) + end end # @return [Array] Relevant pins for infering the type of the Cursor's position @@ -22,7 +24,11 @@ def define result = cursor.chain.define(api_map, closure, locals) result.concat file_global_methods # @sg-ignore Need to add nil check here - result.concat((source_map.pins + source_map.locals).select{ |p| p.name == cursor.word && p.location.range.contain?(cursor.position) }) if result.empty? + if result.empty? + result.concat((source_map.pins + source_map.locals).select do |p| + p.name == cursor.word && p.location.range.contain?(cursor.position) + end) + end result end @@ -34,7 +40,9 @@ def types # @return [Completion] def complete return package_completions([]) if !source_map.source.parsed? || cursor.string? - return package_completions(api_map.get_symbols) if cursor.chain.literal? && cursor.chain.links.last.word == '' + if cursor.chain.literal? && cursor.chain.links.last.word == '' + return package_completions(api_map.get_symbols) + end return Completion.new([], cursor.range) if cursor.chain.literal? if cursor.comment? tag_complete @@ -128,12 +136,11 @@ def complete_keyword_parameters next unless param.keyword? result.push Pin::KeywordParam.new(pin.location, "#{param.name}:") end - if !pin.parameters.empty? && pin.parameters.last.kwrestarg? - pin.docstring.tags(:param).each do |tag| - next if done.include?(tag.name) - done.push tag.name - result.push Pin::KeywordParam.new(pin.location, "#{tag.name}:") - end + next unless !pin.parameters.empty? && pin.parameters.last.kwrestarg? + pin.docstring.tags(:param).each do |tag| + next if done.include?(tag.name) + done.push tag.name + result.push Pin::KeywordParam.new(pin.location, "#{tag.name}:") end end result @@ -143,10 +150,10 @@ def complete_keyword_parameters # @return [Completion] def package_completions result frag_start = cursor.start_of_word.to_s.downcase - filtered = result.uniq(&:name).select { |s| + filtered = result.uniq(&:name).select do |s| s.name.downcase.start_with?(frag_start) && - (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i)) - } + (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(!|\?|=)?$/i)) + end Completion.new(filtered, cursor.range) end @@ -154,7 +161,7 @@ def package_completions result def tag_complete result = [] # @sg-ignore Need to add nil check here - match = source_map.code[0..cursor.offset-1].match(/[\[<, ]([a-z0-9_:]*)\z/i) + match = source_map.code[0..cursor.offset - 1].match(/[\[<, ]([a-z0-9_:]*)\z/i) if match # @sg-ignore Need to add nil check here full = match[1] @@ -170,7 +177,7 @@ def tag_complete end else # @sg-ignore Need to add nil check here - result.concat api_map.get_constants('', full.end_with?('::') ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) } + result.concat api_map.get_constants('', full.end_with?('::') ? '' : context_pin.full_context.namespace, *gates) # .select { |pin| pin.name.start_with?(full) } end end package_completions(result) @@ -183,25 +190,24 @@ def code_complete if cursor.chain.constant? || cursor.start_of_constant? full = cursor.chain.links.first.word type = if cursor.chain.undefined? - cursor.chain.base.infer(api_map, context_pin, locals) - else - if full.include?('::') && cursor.chain.links.length == 1 - # @sg-ignore Need to add nil check here - ComplexType.try_parse(full.split('::')[0..-2].join('::')) - elsif cursor.chain.links.length > 1 - ComplexType.try_parse(full) - else - ComplexType::UNDEFINED - end - end + cursor.chain.base.infer(api_map, context_pin, locals) + elsif full.include?('::') && cursor.chain.links.length == 1 + # @sg-ignore Need to add nil check here + ComplexType.try_parse(full.split('::')[0..-2].join('::')) + elsif cursor.chain.links.length > 1 + ComplexType.try_parse(full) + else + ComplexType::UNDEFINED + end if type.undefined? if full.include?('::') result.concat api_map.get_constants(full, *gates) else - result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) } + result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) # .select { |pin| pin.name.start_with?(full) } end else - result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) + result.concat api_map.get_constants(type.namespace, + cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) end else type = cursor.chain.base.infer(api_map, closure, locals) @@ -210,14 +216,16 @@ def code_complete if cursor.word.start_with?('@@') return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace)) elsif cursor.word.start_with?('@') - return package_completions(api_map.get_instance_variable_pins(closure.full_context.namespace, closure.context.scope)) + return package_completions(api_map.get_instance_variable_pins(closure.full_context.namespace, + closure.context.scope)) elsif cursor.word.start_with?('$') return package_completions(api_map.get_global_variable_pins) end result.concat locals result.concat file_global_methods unless closure.binder.namespace.empty? result.concat api_map.get_constants(context_pin.context.namespace, *gates) - result.concat api_map.get_methods(closure.binder.namespace, scope: closure.binder.scope, visibility: [:public, :private, :protected]) + result.concat api_map.get_methods(closure.binder.namespace, scope: closure.binder.scope, + visibility: %i[public private protected]) result.concat api_map.get_methods('Kernel') result.concat api_map.keyword_pins.to_a end diff --git a/lib/solargraph/source_map/mapper.rb b/lib/solargraph/source_map/mapper.rb index e6b8f6878..5d8d66a9c 100644 --- a/lib/solargraph/source_map/mapper.rb +++ b/lib/solargraph/source_map/mapper.rb @@ -12,7 +12,7 @@ class Mapper private_class_method :new - DIRECTIVE_REGEXP = /(@\!method|@\!attribute|@\!visibility|@\!domain|@\!macro|@\!parse|@\!override)/.freeze + DIRECTIVE_REGEXP = /(@!method|@!attribute|@!visibility|@!domain|@!macro|@!parse|@!override)/ # Generate the data. # @@ -29,10 +29,10 @@ def map source @locals.each { |l| l.source = :code } process_comment_directives [@pins, @locals] - # rescue Exception => e - # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}" - # Solargraph.logger.warn e.backtrace.join("\n") - # [[], []] + # rescue Exception => e + # Solargraph.logger.warn "Error mapping #{source.filename}: [#{e.class}] #{e.message}" + # Solargraph.logger.warn e.backtrace.join("\n") + # [[], []] end # @param filename [String] @@ -63,9 +63,9 @@ def pins # @param position [Solargraph::Position] # @return [Solargraph::Pin::Closure] - def closure_at(position) + def closure_at position # @sg-ignore Need to add nil check here - pins.select{|pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position)}.last + pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position) }.last end # @param source_position [Position] @@ -116,9 +116,7 @@ def process_directive source_position, comment_position, directive namespace = closure_at(source_position) || @pins.first # @todo Missed nil violation # @todo Need to add nil check here - if namespace.location.range.start.line < comment_position.line - namespace = closure_at(comment_position) - end + namespace = closure_at(comment_position) if namespace.location.range.start.line < comment_position.line begin src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename) region = Parser::Region.new(source: src, closure: namespace) @@ -128,19 +126,20 @@ def process_directive source_position, comment_position, directive return if gen_pin.nil? # Move the location to the end of the line so it gets recognized # as originating from a comment - shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].to_s.chomp.length) + shifted = Solargraph::Position.new(comment_position.line, + @code.lines[comment_position.line].to_s.chomp.length) # @todo: Smelly instance variable access gen_pin.instance_variable_set(:@comments, docstring.all.to_s) gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted))) gen_pin.instance_variable_set(:@explicit, false) @pins.push gen_pin - rescue Parser::SyntaxError => e + rescue Parser::SyntaxError # @todo Handle error in directive end when 'attribute' return if directive.tag.name.nil? namespace = closure_at(source_position) - t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('') + t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.flatten.join('') if t.nil? || t.include?('r') pins.push Solargraph::Pin::Method.new( location: location, @@ -166,34 +165,36 @@ def process_directive source_position, comment_position, directive source: :source_map ) pins.push method_pin - method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map) + method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, + source: :source_map) if pins.last.return_type.defined? - pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value') + pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), + 'value') end end when 'visibility' - kind = directive.tag.text&.to_sym - # @sg-ignore Need to look at Tuple#include? handling - return unless [:private, :protected, :public].include?(kind) + kind = directive.tag.text&.to_sym + # @sg-ignore Need to look at Tuple#include? handling + return unless %i[private protected public].include?(kind) - name = directive.tag.name - closure = closure_at(source_position) || @pins.first - # @todo Missed nil violation - # @todo Need to add nil check here - if closure.location.range.start.line < comment_position.line - closure = closure_at(comment_position) + name = directive.tag.name + closure = closure_at(source_position) || @pins.first + # @todo Missed nil violation + # @todo Need to add nil check here + closure = closure_at(comment_position) if closure.location.range.start.line < comment_position.line + if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line) + # @todo Smelly instance variable access + closure.instance_variable_set(:@visibility, kind) + else + matches = pins.select do |pin| + pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance end - if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line) + matches.each do |pin| # @todo Smelly instance variable access - closure.instance_variable_set(:@visibility, kind) - else - matches = pins.select{ |pin| pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance } - matches.each do |pin| - # @todo Smelly instance variable access - pin.instance_variable_set(:@visibility, kind) - end + pin.instance_variable_set(:@visibility, kind) end + end when 'parse' begin @@ -204,10 +205,10 @@ def process_directive source_position, comment_position, directive # @todo These pins may need to be marked not explicit index = @pins.length loff = if @code.lines[comment_position.line].strip.end_with?('@!parse') - comment_position.line + 1 - else - comment_position.line - end + comment_position.line + 1 + else + comment_position.line + end locals = [] ivars = [] Parser.process_node(src.node, region, @pins, locals, ivars) @@ -218,7 +219,7 @@ def process_directive source_position, comment_position, directive p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff) p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff) end - rescue Parser::SyntaxError => e + rescue Parser::SyntaxError # @todo Handle parser errors in !parse directives end when 'domain' @@ -237,7 +238,7 @@ def process_directive source_position, comment_position, directive # @param line1 [Integer] # @param line2 [Integer] # @sg-ignore Need to add nil check here - def no_empty_lines?(line1, line2) + def no_empty_lines? line1, line2 # @sg-ignore Need to add nil check here @code.lines[line1..line2].none? { |line| line.strip.empty? } end @@ -248,7 +249,7 @@ def remove_inline_comment_hashes comment ctxt = '' num = nil started = false - comment.lines.each { |l| + comment.lines.each do |l| # Trim the comment and minimum leading whitespace p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '') if num.nil? && !p.strip.empty? @@ -260,7 +261,7 @@ def remove_inline_comment_hashes comment num = cur if cur < num end ctxt += "#{p[num..-1]}" if started - } + end ctxt end @@ -269,7 +270,14 @@ def process_comment_directives return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ DIRECTIVE_REGEXP code_lines = @code.lines @source.associated_comments.each do |line, comments| - src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0) + src_pos = if line + Position.new(line, + code_lines[line].to_s.chomp.index(/[^\s]/) || 0) + else + Position.new( + code_lines.length, 0 + ) + end # @sg-ignore Need to add nil check here com_pos = Position.new(line + 1 - comments.lines.length, 0) process_comment(src_pos, com_pos, comments) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index fb76ba7b5..ea914644f 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -52,26 +52,26 @@ def source # @param inferred [ComplexType, ComplexType::UniqueType] # @param expected [ComplexType, ComplexType::UniqueType] - def return_type_conforms_to?(inferred, expected) + def return_type_conforms_to? inferred, expected conforms_to?(inferred, expected, :return_type) end # @param inferred [ComplexType, ComplexType::UniqueType] # @param expected [ComplexType, ComplexType::UniqueType] - def arg_conforms_to?(inferred, expected) + def arg_conforms_to? inferred, expected conforms_to?(inferred, expected, :method_call) end # @param inferred [ComplexType, ComplexType::UniqueType] # @param expected [ComplexType, ComplexType::UniqueType] - def assignment_conforms_to?(inferred, expected) + def assignment_conforms_to? inferred, expected conforms_to?(inferred, expected, :assignment) end # @param inferred [ComplexType, ComplexType::UniqueType] # @param expected [ComplexType, ComplexType::UniqueType] # @param scenario [Symbol] - def conforms_to?(inferred, expected, scenario) + def conforms_to? inferred, expected, scenario rules_arr = [] rules_arr << :allow_empty_params unless rules.require_inferred_type_params? rules_arr << :allow_any_match unless rules.require_all_unique_types_match_expected? @@ -86,12 +86,12 @@ def conforms_to?(inferred, expected, scenario) # @return [Array] def problems @problems ||= begin - all = method_tag_problems - .concat(variable_type_tag_problems) - .concat(const_problems) - .concat(call_problems) - unignored = without_ignored(all) - unignored.concat(unneeded_sgignore_problems) + all = method_tag_problems + .concat(variable_type_tag_problems) + .concat(const_problems) + .concat(call_problems) + unignored = without_ignored(all) + unignored.concat(unneeded_sgignore_problems) end end @@ -148,7 +148,10 @@ def method_return_type_problems_for pin if pin.return_type.undefined? && rules.require_type_tags? if pin.attribute? inferred = pin.probe(api_map).self_to_type(pin.full_context) - result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined? + unless inferred.defined? + result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", + pin: pin) + end else result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) end @@ -167,7 +170,8 @@ def method_return_type_problems_for pin end else unless return_type_conforms_to?(inferred, declared) - result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin) + result.push Problem.new(pin.location, + "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin) end end end @@ -183,7 +187,7 @@ def method_return_type_problems_for pin def resolved_constant? pin return true if pin.typify(api_map).defined? constant_pins = api_map.get_constants('', *pin.closure.gates) - .select { |p| p.name == pin.return_type.namespace } + .select { |p| p.name == pin.return_type.namespace } return true if constant_pins.find { |p| p.typify(api_map).defined? } # will need to probe when a constant name is assigned to a # class/module (alias) @@ -205,19 +209,19 @@ def method_param_type_problems_for pin pin.signatures.each do |sig| params = param_details_from_stack(sig, stack) if rules.require_type_tags? - sig.parameters.each do |par| - break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg - unless params[par.name] - if pin.attribute? - inferred = pin.probe(api_map).self_to_type(pin.full_context) - if inferred.undefined? - result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) - end - else + sig.parameters.each do |par| + break if %i[restarg kwrestarg blockarg].include?(par.decl) + unless params[par.name] + if pin.attribute? + inferred = pin.probe(api_map).self_to_type(pin.full_context) + if inferred.undefined? result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) end + else + result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin) end end + end end # @param name [String] # @param data [Hash{Symbol => BasicObject}] @@ -225,7 +229,8 @@ def method_param_type_problems_for pin # @type [ComplexType] type = data[:qualified] if type.undefined? - result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin) + result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", + pin: pin) end end end @@ -257,20 +262,20 @@ def variable_type_tag_problems end else unless assignment_conforms_to?(inferred, declared) - result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin) + result.push Problem.new(pin.location, + "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin) end end elsif declared_externally?(pin) ignored_pins.push pin end elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin) - result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin) + result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", + pin: pin) end elsif pin.assignment inferred = pin.probe(api_map) - if inferred.undefined? && declared_externally?(pin) - ignored_pins.push pin - end + ignored_pins.push pin if inferred.undefined? && declared_externally?(pin) end end result @@ -313,7 +318,6 @@ def call_problems chain = Solargraph::Parser.chain(call, filename) # @sg-ignore Need to add nil check here closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column) - namespace_pin = closure_pin if call.type == :block # blocks in the AST include the method call as well, so the # node returned by #call_nodes_from needs to be backed out @@ -336,7 +340,6 @@ def call_problems found = nil # @type [Array] all_found = [] - closest = ComplexType::UNDEFINED until base.links.first.undefined? # @sg-ignore Need to add nil check here all_found = base.define(api_map, closure_pin, locals) @@ -348,16 +351,14 @@ def call_problems all_closest = all_found.map { |pin| pin.typify(api_map) } closest = ComplexType.new(all_closest.flat_map(&:items).uniq) # @todo remove the internal_or_core? check at a higher-than-strict level - if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found)) - # @sg-ignore Need to add nil check here - unless closest.generic? || ignored_pins.include?(found) - if closest.defined? - result.push Problem.new(location, "Unresolved call to #{missing.links.last.word} on #{closest}") - else - result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}") - end - @marked_ranges.push rng + # @sg-ignore Need to add nil check here + if (!found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))) && !(closest.generic? || ignored_pins.include?(found)) + if closest.defined? + result.push Problem.new(location, "Unresolved call to #{missing.links.last.word} on #{closest}") + else + result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}") end + @marked_ranges.push rng end end # @sg-ignore Need to add nil check here @@ -441,7 +442,7 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum # when possible, and when not, ensure provably # incorrect situations are detected. sig.parameters.each_with_index do |par, idx| - return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing + return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing argchain = arguments[idx] if argchain.nil? if par.decl == :arg @@ -454,18 +455,13 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum end else final_arg = arguments.last - argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type) + argchain = final_arg if final_arg && %i[kwsplat hash].include?(final_arg.node.type) end end if argchain - if par.decl != :arg - errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx - next - else - if argchain.node.type == :splat && argchain == arguments.last - final_arg = argchain - end - if (final_arg && final_arg.node.type == :splat) + if par.decl == :arg + final_arg = argchain if argchain.node.type == :splat && argchain == arguments.last + if final_arg && final_arg.node.type == :splat # The final argument given has been seen and was a # splat, which doesn't give us useful types or # arities against positional parameters, so let's @@ -490,10 +486,14 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum argtype = argchain.infer(api_map, closure_pin, locals) argtype = argtype.self_to_type(closure_pin.context) if argtype.defined? && ptype.defined? && !arg_conforms_to?(argtype, ptype) - errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") + errors.push Problem.new(location, + "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") return errors end end + else + errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx + next end elsif par.decl == :kwarg errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}") @@ -522,27 +522,26 @@ def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pi argchain = kwargs[par.name.to_sym] if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}') result.concat kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs) - else - if argchain - data = params[par.name] - if data.nil? - # @todo Some level (strong, I guess) should require the param here - else - # @type [ComplexType, ComplexType::UniqueType] - ptype = data[:qualified] - ptype = ptype.self_to_type(pin.context) - unless ptype.undefined? - # @type [ComplexType] - argtype = argchain.infer(api_map, closure_pin, locals).self_to_type(closure_pin.context) - # @todo Unresolved call to defined? - if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) - result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") - end + elsif argchain + data = params[par.name] + if data.nil? + # @todo Some level (strong, I guess) should require the param here + else + # @type [ComplexType, ComplexType::UniqueType] + ptype = data[:qualified] + ptype = ptype.self_to_type(pin.context) + unless ptype.undefined? + # @type [ComplexType] + argtype = argchain.infer(api_map, closure_pin, locals).self_to_type(closure_pin.context) + # @todo Unresolved call to defined? + if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) + result.push Problem.new(location, + "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}") end end - elsif par.decl == :kwarg - result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}") end + elsif par.decl == :kwarg + result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}") end result end @@ -555,7 +554,7 @@ def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pi # @param params [Hash{String => [nil, Hash]}] # @param kwargs [Hash{Symbol => Source::Chain}] # @return [Array] - def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs) + def kwrestarg_problems_for api_map, closure_pin, locals, location, pin, params, kwargs result = [] kwargs.each_pair do |pname, argchain| next unless params.key?(pname.to_s) @@ -565,7 +564,8 @@ def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, argtype = argchain.infer(api_map, closure_pin, locals) argtype = argtype.self_to_type(closure_pin.context) if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype) - result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") + result.push Problem.new(location, + "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}") end end result @@ -575,7 +575,7 @@ def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, # @param pin [Pin::Method, Pin::Signature] # @param relevant_pin [Pin::Method, Pin::Signature] the pin which is under inspection # @return [void] - def add_restkwarg_param_tag_details(param_details, pin, relevant_pin) + def add_restkwarg_param_tag_details param_details, pin, relevant_pin # see if we have additional tags to pay attention to from YARD - # e.g., kwargs in a **restkwargs splat tags = pin.docstring.tags(:param) @@ -596,7 +596,7 @@ def add_restkwarg_param_tag_details(param_details, pin, relevant_pin) # @param pin [Pin::Signature] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] - def signature_param_details(pin) + def signature_param_details pin # @type [Hash{String => Hash{Symbol => String, ComplexType}}] result = {} pin.parameters.each do |param| @@ -630,7 +630,7 @@ def signature_param_details(pin) # @param new_param_details [Hash{String => Hash{Symbol => String, ComplexType}}] # # @return [void] - def add_to_param_details(param_details, param_names, new_param_details) + def add_to_param_details param_details, param_names, new_param_details new_param_details.each do |param_name, details| next unless param_names.include?(param_name) @@ -643,7 +643,7 @@ def add_to_param_details(param_details, param_names, new_param_details) # @param signature [Pin::Signature] # @param method_pin_stack [Array] # @return [Hash{String => Hash{Symbol => String, ComplexType}}] - def param_details_from_stack(signature, method_pin_stack) + def param_details_from_stack signature, method_pin_stack signature_type = signature.typify(api_map) signature = signature.proxy signature_type param_details = signature_param_details(signature) @@ -683,7 +683,7 @@ def external? pin # @param pin [Pin::BaseVariable] def declared_externally? pin - raise "No assignment found" if pin.assignment.nil? + raise 'No assignment found' if pin.assignment.nil? chain = Solargraph::Parser.chain(pin.assignment, filename) # @sg-ignore flow sensitive typing needs to handle attrs @@ -696,24 +696,19 @@ def declared_externally? pin type = chain.infer(api_map, closure_pin, locals) if type.undefined? && !rules.ignore_all_undefined? base = chain - missing = chain # @type [Solargraph::Pin::Base, nil] found = nil # @type [Array] all_found = [] - closest = ComplexType::UNDEFINED until base.links.first.undefined? all_found = base.define(api_map, closure_pin, locals) found = all_found.first break if found - missing = base base = base.base end all_closest = all_found.map { |pin| pin.typify(api_map) } closest = ComplexType.new(all_closest.flat_map(&:items).uniq) - if !found || closest.defined? || internal?(found) - return false - end + return false if !found || closest.defined? || internal?(found) end true end @@ -736,7 +731,7 @@ def arity_problems_for pin, arguments, location # @param arguments [Array] # @param location [Location] # @return [Array] - def parameterized_arity_problems_for(pin, parameters, arguments, location) + def parameterized_arity_problems_for pin, parameters, arguments, location return [] unless pin.explicit? return [] if parameters.empty? && arguments.empty? return [] if pin.anon_splat? @@ -751,7 +746,7 @@ def parameterized_arity_problems_for(pin, parameters, arguments, location) settled_kwargs = parameters.count(&:keyword?) else kwargs = convert_hash(unchecked.last.node) - if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? } + if parameters.any? { |param| %i[kwarg kwoptarg].include?(param.decl) || param.kwrestarg? } if kwargs.empty? add_params += 1 else @@ -780,10 +775,12 @@ def parameterized_arity_problems_for(pin, parameters, arguments, location) return [] if parameters.any?(&:rest?) opt = optional_param_count(parameters) return [] if unchecked.length <= req + opt - if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any? + if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & %i[ + kwarg kwoptarg kwrestarg + ]).any? return [] end - return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length + return [] if arguments.length - req == parameters.select { |p| %i[optarg kwoptarg].include?(p.decl) }.length return [Problem.new(location, "Too many arguments to #{pin.path}")] elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash))) # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs. @@ -799,14 +796,14 @@ def parameterized_arity_problems_for(pin, parameters, arguments, location) # @todo need to use generic types in method to choose correct # signature and generate Integer as return type # @return [Integer] - def required_param_count(parameters) + def required_param_count parameters parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 } end # @param parameters [Enumerable] # @param pin [Pin::Method] # @return [Integer] - def optional_param_count(parameters) + def optional_param_count parameters parameters.select { |p| p.decl == :optarg }.length end @@ -820,14 +817,14 @@ def abstract? pin # @param pin [Pin::Method] # @return [Array] - def fake_args_for(pin) + def fake_args_for pin args = [] with_opts = false with_block = false # @param pin [Pin::Parameter] pin.parameters.each do |pin| # @sg-ignore flow sensitive typing should be able to handle redefinition - if [:kwarg, :kwoptarg, :kwrestarg].include?(pin.decl) + if %i[kwarg kwoptarg kwrestarg].include?(pin.decl) with_opts = true # @sg-ignore flow sensitive typing should be able to handle redefinition elsif pin.decl == :block diff --git a/lib/solargraph/type_checker/rules.rb b/lib/solargraph/type_checker/rules.rb index d551b066d..a0af63aec 100644 --- a/lib/solargraph/type_checker/rules.rb +++ b/lib/solargraph/type_checker/rules.rb @@ -23,11 +23,11 @@ class Rules # @param overrides [Hash{Symbol => Symbol}] def initialize level, overrides @rank = if LEVELS.key?(level) - LEVELS[level] - else - Solargraph.logger.warn "Unrecognized TypeChecker level #{level}, assuming normal" - 0 - end + LEVELS[level] + else + Solargraph.logger.warn "Unrecognized TypeChecker level #{level}, assuming normal" + 0 + end @level = LEVELS[LEVELS.values.index(@rank)] @overrides = overrides end @@ -149,7 +149,7 @@ def validate_sg_ignores? # @param type [Symbol] # @param level [Symbol] - def report?(type, level) + def report? type, level rank >= LEVELS[@overrides.fetch(type, level)] end end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index f3de99242..acedf6375 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -127,7 +127,7 @@ def yard_plugins # @param level [Symbol] # @return [TypeChecker::Rules] - def rules(level) + def rules level @rules ||= TypeChecker::Rules.new(level, config.type_checker_rules) end @@ -147,7 +147,7 @@ def merge *sources includes_any = false sources.each do |source| # @sg-ignore Need to add nil check here - next unless directory == "*" || config.calculated.include?(source.filename) + next unless directory == '*' || config.calculated.include?(source.filename) # @sg-ignore Need to add nil check here source_hash[source.filename] = source @@ -199,7 +199,7 @@ def source filename def would_require? path require_paths.each do |rp| full = File.join rp, path - return true if File.file?(full) || File.file?(full << ".rb") + return true if File.file?(full) || File.file?(full << '.rb') end false end @@ -229,11 +229,9 @@ def rbs_collection_path # @return [String, nil] def rbs_collection_config_path @rbs_collection_config_path ||= - begin - unless directory.empty? || directory == '*' - yaml_file = File.join(directory, 'rbs_collection.yaml') - yaml_file if File.file?(yaml_file) - end + unless directory.empty? || directory == '*' + yaml_file = File.join(directory, 'rbs_collection.yaml') + yaml_file if File.file?(yaml_file) end end @@ -278,7 +276,7 @@ def cache_all_for_workspace! out, rebuild: false # which are likely to be newer and have more pins pin_cache.cache_all_stdlibs(out: out, rebuild: rebuild) - out&.puts "Documentation cached for core, standard library and gems." + out&.puts 'Documentation cached for core, standard library and gems.' end # Synchronize the workspace from the provided updater. @@ -323,30 +321,25 @@ def source_hash # @return [void] def load_sources source_hash.clear - unless directory.empty? || directory == '*' - size = config.calculated.length - if config.max_files > 0 and size > config.max_files - raise WorkspaceTooLargeError, - "The workspace is too large to index (#{size} files, #{config.max_files} max)" - end - config.calculated.each do |filename| - begin - source_hash[filename] = Solargraph::Source.load(filename) - rescue Errno::ENOENT => e - Solargraph.logger.warn("Error loading #{filename}: [#{e.class}] #{e.message}") - end - end + return if directory.empty? || directory == '*' + size = config.calculated.length + if config.max_files > 0 and size > config.max_files + raise WorkspaceTooLargeError, + "The workspace is too large to index (#{size} files, #{config.max_files} max)" + end + config.calculated.each do |filename| + source_hash[filename] = Solargraph::Source.load(filename) + rescue Errno::ENOENT => e + Solargraph.logger.warn("Error loading #{filename}: [#{e.class}] #{e.message}") end end # @return [void] def require_plugins config.plugins.each do |plugin| - begin - require plugin - rescue LoadError - Solargraph.logger.warn "Failed to load plugin '#{plugin}'" - end + require plugin + rescue LoadError + Solargraph.logger.warn "Failed to load plugin '#{plugin}'" end end diff --git a/lib/solargraph/workspace/config.rb b/lib/solargraph/workspace/config.rb index 31e065e17..bd494b380 100644 --- a/lib/solargraph/workspace/config.rb +++ b/lib/solargraph/workspace/config.rb @@ -54,7 +54,9 @@ def allow? filename # # @return [Array] def calculated - Solargraph.logger.info "Indexing workspace files in #{directory}" unless @calculated || directory.empty? || directory == '*' + unless @calculated || directory.empty? || directory == '*' + Solargraph.logger.info "Indexing workspace files in #{directory}" + end @calculated ||= included - excluded end @@ -146,7 +148,7 @@ def config_data global_config = read_config(global_config_path) defaults = default_config - defaults.merge({'exclude' => []}) unless workspace_config.nil? + defaults.merge({ 'exclude' => [] }) unless workspace_config.nil? defaults .merge(global_config || {}) @@ -160,7 +162,7 @@ def config_data def read_config config_path = '' return nil if config_path.empty? return nil unless File.file?(config_path) - YAML.safe_load(File.read(config_path)) + YAML.safe_load_file(config_path) end # @return [Hash{String => Array, Hash, Integer}] @@ -176,11 +178,11 @@ def default_config 'cops' => 'safe', 'except' => [], 'only' => [], - 'extra_args' =>[] + 'extra_args' => [] } }, 'type_checker' => { - 'rules' => { } + 'rules' => {} }, 'require_paths' => [], 'plugins' => [], @@ -193,12 +195,11 @@ def default_config # @param globs [Array] # @return [Array] def process_globs globs - result = globs.flat_map do |glob| + globs.flat_map do |glob| Dir[File.absolute_path(glob, directory)] - .map{ |f| f.gsub(/\\/, '/') } + .map { |f| f.gsub('\\', '/') } .select { |f| File.file?(f) } end - result end # Modify the included files based on excluded directories and get an @@ -241,7 +242,7 @@ def glob_is_directory? glob # @param glob [String] # @return [String] def glob_to_directory glob - glob.gsub(/(\/\*|\/\*\*\/\*\*?)$/, '') + glob.gsub(%r{(/\*|/\*\*/\*\*?)$}, '') end # @return [Array] diff --git a/lib/solargraph/workspace/gemspecs.rb b/lib/solargraph/workspace/gemspecs.rb index c6af306dd..2389c2d76 100644 --- a/lib/solargraph/workspace/gemspecs.rb +++ b/lib/solargraph/workspace/gemspecs.rb @@ -117,7 +117,7 @@ def find_gem name, version = nil, out: $stderr # # @return [Array] def fetch_dependencies gemspec, out: $stderr - gemspecs = all_gemspecs_from_bundle + all_gemspecs_from_bundle # @type [Hash{String => Gem::Specification}] deps_so_far = {} diff --git a/lib/solargraph/yard_map/helpers.rb b/lib/solargraph/yard_map/helpers.rb index cd4be9acc..7969ffce5 100644 --- a/lib/solargraph/yard_map/helpers.rb +++ b/lib/solargraph/yard_map/helpers.rb @@ -22,10 +22,12 @@ def object_location code_object, spec # @param code_object [YARD::CodeObjects::Base] # @param spec [Gem::Specification, nil] # @return [Solargraph::Pin::Namespace] - def create_closure_namespace_for(code_object, spec) + def create_closure_namespace_for code_object, spec code_object_for_location = code_object # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new") - code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) + if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) + code_object_for_location = code_object.namespace + end namespace_location = object_location(code_object_for_location, spec) ns_name = code_object.namespace.to_s if ns_name.empty? diff --git a/lib/solargraph/yard_map/mapper.rb b/lib/solargraph/yard_map/mapper.rb index f0708e9d9..67a9ff860 100644 --- a/lib/solargraph/yard_map/mapper.rb +++ b/lib/solargraph/yard_map/mapper.rb @@ -46,12 +46,12 @@ def generate_pins code_object # yardoc and converted to a fully qualified namespace. # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check superclass = if code_object.superclass.is_a?(YARD::CodeObjects::Proxy) - # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check - "::#{code_object.superclass}" - else - # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check - code_object.superclass.to_s - end + # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check + "::#{code_object.superclass}" + else + # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check + code_object.superclass.to_s + end result.push Solargraph::Pin::Reference::Superclass.new(name: superclass, closure: nspin, source: :yard_map) end code_object.class_mixins.each do |m| diff --git a/lib/solargraph/yard_map/mapper/to_method.rb b/lib/solargraph/yard_map/mapper/to_method.rb index 42593ed8c..f4d5b0dad 100644 --- a/lib/solargraph/yard_map/mapper/to_method.rb +++ b/lib/solargraph/yard_map/mapper/to_method.rb @@ -9,7 +9,7 @@ module ToMethod # @type [Hash{Array => Symbol}] VISIBILITY_OVERRIDE = { # YARD pays attention to 'private' statements prior to class methods but shouldn't - ["Rails::Engine", :class, "find_root_with_flag"] => :public + ['Rails::Engine', :class, 'find_root_with_flag'] => :public } # @param code_object [YARD::CodeObjects::MethodObject] @@ -32,7 +32,9 @@ def self.make code_object, name = nil, scope = nil, visibility = nil, closure = # @sg-ignore Need to add nil check here final_visibility ||= VISIBILITY_OVERRIDE[[closure.path, final_scope]] # @sg-ignore Need to add nil check here - final_visibility ||= :private if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name.to_sym) + if closure.path == 'Kernel' && Kernel.private_instance_methods(false).include?(name.to_sym) + final_visibility ||= :private + end final_visibility ||= visibility final_visibility ||= :private if code_object.module_function? && final_scope == :instance final_visibility ||= :public if code_object.module_function? && final_scope == :class @@ -50,7 +52,7 @@ def self.make code_object, name = nil, scope = nil, visibility = nil, closure = explicit: code_object.is_explicit?, return_type: return_type, parameters: [], - source: :yardoc, + source: :yardoc ) else # @sg-ignore Need to add nil check here @@ -66,7 +68,7 @@ def self.make code_object, name = nil, scope = nil, visibility = nil, closure = return_type: return_type, attribute: code_object.is_attribute?, parameters: [], - source: :yardoc, + source: :yardoc ) pin.parameters.concat get_parameters(code_object, location, comments, pin) pin.parameters.freeze @@ -99,7 +101,7 @@ def get_parameters code_object, location, comments, pin presence: nil, decl: arg_type(a), asgn_code: a[1], - source: :yardoc, + source: :yardoc ) end end diff --git a/lib/solargraph/yard_map/mapper/to_namespace.rb b/lib/solargraph/yard_map/mapper/to_namespace.rb index 7d1d3ce6e..3e0887ecd 100644 --- a/lib/solargraph/yard_map/mapper/to_namespace.rb +++ b/lib/solargraph/yard_map/mapper/to_namespace.rb @@ -23,7 +23,7 @@ def self.make code_object, spec, closure = nil closure: closure, # @sg-ignore need to add a nil check here gates: closure.gates, - source: :yardoc, + source: :yardoc ) end end diff --git a/lib/solargraph/yard_tags.rb b/lib/solargraph/yard_tags.rb index c34710b63..462138f9c 100644 --- a/lib/solargraph/yard_tags.rb +++ b/lib/solargraph/yard_tags.rb @@ -14,7 +14,7 @@ def call; end end # Define a @type tag for documenting variables -YARD::Tags::Library.define_tag("Type", :type, :with_types_and_name) +YARD::Tags::Library.define_tag('Type', :type, :with_types_and_name) # Define an @!override directive for overriding method tags -YARD::Tags::Library.define_directive("override", :with_name, Solargraph::DomainDirective) +YARD::Tags::Library.define_directive('override', :with_name, Solargraph::DomainDirective) diff --git a/solargraph.gemspec b/solargraph.gemspec index d2ac422da..a02082985 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -1,78 +1,77 @@ -# @sg-ignore Should better support meaning of '&' in RBS -$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' -require 'solargraph/version' -require 'date' - -# @param s [Gem::Specification] -Gem::Specification.new do |s| - s.name = 'solargraph' - s.version = Solargraph::VERSION - s.date = Date.today.strftime("%Y-%m-%d") - s.summary = "A Ruby language server" - s.description = "IDE tools for code completion, inline documentation, and static analysis" - s.authors = ["Fred Snyder"] - s.email = 'admin@castwide.com' - s.files = Dir.chdir(File.expand_path('..', __FILE__)) do - # @sg-ignore Need backtick support - # @type [String] - all_files = `git ls-files -z` - all_files.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end - s.homepage = 'https://solargraph.org' - s.license = 'MIT' - s.executables = ['solargraph'] - s.metadata["funding_uri"] = "https://www.patreon.com/castwide" - s.metadata["bug_tracker_uri"] = "https://github.com/castwide/solargraph/issues" - s.metadata["changelog_uri"] = "https://github.com/castwide/solargraph/blob/master/CHANGELOG.md" - s.metadata["source_code_uri"] = "https://github.com/castwide/solargraph" - s.metadata["rubygems_mfa_required"] = "true" - - s.required_ruby_version = '>= 3.0' - - s.add_runtime_dependency 'ast', '~> 2.4.3' - s.add_runtime_dependency 'backport', '~> 1.2' - s.add_runtime_dependency 'benchmark', '~> 0.4' - s.add_runtime_dependency 'bundler', '>= 2.0' - s.add_runtime_dependency 'diff-lcs', '~> 1.4' - s.add_runtime_dependency 'jaro_winkler', '~> 1.6', '>= 1.6.1' - s.add_runtime_dependency 'kramdown', '~> 2.3' - s.add_runtime_dependency 'kramdown-parser-gfm', '~> 1.1' - s.add_runtime_dependency 'logger', '~> 1.6' - s.add_runtime_dependency 'observer', '~> 0.1' - s.add_runtime_dependency 'ostruct', '~> 0.6' - s.add_runtime_dependency 'open3', '~> 0.2.1' - s.add_runtime_dependency 'parser', '~> 3.0' - s.add_runtime_dependency 'prism', '~> 1.4' - s.add_runtime_dependency 'rbs', ['>= 3.6.1', '<= 4.0.0.dev.5'] - s.add_runtime_dependency 'reverse_markdown', '~> 3.0' - s.add_runtime_dependency 'rubocop', '~> 1.76' - s.add_runtime_dependency 'thor', '~> 1.0' - s.add_runtime_dependency 'tilt', '~> 2.0' - s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24' - s.add_runtime_dependency 'yard-solargraph', '~> 0.1' - s.add_runtime_dependency 'yard-activesupport-concern', '~> 0.0' - - s.add_development_dependency 'pry', '~> 0.15' - s.add_development_dependency 'public_suffix', '~> 3.1' - s.add_development_dependency 'rake', '~> 13.2' - s.add_development_dependency 'rspec', '~> 3.5' - s.add_development_dependency 'rspec-time-guard', '~> 0.2.0' - s.add_development_dependency 'parallel_rspec', '~> 3.0' - # - # very specific development-time RuboCop version patterns for CI - # stability - feel free to update in an isolated PR - # - # even more specific on RuboCop itself, which is written into _todo - # file. - s.add_development_dependency 'rubocop', '~> 1.80.0.0' - s.add_development_dependency 'rubocop-rake', '~> 0.7.1' - s.add_development_dependency 'rubocop-rspec', '~> 3.6.0' - s.add_development_dependency 'rubocop-yard', '~> 1.0.0' - s.add_development_dependency 'simplecov', '~> 0.21' - s.add_development_dependency 'simplecov-lcov', '~> 0.8' - s.add_development_dependency 'undercover', '~> 0.7' - s.add_development_dependency 'overcommit', '~> 0.68.0' - s.add_development_dependency 'webmock', '~> 3.6' - # work around missing yard dependency needed as of Ruby 3.5 - s.add_development_dependency 'irb', '~> 1.15' -end +# @sg-ignore Should better support meaning of '&' in RBS +$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' +require 'solargraph/version' +require 'date' + +# @param s [Gem::Specification] +Gem::Specification.new do |s| + s.name = 'solargraph' + s.version = Solargraph::VERSION + s.summary = 'A Ruby language server' + s.description = 'IDE tools for code completion, inline documentation, and static analysis' + s.authors = ['Fred Snyder'] + s.email = 'admin@castwide.com' + s.files = Dir.chdir(File.expand_path(__dir__)) do + # @sg-ignore Need backtick support + # @type [String] + all_files = `git ls-files -z` + all_files.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + end + s.homepage = 'https://solargraph.org' + s.license = 'MIT' + s.executables = ['solargraph'] + s.metadata['funding_uri'] = 'https://www.patreon.com/castwide' + s.metadata['bug_tracker_uri'] = 'https://github.com/castwide/solargraph/issues' + s.metadata['changelog_uri'] = 'https://github.com/castwide/solargraph/blob/master/CHANGELOG.md' + s.metadata['source_code_uri'] = 'https://github.com/castwide/solargraph' + s.metadata['rubygems_mfa_required'] = 'true' + + s.required_ruby_version = '>= 3.0' + + s.add_dependency 'ast', '~> 2.4.3' + s.add_dependency 'backport', '~> 1.2' + s.add_dependency 'benchmark', '~> 0.4' + s.add_dependency 'bundler', '>= 2.0' + s.add_dependency 'diff-lcs', '~> 1.4' + s.add_dependency 'jaro_winkler', '~> 1.6', '>= 1.6.1' + s.add_dependency 'kramdown', '~> 2.3' + s.add_dependency 'kramdown-parser-gfm', '~> 1.1' + s.add_dependency 'logger', '~> 1.6' + s.add_dependency 'observer', '~> 0.1' + s.add_dependency 'open3', '~> 0.2.1' + s.add_dependency 'ostruct', '~> 0.6' + s.add_dependency 'parser', '~> 3.0' + s.add_dependency 'prism', '~> 1.4' + s.add_dependency 'rbs', ['>= 3.6.1', '<= 4.0.0.dev.5'] + s.add_dependency 'reverse_markdown', '~> 3.0' + s.add_dependency 'rubocop', '~> 1.76' + s.add_dependency 'thor', '~> 1.0' + s.add_dependency 'tilt', '~> 2.0' + s.add_dependency 'yard', '~> 0.9', '>= 0.9.24' + s.add_dependency 'yard-activesupport-concern', '~> 0.0' + s.add_dependency 'yard-solargraph', '~> 0.1' + + s.add_development_dependency 'parallel_rspec', '~> 3.0' + s.add_development_dependency 'pry', '~> 0.15' + s.add_development_dependency 'public_suffix', '~> 3.1' + s.add_development_dependency 'rake', '~> 13.2' + s.add_development_dependency 'rspec', '~> 3.5' + s.add_development_dependency 'rspec-time-guard', '~> 0.2.0' + # + # very specific development-time RuboCop version patterns for CI + # stability - feel free to update in an isolated PR + # + # even more specific on RuboCop itself, which is written into _todo + # file. + s.add_development_dependency 'overcommit', '~> 0.68.0' + s.add_development_dependency 'rubocop', '~> 1.80.0.0' + s.add_development_dependency 'rubocop-rake', '~> 0.7.1' + s.add_development_dependency 'rubocop-rspec', '~> 3.6.0' + s.add_development_dependency 'rubocop-yard', '~> 1.0.0' + s.add_development_dependency 'simplecov', '~> 0.21' + s.add_development_dependency 'simplecov-lcov', '~> 0.8' + s.add_development_dependency 'undercover', '~> 0.7' + s.add_development_dependency 'webmock', '~> 3.6' + # work around missing yard dependency needed as of Ruby 3.5 + s.add_development_dependency 'irb', '~> 1.15' +end diff --git a/spec/api_map/cache_spec.rb b/spec/api_map/cache_spec.rb index 20e5b9df1..0a8d002de 100644 --- a/spec/api_map/cache_spec.rb +++ b/spec/api_map/cache_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::ApiMap::Cache do - it "recognizes empty caches" do + it 'recognizes empty caches' do cache = Solargraph::ApiMap::Cache.new expect(cache).to be_empty cache.set_methods('', :class, [:public], true, []) diff --git a/spec/api_map/config_spec.rb b/spec/api_map/config_spec.rb index 5790265cd..936d05e27 100644 --- a/spec/api_map/config_spec.rb +++ b/spec/api_map/config_spec.rb @@ -5,62 +5,62 @@ let(:workspace_path) { File.realpath(Dir.mktmpdir) } let(:global_path) { @global_path } - before(:each) do + before do @global_path = File.realpath(Dir.mktmpdir) - @orig_env = ENV['SOLARGRAPH_GLOBAL_CONFIG'] + @orig_env = ENV.fetch('SOLARGRAPH_GLOBAL_CONFIG', nil) ENV['SOLARGRAPH_GLOBAL_CONFIG'] = File.join(@global_path, '.solargraph.yml') end - after(:each) do + after do ENV['SOLARGRAPH_GLOBAL_CONFIG'] = @orig_env FileUtils.remove_entry(workspace_path) FileUtils.remove_entry(global_path) end - it "reads workspace files from config" do + it 'reads workspace files from config' do File.write(File.join(workspace_path, 'foo.rb'), 'test') File.write(File.join(workspace_path, 'bar.rb'), 'test') File.open(File.join(workspace_path, '.solargraph.yml'), 'w') do |file| - file.puts "include:" - file.puts " - foo.rb" - file.puts "exclude:" - file.puts " - bar.rb" + file.puts 'include:' + file.puts ' - foo.rb' + file.puts 'exclude:' + file.puts ' - bar.rb' end expect(config.included).to eq([File.join(workspace_path, 'foo.rb')]) expect(config.excluded).to eq([File.join(workspace_path, 'bar.rb')]) end - it "reads workspace files from global config" do + it 'reads workspace files from global config' do File.write(File.join(workspace_path, 'foo.rb'), 'test') File.write(File.join(workspace_path, 'bar.rb'), 'test') File.open(File.join(global_path, '.solargraph.yml'), 'w') do |file| - file.puts "include:" - file.puts " - foo.rb" - file.puts "exclude:" - file.puts " - bar.rb" + file.puts 'include:' + file.puts ' - foo.rb' + file.puts 'exclude:' + file.puts ' - bar.rb' end expect(config.included).to eq([File.join(workspace_path, 'foo.rb')]) expect(config.excluded).to eq([File.join(workspace_path, 'bar.rb')]) end - it "overrides global config with workspace config" do + it 'overrides global config with workspace config' do File.write(File.join(workspace_path, 'foo.rb'), 'test') File.write(File.join(workspace_path, 'bar.rb'), 'test') - + File.open(File.join(workspace_path, '.solargraph.yml'), 'w') do |file| - file.puts "include:" - file.puts " - foo.rb" - file.puts "max_files: 8000" + file.puts 'include:' + file.puts ' - foo.rb' + file.puts 'max_files: 8000' end File.open(File.join(global_path, '.solargraph.yml'), 'w') do |file| - file.puts "include:" - file.puts " - include.rb" - file.puts "exclude:" - file.puts " - bar.rb" - file.puts "max_files: 1000" + file.puts 'include:' + file.puts ' - include.rb' + file.puts 'exclude:' + file.puts ' - bar.rb' + file.puts 'max_files: 1000' end expect(config.included).to eq([File.join(workspace_path, 'foo.rb')]) diff --git a/spec/api_map/source_to_yard_spec.rb b/spec/api_map/source_to_yard_spec.rb index 88be54c64..e7dcf3d0d 100644 --- a/spec/api_map/source_to_yard_spec.rb +++ b/spec/api_map/source_to_yard_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::ApiMap::SourceToYard do - it "rakes sources" do + it 'rakes sources' do source = Solargraph::SourceMap.load_string(%( module Foo class Bar @@ -17,7 +17,7 @@ def baz expect(object.code_object_paths).to include('Foo::Bar#baz') end - it "generates docstrings" do + it 'generates docstrings' do source = Solargraph::SourceMap.load_string(%( # My foo class 描述 class Foo @@ -40,7 +40,7 @@ def self.baz expect(class_method_object.tag(:return).types).to eq(['Foo']) end - it "generates instance mixins" do + it 'generates instance mixins' do source = Solargraph::SourceMap.load_string(%( module Foo def bar @@ -58,7 +58,7 @@ class Baz expect(class_object.instance_mixins).to include(module_object) end - it "generates class mixins" do + it 'generates class mixins' do source = Solargraph::SourceMap.load_string(%( module Foo def bar; end @@ -75,7 +75,7 @@ class Baz expect(class_object.class_mixins).to include(module_object) end - it "generates methods for attributes" do + it 'generates methods for attributes' do source = Solargraph::SourceMap.load_string(%( class Foo attr_reader :bar @@ -86,15 +86,15 @@ class Foo object = Object.new object.extend Solargraph::ApiMap::SourceToYard object.rake_yard Solargraph::ApiMap::Store.new(source.pins) - expect(object.code_object_at('Foo#bar')).not_to be(nil) - expect(object.code_object_at('Foo#bar=')).to be(nil) - expect(object.code_object_at('Foo#baz')).to be(nil) - expect(object.code_object_at('Foo#baz=')).not_to be(nil) - expect(object.code_object_at('Foo#boo')).not_to be(nil) - expect(object.code_object_at('Foo#boo=')).not_to be(nil) + expect(object.code_object_at('Foo#bar')).not_to be_nil + expect(object.code_object_at('Foo#bar=')).to be_nil + expect(object.code_object_at('Foo#baz')).to be_nil + expect(object.code_object_at('Foo#baz=')).not_to be_nil + expect(object.code_object_at('Foo#boo')).not_to be_nil + expect(object.code_object_at('Foo#boo=')).not_to be_nil end - it "generates method parameters" do + it 'generates method parameters' do source = Solargraph::SourceMap.load_string(%( class Foo def bar baz, boo = 'boo' @@ -110,7 +110,7 @@ def bar baz, boo = 'boo' expect(method_object.parameters[1]).to eq(['boo', "'boo'"]) end - it "generates method keyword parameters" do + it 'generates method keyword parameters' do source = Solargraph::SourceMap.load_string(%( class Foo def bar baz, boo: 'boo' diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index ef51d6292..c4303dab4 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -440,7 +440,7 @@ class Sup method_pins = @api_map.get_method_stack("Array(1, 2, 'a')", 'include?') method_pin = method_pins.first - expect(method_pin).to_not be_nil + expect(method_pin).not_to be_nil expect(method_pin.path).to eq('Array#include?') parameter_type = method_pin.signatures.first.parameters.first.return_type expect(parameter_type.rooted_tags).to eq("1, 2, 'a'") diff --git a/spec/complex_type_spec.rb b/spec/complex_type_spec.rb index 8a5b273dd..a11307edd 100644 --- a/spec/complex_type_spec.rb +++ b/spec/complex_type_spec.rb @@ -160,7 +160,6 @@ expect(types.to_rbs).to eq('Array[Symbol, String]') end - # Note that parametrized types are typically not order-dependent, in # other words, a list of parametrized types can occur in any order # inside of a type. An array specified as Array can @@ -543,13 +542,13 @@ let(:complex_type) { Solargraph::ComplexType.parse(tag) } let(:unique_type) { complex_type.first } + let(:context_type) { Solargraph::ComplexType.parse(context_type_tag) } + let(:generic_value) { unfrozen_input_map.transform_values! { |tag| Solargraph::ComplexType.parse(tag) } } + it '#{tag} is a unique type' do expect(complex_type.length).to eq(1) end - let(:generic_value) { unfrozen_input_map.transform_values! { |tag| Solargraph::ComplexType.parse(tag) } } - let(:context_type) { Solargraph::ComplexType.parse(context_type_tag) } - it "resolves to #{expected_tag} with updated map #{expected_output_map}" do resolved_generic_values = unfrozen_input_map.transform_values { |tag| Solargraph::ComplexType.parse(tag) } resolved_type = unique_type.resolve_generics_from_context(expected_output_map.keys, context_type, @@ -607,7 +606,22 @@ end end - context "supports arbitrary combinations of the above syntax and features" do + context 'supports arbitrary combinations of the above syntax and features' do + let(:foo_bar_api_map) do + api_map = Solargraph::ApiMap.new + source = Solargraph::Source.load_string(%( + module Foo + class Bar + # @return [Bar] + def make_bar + end + end + end + )) + api_map.map source + api_map + end + it 'returns string representations of the entire type array' do type = Solargraph::ComplexType.parse('String', 'Array') expect(type.to_s).to eq('String, Array') @@ -635,21 +649,6 @@ expect(types.to_rbs).to eq('(Array[String] | Hash[String, Symbol] | [String, Integer])') end - let(:foo_bar_api_map) do - api_map = Solargraph::ApiMap.new - source = Solargraph::Source.load_string(%( - module Foo - class Bar - # @return [Bar] - def make_bar - end - end - end - )) - api_map.map source - api_map - end - it 'qualifies types with list parameters' do original = Solargraph::ComplexType.parse('Class').first expect(original).not_to be_rooted diff --git a/spec/convention/activesupport_concern_spec.rb b/spec/convention/activesupport_concern_spec.rb index a0eae979a..2360e2a83 100644 --- a/spec/convention/activesupport_concern_spec.rb +++ b/spec/convention/activesupport_concern_spec.rb @@ -101,7 +101,7 @@ def self.my_method; end # create a temporary directory with the scope of the spec around do |example| require 'tmpdir' - Dir.mktmpdir("rspec-solargraph-") do |dir| + Dir.mktmpdir('rspec-solargraph-') do |dir| @temp_dir = dir example.run end @@ -151,11 +151,11 @@ class Base it { is_expected.not_to be_empty } - it "has one item" do + it 'has one item' do expect(method_pins.size).to eq(1) end - it "is a Pin::Method" do + it 'is a Pin::Method' do expect(method_pins.first).to be_a(Solargraph::Pin::Method) end end diff --git a/spec/convention/struct_definition_spec.rb b/spec/convention/struct_definition_spec.rb index 5c3fc5211..51a71f673 100644 --- a/spec/convention/struct_definition_spec.rb +++ b/spec/convention/struct_definition_spec.rb @@ -103,7 +103,7 @@ expect(params.map(&:name)).to eql(%w[bar baz]) expect(params.map(&:return_type).map(&:tag)).to eql(%w[Integer String]) - expect(params[1].documentation).to eql("Some text") + expect(params[1].documentation).to eql('Some text') end [true, false].each do |kw_args| @@ -116,21 +116,21 @@ Foo = Struct.new(:bar, :baz, keyword_init: #{kw_args}) ), 'test.rb') - params_bar = source.pins.find { |p| p.path == "Foo#bar=" }.parameters + params_bar = source.pins.find { |p| p.path == 'Foo#bar=' }.parameters expect(params_bar.length).to be(1) - expect(params_bar.first.return_type.tag).to eql("String") + expect(params_bar.first.return_type.tag).to eql('String') expect(params_bar.first.arg?).to be(true) - params_baz = source.pins.find { |p| p.path == "Foo#baz=" }.parameters + params_baz = source.pins.find { |p| p.path == 'Foo#baz=' }.parameters expect(params_baz.length).to be(1) - expect(params_baz.first.return_type.tag).to eql("Integer") + expect(params_baz.first.return_type.tag).to eql('Integer') expect(params_baz.first.arg?).to be(true) - iv_bar = source.pins.find { |p| p.name == "@bar" } - expect(iv_bar.return_type.tag).to eql("String") + iv_bar = source.pins.find { |p| p.name == '@bar' } + expect(iv_bar.return_type.tag).to eql('String') - iv_baz = source.pins.find { |p| p.name == "@baz" } - expect(iv_baz.return_type.tag).to eql("Integer") + iv_baz = source.pins.find { |p| p.name == '@baz' } + expect(iv_baz.return_type.tag).to eql('Integer') end end end diff --git a/spec/diagnostics/base_spec.rb b/spec/diagnostics/base_spec.rb index d2068caf1..42fbb097e 100644 --- a/spec/diagnostics/base_spec.rb +++ b/spec/diagnostics/base_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Diagnostics::Base do - it "returns empty diagnostics" do + it 'returns empty diagnostics' do reporter = Solargraph::Diagnostics::Base.new expect(reporter.diagnose(nil, nil)).to be_empty end diff --git a/spec/diagnostics/require_not_found_spec.rb b/spec/diagnostics/require_not_found_spec.rb index 6ecfdcae9..53b375d7e 100644 --- a/spec/diagnostics/require_not_found_spec.rb +++ b/spec/diagnostics/require_not_found_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Diagnostics::RequireNotFound do - before :each do + before do @source = Solargraph::Source.new(%( require 'rexml/document' require 'not_valid' @@ -11,7 +11,7 @@ @api_map.catalog Solargraph::Bench.new(source_maps: [@source_map], external_requires: ['not_valid']) end - it "reports unresolved requires" do + it 'reports unresolved requires' do reporter = Solargraph::Diagnostics::RequireNotFound.new result = reporter.diagnose(@source, @api_map) expect(result.length).to eq(1) diff --git a/spec/diagnostics/rubocop_helpers_spec.rb b/spec/diagnostics/rubocop_helpers_spec.rb index d59dac4ae..bccfeb9d6 100644 --- a/spec/diagnostics/rubocop_helpers_spec.rb +++ b/spec/diagnostics/rubocop_helpers_spec.rb @@ -2,7 +2,7 @@ context do around do |example| old_gem_path = Gem.paths.path - custom_gem_path = File.absolute_path('spec/fixtures/rubocop-custom-version').gsub(/\\/, '/') + custom_gem_path = File.absolute_path('spec/fixtures/rubocop-custom-version').gsub('\\', '/') # Remove a post_reset hook set by bundler to restore cached specs # Source: https://github.com/ruby/ruby/blob/master/lib/bundler/rubygems_integration.rb#L487-L489 old_post_reset_hooks = Gem.post_reset_hooks.dup @@ -18,7 +18,7 @@ let(:custom_version) { '0.0.0' } - it "requires the specified version of rubocop" do + it 'requires the specified version of rubocop' do input = custom_version Solargraph::Diagnostics::RubocopHelpers.require_rubocop(input) output = RuboCop::Version::STRING @@ -29,7 +29,7 @@ context do let(:default_version) { Gem::Specification.find_by_name('rubocop').full_gem_path[/[^-]+$/] } - it "requires the default version of rubocop" do + it 'requires the default version of rubocop' do input = nil Solargraph::Diagnostics::RubocopHelpers.require_rubocop(input) output = RuboCop::Version::STRING @@ -37,13 +37,13 @@ end end - it "converts lower-case drive letters to upper-case" do + it 'converts lower-case drive letters to upper-case' do input = 'c:/one/two' output = Solargraph::Diagnostics::RubocopHelpers.fix_drive_letter(input) expect(output).to eq('C:/one/two') end - it "ignores paths without drive letters" do + it 'ignores paths without drive letters' do input = 'one/two' output = Solargraph::Diagnostics::RubocopHelpers.fix_drive_letter(input) expect(output).to eq('one/two') diff --git a/spec/diagnostics/rubocop_spec.rb b/spec/diagnostics/rubocop_spec.rb index aa279f66c..4926a458f 100644 --- a/spec/diagnostics/rubocop_spec.rb +++ b/spec/diagnostics/rubocop_spec.rb @@ -15,17 +15,17 @@ def bar expect(result).to be_a(Array) end - context "with validation error" do + context 'with validation error' do let(:fixture_path) do - File.absolute_path('spec/fixtures/rubocop-validation-error').gsub(/\\/, '/') + File.absolute_path('spec/fixtures/rubocop-validation-error').gsub('\\', '/') end around do |example| config_file = File.join(fixture_path, '.rubocop.yml') File.write(config_file, <<~YAML) - inherit_from: - - file_not_found.yml - YAML + inherit_from: + - file_not_found.yml + YAML example.run ensure File.delete(config_file) if File.exist?(config_file) @@ -35,9 +35,9 @@ def bar file = File.realpath(File.join(fixture_path, 'app.rb')) source = Solargraph::Source.load(file) rubocop = Solargraph::Diagnostics::Rubocop.new - expect { + expect do rubocop.diagnose(source, nil) - }.to raise_error(Solargraph::DiagnosticsError) + end.to raise_error(Solargraph::DiagnosticsError) end end diff --git a/spec/diagnostics/type_check_spec.rb b/spec/diagnostics/type_check_spec.rb index e343d6598..c140f9593 100644 --- a/spec/diagnostics/type_check_spec.rb +++ b/spec/diagnostics/type_check_spec.rb @@ -1,7 +1,7 @@ describe Solargraph::Diagnostics::TypeCheck do let(:api_map) { Solargraph::ApiMap.new } - it "detects defined return types" do + it 'detects defined return types' do source = Solargraph::Source.load_string(%( # @return [String] def foo @@ -12,7 +12,7 @@ def foo expect(result).to be_empty end - it "detects missing return types" do + it 'detects missing return types' do source = Solargraph::Source.load_string(%( def foo end @@ -23,7 +23,7 @@ def foo expect(result[0][:message]).to include('foo') end - it "detects defined parameter types" do + it 'detects defined parameter types' do source = Solargraph::Source.load_string(%( # @param bar [String] # @return [String] @@ -35,7 +35,7 @@ def foo(bar) expect(result).to be_empty end - it "detects missing parameter types" do + it 'detects missing parameter types' do source = Solargraph::Source.load_string(%( # @return [String] def foo(bar) @@ -48,7 +48,7 @@ def foo(bar) expect(result[0][:message]).to include('bar') end - it "detects return types from superclasses" do + it 'detects return types from superclasses' do source = Solargraph::Source.load_string(%( class First # @return [String] @@ -65,7 +65,7 @@ def foo expect(result).to be_empty end - it "detects parameter types from superclasses" do + it 'detects parameter types from superclasses' do source = Solargraph::Source.load_string(%( class First # @param bar [String] @@ -83,7 +83,7 @@ def foo bar expect(result).to be_empty end - it "works with optional and keyword arguments" do + it 'works with optional and keyword arguments' do source = Solargraph::Source.load_string(%( # @param bar [String] # @param baz [String] diff --git a/spec/diagnostics/update_errors_spec.rb b/spec/diagnostics/update_errors_spec.rb index 11b8c6f8f..1a05f93d5 100644 --- a/spec/diagnostics/update_errors_spec.rb +++ b/spec/diagnostics/update_errors_spec.rb @@ -1,16 +1,16 @@ describe Solargraph::Diagnostics::UpdateErrors do - it "detects repaired lines" do + it 'detects repaired lines' do api_map = Solargraph::ApiMap.new orig = Solargraph::Source.load_string('foo', 'test.rb') diagnoser = Solargraph::Diagnostics::UpdateErrors.new result = diagnoser.diagnose(orig, api_map) expect(result.length).to eq(0) updater = Solargraph::Source::Updater.new('test.rb', 2, [ - Solargraph::Source::Change.new( - Solargraph::Range.from_to(0, 3, 0, 3), - '.' - ) - ]) + Solargraph::Source::Change.new( + Solargraph::Range.from_to(0, 3, 0, 3), + '.' + ) + ]) source = orig.synchronize(updater) diagnoser = Solargraph::Diagnostics::UpdateErrors.new result = diagnoser.diagnose(source, api_map) diff --git a/spec/diagnostics_spec.rb b/spec/diagnostics_spec.rb index ba689692d..628341f2a 100644 --- a/spec/diagnostics_spec.rb +++ b/spec/diagnostics_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Diagnostics do - it "registers reporters" do + it 'registers reporters' do Solargraph::Diagnostics.register 'base', Solargraph::Diagnostics::Base expect(Solargraph::Diagnostics.reporters).to include('base') expect(Solargraph::Diagnostics.reporter('base')).to be(Solargraph::Diagnostics::Base) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 0ac440c55..b99b14786 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -61,14 +61,14 @@ xit 'tracks unresolved requires' do # These are auto-required by solargraph-rspec in case the bundle # includes these gems. In our case, it doesn't! - unprovided_solargraph_rspec_requires = [ - 'rspec-rails', - 'actionmailer', - 'activerecord', - 'shoulda-matchers', - 'rspec-sidekiq', - 'airborne', - 'activesupport' + unprovided_solargraph_rspec_requires = %w[ + rspec-rails + actionmailer + activerecord + shoulda-matchers + rspec-sidekiq + airborne + activesupport ] expect(doc_map.unresolved_requires - unprovided_solargraph_rspec_requires) .to eq(['not_a_gem']) @@ -174,9 +174,9 @@ it 'includes convention requires from environ' do dummy_convention = Class.new(Solargraph::Convention::Base) do - def global(doc_map) + def global doc_map Solargraph::Environ.new( - requires: ['convention_gem1', 'convention_gem2'] + requires: %w[convention_gem1 convention_gem2] ) end end diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 64ec19a0b..3a9745d70 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -10,11 +10,6 @@ end context 'with a combined method pin' do - before :context do - # avoid race conditions building the rbs gem by letting parallel - # rspec know to run these serially in the same worker - end - let(:path) { 'RBS::EnvironmentLoader#core_root' } let(:requires) { ['rbs'] } @@ -33,11 +28,6 @@ end context 'with a YARD-only pin' do - before :context do - # avoid race conditions building the rbs gem by letting parallel - # rspec know to run these serially in the same worker - end - let(:requires) { ['rake'] } let(:path) { 'Rake::Task#prerequisites' } diff --git a/spec/language_server/host/diagnoser_spec.rb b/spec/language_server/host/diagnoser_spec.rb index 697d352bd..78c50a7a2 100644 --- a/spec/language_server/host/diagnoser_spec.rb +++ b/spec/language_server/host/diagnoser_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::LanguageServer::Host::Diagnoser do - it "diagnoses on ticks" do + it 'diagnoses on ticks' do host = double(Solargraph::LanguageServer::Host, options: { 'diagnostics' => true }, synchronizing?: false) allow(host).to receive(:diagnose) diagnoser = Solargraph::LanguageServer::Host::Diagnoser.new(host) diff --git a/spec/language_server/host/dispatch_spec.rb b/spec/language_server/host/dispatch_spec.rb index 5e392d47c..a11093b5e 100644 --- a/spec/language_server/host/dispatch_spec.rb +++ b/spec/language_server/host/dispatch_spec.rb @@ -5,12 +5,12 @@ @dispatch.extend Solargraph::LanguageServer::Host::Dispatch end - after :each do + after do @dispatch.libraries.clear @dispatch.sources.clear end - it "finds an explicit library" do + it 'finds an explicit library' do @dispatch.libraries.push Solargraph::Library.load('*') src = @dispatch.sources.open('file:///file.rb', 'a=b', 0) @dispatch.libraries.first.merge src @@ -18,7 +18,7 @@ expect(lib).to be(@dispatch.libraries.first) end - it "finds an implicit library" do + it 'finds an implicit library' do dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) file = File.join(dir, 'new.rb') uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) @@ -28,7 +28,7 @@ expect(lib).to be(@dispatch.libraries.first) end - it "finds a generic library" do + it 'finds a generic library' do dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) file = '/external.rb' uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) diff --git a/spec/language_server/host/message_worker_spec.rb b/spec/language_server/host/message_worker_spec.rb index 5e5bef481..110b41cc4 100644 --- a/spec/language_server/host/message_worker_spec.rb +++ b/spec/language_server/host/message_worker_spec.rb @@ -1,7 +1,7 @@ describe Solargraph::LanguageServer::Host::MessageWorker do - it "handle requests on queue" do + it 'handle requests on queue' do host = double(Solargraph::LanguageServer::Host) - message = {'method' => '$/example'} + message = { 'method' => '$/example' } allow(host).to receive(:receive).with(message).and_return(nil) worker = Solargraph::LanguageServer::Host::MessageWorker.new(host) diff --git a/spec/language_server/host_spec.rb b/spec/language_server/host_spec.rb index 5635680e3..3eb4d52f3 100644 --- a/spec/language_server/host_spec.rb +++ b/spec/language_server/host_spec.rb @@ -1,15 +1,15 @@ require 'tmpdir' describe Solargraph::LanguageServer::Host do - it "prepares a workspace" do + it 'prepares a workspace' do host = Solargraph::LanguageServer::Host.new Dir.mktmpdir do |dir| - host.prepare (dir) - expect(host.libraries.first).not_to be(nil) + host.prepare(dir) + expect(host.libraries.first).not_to be_nil end end - it "processes responses to message requests" do + it 'processes responses to message requests' do host = Solargraph::LanguageServer::Host.new done_somethings = 0 host.send_request 'window/showMessageRequest', { @@ -20,13 +20,13 @@ end expect(host.pending_requests.length).to eq(1) host.receive({ - 'id' => host.pending_requests.first, - 'result' => 'Do something' - }) + 'id' => host.pending_requests.first, + 'result' => 'Do something' + }) expect(done_somethings).to eq(1) end - it "creates files from disk" do + it 'creates files from disk' do Dir.mktmpdir do |dir| host = Solargraph::LanguageServer::Host.new host.prepare dir @@ -38,26 +38,26 @@ end end - it "deletes files" do + it 'deletes files' do Dir.mktmpdir do |dir| - expect { + expect do host = Solargraph::LanguageServer::Host.new file = File.join(dir, 'test.rb') File.write(file, "foo = 'foo'") host.prepare dir uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) host.delete(uri) - }.not_to raise_error + end.not_to raise_error end end - it "cancels requests" do + it 'cancels requests' do host = Solargraph::LanguageServer::Host.new host.cancel 1 expect(host.cancel?(1)).to be(true) end - it "runs diagnostics on opened files" do + it 'runs diagnostics on opened files' do Dir.mktmpdir do |dir| host = Solargraph::LanguageServer::Host.new host.configure({ 'diagnostics' => true }) @@ -65,7 +65,7 @@ File.write(file, "foo = 'foo'") host.start host.prepare dir - uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) + Solargraph::LanguageServer::UriHelpers.file_to_uri(file) host.open(file, File.read(file), 1) buffer = host.flush times = 0 @@ -81,7 +81,7 @@ end end - it "handles DiagnosticsErrors" do + it 'handles DiagnosticsErrors' do host = Solargraph::LanguageServer::Host.new library = double(:Library) allow(library).to receive(:diagnose).and_raise(Solargraph::DiagnosticsError) @@ -94,17 +94,17 @@ # @todo Smelly instance variable access host.instance_variable_set(:@libraries, [library]) host.open('file:///test.rb', '', 0) - expect { + expect do host.diagnose 'file:///test.rb' - }.not_to raise_error + end.not_to raise_error result = host.flush expect(result).to include('Error in diagnostics') end - it "opens multiple folders" do + it 'opens multiple folders' do host = Solargraph::LanguageServer::Host.new - app1_folder = File.absolute_path('spec/fixtures/workspace_folders/folder1').gsub(/\\/, '/') - app2_folder = File.absolute_path('spec/fixtures/workspace_folders/folder2').gsub(/\\/, '/') + app1_folder = File.absolute_path('spec/fixtures/workspace_folders/folder1').gsub('\\', '/') + app2_folder = File.absolute_path('spec/fixtures/workspace_folders/folder2').gsub('\\', '/') host.prepare(app1_folder) host.prepare(app2_folder) file1_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri("#{app1_folder}/app.rb") @@ -119,13 +119,13 @@ expect(app2_map).not_to include('Folder1App') end - it "stops" do + it 'stops' do host = Solargraph::LanguageServer::Host.new host.stop expect(host.stopped?).to be(true) end - it "retains orphaned sources" do + it 'retains orphaned sources' do dir = File.absolute_path('spec/fixtures/workspace') file = File.join(dir, 'lib', 'thing.rb') file_uri = Solargraph::LanguageServer::UriHelpers.uri_to_file(file) @@ -133,12 +133,12 @@ host.prepare(dir) host.open(file_uri, File.read(file), 1) host.remove(dir) - expect{ + expect do host.document_symbols(file_uri) - }.not_to raise_error + end.not_to raise_error end - it "responds with empty diagnostics for unopened files" do + it 'responds with empty diagnostics for unopened files' do host = Solargraph::LanguageServer::Host.new host.diagnose 'file:///file.rb' response = host.flush @@ -147,28 +147,28 @@ expect(json['params']['diagnostics']).to be_empty end - it "rescues runtime errors from messages" do + it 'rescues runtime errors from messages' do host = Solargraph::LanguageServer::Host.new message_class = Class.new(Solargraph::LanguageServer::Message::Base) do def process - raise RuntimeError, 'Always raise an error from this message' + raise 'Always raise an error from this message' end end Solargraph::LanguageServer::Message.register('raiseRuntimeError', message_class) - expect { + expect do host.receive({ - 'id' => 1, - 'method' => 'raiseRuntimeError', - 'params' => {} - }) - }.not_to raise_error + 'id' => 1, + 'method' => 'raiseRuntimeError', + 'params' => {} + }) + end.not_to raise_error end - it "ignores invalid messages" do + it 'ignores invalid messages' do host = Solargraph::LanguageServer::Host.new - expect { + expect do host.receive({ 'bad' => 'message' }) - }.not_to raise_error + end.not_to raise_error end it 'repairs simple breaking changes without incremental sync' do @@ -179,16 +179,16 @@ def process host.open uri, 'Foo::Bar', 1 sleep 0.1 until host.libraries.all?(&:mapped?) host.change({ - "textDocument" => { - "uri" => uri, - 'version' => 2 - }, - "contentChanges" => [ - { - "text" => "Foo::Bar." - } - ] - }) + 'textDocument' => { + 'uri' => uri, + 'version' => 2 + }, + 'contentChanges' => [ + { + 'text' => 'Foo::Bar.' + } + ] + }) source = host.sources.find(uri) # @todo Smelly private method access expect(source.send(:repaired)).to eq('Foo::Bar ') @@ -211,23 +211,23 @@ def initialize(foo); end host.open uri, code, 1 sleep 0.1 until host.libraries.all?(&:mapped?) result = host.locate_pins({ - "data" => { - "uri" => uri, - "location" => { - "range" => { - "start" => { - "line" => 5, - "character" => 12 - }, - "end" => { - "line" => 5, - "character" => 15 - } - } - }, - "path" => "Example.new" - } - }) + 'data' => { + 'uri' => uri, + 'location' => { + 'range' => { + 'start' => { + 'line' => 5, + 'character' => 12 + }, + 'end' => { + 'line' => 5, + 'character' => 15 + } + } + }, + 'path' => 'Example.new' + } + }) expect(result.map(&:path)).to include('Example.new') end end @@ -260,22 +260,22 @@ def initialize(foo); end end end - describe "Workspace variations" do - before :each do + describe 'Workspace variations' do + before do @host = Solargraph::LanguageServer::Host.new end - after :each do + after do @host.stop end - it "creates a library for a file without a workspace" do + it 'creates a library for a file without a workspace' do @host.open('file:///file.rb', 'class Foo; end', 1) symbols = @host.document_symbols('file:///file.rb') expect(symbols).not_to be_empty end - it "opens a file outside of prepared libraries" do + it 'opens a file outside of prepared libraries' do @host.prepare(File.absolute_path(File.join('spec', 'fixtures', 'workspace'))) @host.open('file:///file.rb', 'class Foo; end', 1) symbols = @host.document_symbols('file:///file.rb') diff --git a/spec/language_server/message/completion_item/resolve_spec.rb b/spec/language_server/message/completion_item/resolve_spec.rb index 3ae66dbda..7cf980377 100644 --- a/spec/language_server/message/completion_item/resolve_spec.rb +++ b/spec/language_server/message/completion_item/resolve_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::LanguageServer::Message::CompletionItem::Resolve do - it "returns MarkupContent for documentation" do + it 'returns MarkupContent for documentation' do pin = Solargraph::Pin::Method.new( location: nil, closure: Solargraph::Pin::Namespace.new(name: 'Foo'), @@ -9,16 +9,17 @@ visibility: :public, parameters: [] ) - host = double(Solargraph::LanguageServer::Host, locate_pins: [pin], probe: pin, detail: nil, options: { 'enablePages' => true }) + host = double(Solargraph::LanguageServer::Host, locate_pins: [pin], probe: pin, detail: nil, + options: { 'enablePages' => true }) resolve = Solargraph::LanguageServer::Message::CompletionItem::Resolve.new(host, { - 'params' => pin.completion_item - }) + 'params' => pin.completion_item + }) resolve.process expect(resolve.result[:documentation][:kind]).to eq('markdown') expect(resolve.result[:documentation][:value]).to include('A method') end - it "returns nil documentation for empty strings" do + it 'returns nil documentation for empty strings' do pin = Solargraph::Pin::InstanceVariable.new( location: nil, closure: Solargraph::Pin::Namespace.new(name: 'Foo'), @@ -27,8 +28,8 @@ ) host = double(Solargraph::LanguageServer::Host, locate_pins: [pin], probe: pin, detail: nil) resolve = Solargraph::LanguageServer::Message::CompletionItem::Resolve.new(host, { - 'params' => pin.completion_item - }) + 'params' => pin.completion_item + }) resolve.process expect(resolve.result[:documentation]).to be_nil end diff --git a/spec/language_server/message/extended/check_gem_version_spec.rb b/spec/language_server/message/extended/check_gem_version_spec.rb index 783770ddf..1987287c3 100644 --- a/spec/language_server/message/extended/check_gem_version_spec.rb +++ b/spec/language_server/message/extended/check_gem_version_spec.rb @@ -1,38 +1,39 @@ describe Solargraph::LanguageServer::Message::Extended::CheckGemVersion do - before :each do + before do version = double(:GemVersion, version: Gem::Version.new('1.0.0')) - Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, search_for_dependency: [version]) + Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, + search_for_dependency: [version]) end - after :each do + after do Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = nil end - it "checks the gem source" do + it 'checks the gem source' do host = Solargraph::LanguageServer::Host.new message = described_class.new(host, {}) expect { message.process }.not_to raise_error end - it "performs a verbose check" do + it 'performs a verbose check' do host = Solargraph::LanguageServer::Host.new message = described_class.new(host, { 'params' => { 'verbose' => true } }) expect { message.process }.not_to raise_error end - it "detects available updates" do + it 'detects available updates' do host = Solargraph::LanguageServer::Host.new message = described_class.new(host, {}, current: Gem::Version.new('0.0.1')) expect { message.process }.not_to raise_error end - it "performs a verbose check with an available update" do + it 'performs a verbose check with an available update' do host = Solargraph::LanguageServer::Host.new message = described_class.new(host, { 'params' => { 'verbose' => true } }, current: Gem::Version.new('0.0.1')) expect { message.process }.not_to raise_error end - it "responds to update actions" do + it 'responds to update actions' do status = instance_double(Process::Status) allow(status).to receive(:==).with(0).and_return(true) allow(Open3).to receive(:capture2).with('gem update solargraph').and_return(['', status]) @@ -47,8 +48,8 @@ end reader.receive host.flush action = { - "id" => response['id'], - "result" => response['params']['actions'].first + 'id' => response['id'], + 'result' => response['params']['actions'].first } host.receive action expect(Open3).to have_received(:capture2).with('gem update solargraph') @@ -56,7 +57,7 @@ it 'uses bundler' do host = Solargraph::LanguageServer::Host.new - host.configure({'useBundler' => true}) + host.configure({ 'useBundler' => true }) message = Solargraph::LanguageServer::Message::Extended::CheckGemVersion.new(host, {}, current: Gem::Version.new('0.0.1')) message.process response = nil @@ -65,12 +66,12 @@ response = data end reader.receive host.flush - expect { + expect do action = { - "id" => response['id'], - "result" => response['params']['actions'].first + 'id' => response['id'], + 'result' => response['params']['actions'].first } host.receive action - }.not_to raise_error + end.not_to raise_error end end diff --git a/spec/language_server/message/initialize_spec.rb b/spec/language_server/message/initialize_spec.rb index 3ab54e2de..877add68f 100644 --- a/spec/language_server/message/initialize_spec.rb +++ b/spec/language_server/message/initialize_spec.rb @@ -1,56 +1,56 @@ describe Solargraph::LanguageServer::Message::Initialize do - it "prepares workspace folders" do + it 'prepares workspace folders' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'workspaceFolders' => [ - { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir), - 'name' => 'workspace' - } - ] - } - }) + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'workspaceFolders' => [ + { + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir), + 'name' => 'workspace' + } + ] + } + }) init.process expect(host.folders.length).to eq(1) end - it "prepares rootUri as a workspace" do + it 'prepares rootUri as a workspace' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'rootUri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) - } - }) + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'rootUri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) + } + }) init.process expect(host.folders.length).to eq(1) end - it "prepares rootPath as a workspace" do + it 'prepares rootPath as a workspace' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'rootPath' => dir - } - }) + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'rootPath' => dir + } + }) init.process expect(host.folders.length).to eq(1) end @@ -62,33 +62,35 @@ result = init.result expect(result).to include(:capabilities) expect(result[:capabilities]).to eq({ - textDocumentSync: 2, - workspace: { workspaceFolders: { supported: true, changeNotifications: true } }, - completionProvider: { resolveProvider: true, triggerCharacters: ['.', ':', '@'] }, - signatureHelpProvider: { triggerCharacters: ['(', ','] }, - hoverProvider: true, - documentSymbolProvider: true, - definitionProvider: true, - typeDefinitionProvider: true, - renameProvider: { prepareProvider: true }, - referencesProvider: true, - workspaceSymbolProvider: true, - foldingRangeProvider: true, - documentHighlightProvider: true - }) + textDocumentSync: 2, + workspace: { workspaceFolders: { supported: true, + changeNotifications: true } }, + completionProvider: { resolveProvider: true, + triggerCharacters: ['.', ':', '@'] }, + signatureHelpProvider: { triggerCharacters: ['(', ','] }, + hoverProvider: true, + documentSymbolProvider: true, + definitionProvider: true, + typeDefinitionProvider: true, + renameProvider: { prepareProvider: true }, + referencesProvider: true, + workspaceSymbolProvider: true, + foldingRangeProvider: true, + documentHighlightProvider: true + }) end it 'returns all capabilities when all options are enabled' do host = Solargraph::LanguageServer::Host.new init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'initializationOptions' => { - 'completion' => true, - 'autoformat' => true, - 'formatting' => true - } - } - }) + 'params' => { + 'initializationOptions' => { + 'completion' => true, + 'autoformat' => true, + 'formatting' => true + } + } + }) init.process result = init.result diff --git a/spec/language_server/message/text_document/definition_spec.rb b/spec/language_server/message/text_document/definition_spec.rb index b6c98b99b..1266e4aa0 100644 --- a/spec/language_server/message/text_document/definition_spec.rb +++ b/spec/language_server/message/text_document/definition_spec.rb @@ -36,16 +36,16 @@ file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/other.rb')) other_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/thing.rb')) message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => file_uri - }, - 'position' => { - 'line' => 4, - 'character' => 10 - } - } - }) + 'params' => { + 'textDocument' => { + 'uri' => file_uri + }, + 'position' => { + 'line' => 4, + 'character' => 10 + } + } + }) message.process expect(message.result.first[:uri]).to eq(other_uri) end @@ -57,17 +57,20 @@ sleep 0.1 until host.libraries.all?(&:mapped?) host.catalog message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', 'other.rb')) - }, - 'position' => { - 'line' => 0, - 'character' => 10 - } - } - }) + 'params' => { + 'textDocument' => { + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join( + path, 'lib', 'other.rb' + )) + }, + 'position' => { + 'line' => 0, + 'character' => 10 + } + } + }) message.process - expect(message.result.first[:uri]).to eq(Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', 'thing.rb'))) + expect(message.result.first[:uri]).to eq(Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', + 'thing.rb'))) end end diff --git a/spec/language_server/message/text_document/formatting_spec.rb b/spec/language_server/message/text_document/formatting_spec.rb index 10797e8db..a174a2361 100644 --- a/spec/language_server/message/text_document/formatting_spec.rb +++ b/spec/language_server/message/text_document/formatting_spec.rb @@ -3,7 +3,7 @@ host = double(:Host, read_text: '', formatter_config: {}) request = { 'params' => { - 'textDocument' => { + 'textDocument' => { 'uri' => 'test.rb' } } diff --git a/spec/language_server/message/text_document/hover_spec.rb b/spec/language_server/message/text_document/hover_spec.rb index 74eaee597..93f05c127 100644 --- a/spec/language_server/message/text_document/hover_spec.rb +++ b/spec/language_server/message/text_document/hover_spec.rb @@ -5,16 +5,16 @@ sleep 0.1 until host.libraries.all?(&:mapped?) host.catalog message = Solargraph::LanguageServer::Message::TextDocument::Hover.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => 'file://spec/fixtures/workspace/lib/other.rb' - }, - 'position' => { - 'line' => 5, - 'character' => 0 - } - } - }) + 'params' => { + 'textDocument' => { + 'uri' => 'file://spec/fixtures/workspace/lib/other.rb' + }, + 'position' => { + 'line' => 5, + 'character' => 0 + } + } + }) message.process expect(message.result).to be_nil end @@ -30,16 +30,16 @@ def foo host.open('file:///test.rb', code, 1) host.catalog message = Solargraph::LanguageServer::Message::TextDocument::Hover.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => 'file:///test.rb' - }, - 'position' => { - 'line' => 4, - 'character' => 6 - } - } - }) + 'params' => { + 'textDocument' => { + 'uri' => 'file:///test.rb' + }, + 'position' => { + 'line' => 4, + 'character' => 6 + } + } + }) message.process expect(message.result[:contents][:value]).to eq("x\n\n`=~ String`") end diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 850a9c657..2f9c1afa4 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -5,7 +5,7 @@ "file:///#{Dir.mktmpdir}/file.rb" end - it "renames a symbol" do + it 'renames a symbol' do host = Solargraph::LanguageServer::Host.new host.start host.open(temp_file_url, %( @@ -15,24 +15,24 @@ class Foo ), 1) sleep 0.01 until host.libraries.all?(&:mapped?) rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 1, - 'character' => 12 - }, - 'newName' => 'Bar' - } - }) + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 1, + 'character' => 12 + }, + 'newName' => 'Bar' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(2) end - it "renames an argument symbol from method signature" do + it 'renames an argument symbol from method signature' do host = Solargraph::LanguageServer::Host.new host.start host.open(temp_file_url, %( @@ -45,24 +45,24 @@ def foo(bar) ), 1) rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 2, - 'character' => 14 - }, - 'newName' => 'baz' - } - }) + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 2, + 'character' => 14 + }, + 'newName' => 'baz' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end - it "renames an argument symbol from method body" do + it 'renames an argument symbol from method body' do host = Solargraph::LanguageServer::Host.new host.start host.open(temp_file_url, %( @@ -74,24 +74,24 @@ def foo(bar) end ), 1) rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 3, - 'character' => 6 - }, - 'newName' => 'baz' - } - }) + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 3, + 'character' => 6 + }, + 'newName' => 'baz' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end - it "renames namespace symbol with proper range" do + it 'renames namespace symbol with proper range' do host = Solargraph::LanguageServer::Host.new host.start host.open(temp_file_url, %( @@ -101,19 +101,19 @@ class Namespace::ExampleClass obj = Namespace::ExampleClass.new ), 1) rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 2, - 'character' => 12 - }, - 'newName' => 'Nameplace' - } - }) + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 2, + 'character' => 12 + }, + 'newName' => 'Nameplace' + } + }) rename.process changes = rename.result[:changes][temp_file_url] expect(changes.length).to eq(3) diff --git a/spec/language_server/message/text_document/type_definition_spec.rb b/spec/language_server/message/text_document/type_definition_spec.rb index 2f7ec3668..7fdd9ce6f 100644 --- a/spec/language_server/message/text_document/type_definition_spec.rb +++ b/spec/language_server/message/text_document/type_definition_spec.rb @@ -7,16 +7,16 @@ file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/other.rb')) something_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/something.rb')) message = Solargraph::LanguageServer::Message::TextDocument::TypeDefinition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => file_uri - }, - 'position' => { - 'line' => 4, - 'character' => 10 - } - } - }) + 'params' => { + 'textDocument' => { + 'uri' => file_uri + }, + 'position' => { + 'line' => 4, + 'character' => 10 + } + } + }) message.process expect(message.result.first[:uri]).to eq(something_uri) end diff --git a/spec/language_server/message/workspace/did_change_watched_files_spec.rb b/spec/language_server/message/workspace/did_change_watched_files_spec.rb index b78aa06d3..504947855 100644 --- a/spec/language_server/message/workspace/did_change_watched_files_spec.rb +++ b/spec/language_server/message/workspace/did_change_watched_files_spec.rb @@ -9,16 +9,16 @@ File.write file, 'class Foo; end' uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CREATED, - 'uri' => uri - } - ] - } - }) + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CREATED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) expect(host.library_for(uri)).to be_a(Solargraph::Library) @@ -34,21 +34,21 @@ host = Solargraph::LanguageServer::Host.new host.prepare dir changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::DELETED, - 'uri' => uri - } - ] - } - }) + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::DELETED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) - expect { + expect do host.library_for(uri) - }.to raise_error(Solargraph::FileNotFoundError) + end.to raise_error(Solargraph::FileNotFoundError) end end @@ -61,16 +61,16 @@ host.prepare dir File.write file, 'class FooBar; end' changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CHANGED, - 'uri' => uri - } - ] - } - }) + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CHANGED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) library = host.library_for(uri) @@ -85,16 +85,16 @@ allow(host).to receive(:create) allow(host).to receive(:delete) changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => -1, - 'uri' => 'file:///foo.rb' - } - ] - } - }) + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => -1, + 'uri' => 'file:///foo.rb' + } + ] + } + }) changed.process expect(changed.error).not_to be_nil end diff --git a/spec/language_server/message_spec.rb b/spec/language_server/message_spec.rb index 9a8c26328..4dca484d5 100644 --- a/spec/language_server/message_spec.rb +++ b/spec/language_server/message_spec.rb @@ -1,10 +1,10 @@ describe Solargraph::LanguageServer::Message do - it "returns MethodNotFound for unregistered methods" do + it 'returns MethodNotFound for unregistered methods' do msg = Solargraph::LanguageServer::Message.select 'notARealMethod' expect(msg).to be(Solargraph::LanguageServer::Message::MethodNotFound) end - it "returns MethodNotImplemented for unregistered $ methods" do + it 'returns MethodNotImplemented for unregistered $ methods' do msg = Solargraph::LanguageServer::Message.select '$/notARealMethod' expect(msg).to be(Solargraph::LanguageServer::Message::MethodNotImplemented) end diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index a032146d0..d8a87e1c8 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -1,5 +1,3 @@ -require 'thread' - class Protocol attr_reader :response @@ -43,16 +41,17 @@ def stop @protocol.stop end - before :each do + before do version = double(:GemVersion, version: Gem::Version.new('1.0.0')) - Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, search_for_dependency: [version]) + Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, + search_for_dependency: [version]) end - after :each do + after do Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = nil end - it "handles initialize" do + it 'handles initialize' do @protocol.request 'initialize', { 'capabilities' => { 'textDocument' => { @@ -69,27 +68,27 @@ def stop expect(response['result'].keys).to include('capabilities') end - it "is not stopped after initialization" do + it 'is not stopped after initialization' do expect(@protocol.host.stopped?).to be(false) end - it "configured dynamic registration capabilities from initialize" do + it 'configured dynamic registration capabilities from initialize' do expect(@protocol.host.can_register?('textDocument/completion')).to be(true) expect(@protocol.host.can_register?('textDocument/hover')).to be(false) expect(@protocol.host.can_register?('workspace/symbol')).to be(false) end - it "handles initialized" do + it 'handles initialized' do @protocol.request 'initialized', nil response = @protocol.response expect(response['error']).to be_nil end - it "configured default dynamic registration capabilities from initialized" do + it 'configured default dynamic registration capabilities from initialized' do expect(@protocol.host.registered?('textDocument/completion')).to be(true) end - it "handles textDocument/didOpen" do + it 'handles textDocument/didOpen' do @protocol.request 'textDocument/didOpen', { 'textDocument' => { 'uri' => 'file:///file.rb', @@ -107,7 +106,7 @@ def bar baz 'version' => 0 } } - response = @protocol.response + @protocol.response expect(@protocol.host.open?('file:///file.rb')).to be(true) end @@ -126,7 +125,7 @@ def bar baz expect(response['result'].length).to eq(2) end - it "handles textDocument/didChange" do + it 'handles textDocument/didChange' do @protocol.request 'textDocument/didChange', { 'textDocument' => { 'uri' => 'file:///file.rb', @@ -148,11 +147,11 @@ def bar baz } ] } - response = @protocol.response + @protocol.response # @todo What to expect? end - it "handles textDocument/completion" do + it 'handles textDocument/completion' do @protocol.request 'textDocument/completion', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -167,10 +166,10 @@ def bar baz expect(response['result']['items'].length > 0).to be(true) end - it "handles completionItem/resolve" do + it 'handles completionItem/resolve' do # Reuse the response from textDocument/completion response = @protocol.response - item = response['result']['items'].select{|h| h['label'] == 'bar'}.first + item = response['result']['items'].select { |h| h['label'] == 'bar' }.first @protocol.request 'completionItem/resolve', item response = @protocol.response expect(response['result']['documentation']['value']).to include('bar method') @@ -189,7 +188,7 @@ def bar baz expect(@protocol.response['error']).to be_nil end - it "documents YARD pins" do + it 'documents YARD pins' do @protocol.request 'textDocument/completion', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -200,14 +199,14 @@ def bar baz } } response = @protocol.response - item = response['result']['items'].select{|i| i['data']['path'] == 'File.absolute_path'}.first + item = response['result']['items'].select { |i| i['data']['path'] == 'File.absolute_path' }.first expect(item).not_to be_nil @protocol.request 'completionItem/resolve', item response = @protocol.response expect(response['result']['documentation']).not_to be_empty end - it "handles workspace/symbol" do + it 'handles workspace/symbol' do @protocol.request 'workspace/symbol', { 'query' => 'test' } @@ -215,7 +214,7 @@ def bar baz expect(response['error']).to be_nil end - it "handles textDocument/definition" do + it 'handles textDocument/definition' do sleep 0.5 # HACK: Give the Host::Sources thread time to work @protocol.request 'textDocument/definition', { 'textDocument' => { @@ -231,7 +230,7 @@ def bar baz expect(response['result']).not_to be_empty end - it "handles textDocument/definition on undefined symbols" do + it 'handles textDocument/definition on undefined symbols' do @protocol.request 'textDocument/definition', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -246,7 +245,7 @@ def bar baz expect(response['result']).to be_empty end - it "handles textDocument/documentSymbol" do + it 'handles textDocument/documentSymbol' do @protocol.request 'textDocument/documentSymbol', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -256,7 +255,7 @@ def bar baz expect(response['error']).to be_nil end - it "handles textDocument/hover" do + it 'handles textDocument/hover' do @protocol.request 'textDocument/hover', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -285,7 +284,7 @@ def bar baz expect(@protocol.response['error']).to be_nil end - it "handles textDocument/signatureHelp" do + it 'handles textDocument/signatureHelp' do @protocol.request 'textDocument/signatureHelp', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -300,7 +299,7 @@ def bar baz expect(response['result']['signatures']).not_to be_empty end - it "handles workspace/symbol" do + it 'handles workspace/symbol' do @protocol.request 'workspace/symbol', { 'query' => 'Foo' } @@ -309,7 +308,7 @@ def bar baz expect(response['result']).not_to be_empty end - it "handles textDocument/references for namespaces" do + it 'handles textDocument/references for namespaces' do @protocol.request 'textDocument/references', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -324,7 +323,7 @@ def bar baz expect(response['result']).not_to be_empty end - it "handles textDocument/references for methods" do + it 'handles textDocument/references for methods' do @protocol.request 'textDocument/references', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -339,7 +338,7 @@ def bar baz expect(response['result']).not_to be_empty end - it "handles textDocument/rename" do + it 'handles textDocument/rename' do @protocol.request 'textDocument/rename', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -355,7 +354,7 @@ def bar baz expect(response['result']['changes']['file:///file.rb']).to be_a(Array) end - it "handles textDocument/prepareRename" do + it 'handles textDocument/prepareRename' do @protocol.request 'textDocument/prepareRename', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -371,7 +370,7 @@ def bar baz expect(response['result']).to be_a(Hash) end - it "handles textDocument/foldingRange" do + it 'handles textDocument/foldingRange' do @protocol.request 'textDocument/foldingRange', { 'textDocument' => { 'uri' => 'file:///file.rb' @@ -382,17 +381,17 @@ def bar baz expect(response['result'].length).not_to be_zero end - it "handles textDocument/didClose" do + it 'handles textDocument/didClose' do @protocol.request 'textDocument/didClose', { 'textDocument' => { 'uri' => 'file:///file.rb' } } - response = @protocol.response + @protocol.response expect(@protocol.host.open?('file:///file.rb')).to be(false) end - it "handles $/solargraph/search" do + it 'handles $/solargraph/search' do @protocol.request '$/solargraph/search', { 'query' => 'Foo#bar' } @@ -401,7 +400,7 @@ def bar baz expect(response['result']['content']).not_to be_empty end - it "handles $/solargraph/document" do + it 'handles $/solargraph/document' do @protocol.request '$/solargraph/document', { 'query' => 'String' } @@ -410,7 +409,7 @@ def bar baz expect(response['result']['content']).not_to be_empty end - it "handles workspace/didChangeConfiguration" do + it 'handles workspace/didChangeConfiguration' do @protocol.request 'workspace/didChangeConfiguration', { 'settings' => { 'solargraph' => { @@ -423,7 +422,7 @@ def bar baz expect(@protocol.host.registered?('textDocument/completion')).to be(false) end - it "handles $/solargraph/checkGemVersion" do + it 'handles $/solargraph/checkGemVersion' do @protocol.request '$/solargraph/checkGemVersion', { verbose: false } response = @protocol.response expect(response['error']).to be_nil @@ -431,7 +430,7 @@ def bar baz expect(response['result']['available']).to be_a(String) end - it "handles $/solargraph/documentGems" do + it 'handles $/solargraph/documentGems' do status = instance_double(Process::Status) allow(status).to receive(:==).with(0).and_return(true) allow(Open3).to receive(:capture2).with('solargraph', 'gems').and_return(['', status]) @@ -443,7 +442,7 @@ def bar baz expect(Open3).to have_received(:capture2).with('solargraph', 'gems') end - it "handles textDocument/formatting" do + it 'handles textDocument/formatting' do @protocol.request 'textDocument/didOpen', { 'textDocument' => { 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable.rb')), @@ -461,7 +460,7 @@ def bar baz expect(response['result'].first['newText']).to be_a(String) end - it "can format file without file extension" do + it 'can format file without file extension' do @protocol.request 'textDocument/didOpen', { 'textDocument' => { 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable')), @@ -480,13 +479,13 @@ def bar baz # expect(response['result'].first['newText']).to include('def barbaz(parameter); end') end - it "handles MethodNotFound errors" do + it 'handles MethodNotFound errors' do @protocol.request 'notamethod', {} response = @protocol.response expect(response['error']['code']).to be(Solargraph::LanguageServer::ErrorCodes::METHOD_NOT_FOUND) end - it "handles didChangeWatchedFiles for created files" do + it 'handles didChangeWatchedFiles for created files' do @protocol.request 'workspace/didChangeWatchedFiles', { 'changes' => [ { @@ -499,7 +498,7 @@ def bar baz expect(response['error']).to be_nil end - it "handles didChangeWatchedFiles for changed files" do + it 'handles didChangeWatchedFiles for changed files' do @protocol.request 'workspace/didChangeWatchedFiles', { 'changes' => [ { @@ -512,7 +511,7 @@ def bar baz expect(response['error']).to be_nil end - it "handles didChangeWatchedFiles for deleted files" do + it 'handles didChangeWatchedFiles for deleted files' do @protocol.request 'workspace/didChangeWatchedFiles', { 'changes' => [ { @@ -525,11 +524,11 @@ def bar baz expect(response['error']).to be_nil end - it "handles didChangeWatchedFiles for invalid change types" do + it 'handles didChangeWatchedFiles for invalid change types' do @protocol.request 'workspace/didChangeWatchedFiles', { 'changes' => [ { - 'type' => -99999, + 'type' => -99_999, 'uri' => 'file:///watched-file.rb' } ] @@ -538,7 +537,7 @@ def bar baz expect(response['error']).not_to be_nil end - it "adds folders to the workspace" do + it 'adds folders to the workspace' do dir = File.absolute_path('spec/fixtures/workspace_folders/folder1') uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) @protocol.request 'workspace/didChangeWorkspaceFolders', { @@ -555,7 +554,7 @@ def bar baz expect(@protocol.host.folders).to include(dir) end - it "removes folders from the workspace" do + it 'removes folders from the workspace' do dir = File.absolute_path('spec/fixtures/workspace_folders/folder1') uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) @protocol.request 'workspace/didChangeWorkspaceFolders', { @@ -572,27 +571,27 @@ def bar baz expect(@protocol.host.folders).not_to include(dir) end - it "handles $/cancelRequest" do - expect { + it 'handles $/cancelRequest' do + expect do @protocol.request '$/cancelRequest', { 'id' => 0 } - }.not_to raise_error + end.not_to raise_error end - it "handles $/solargraph/environment" do + it 'handles $/solargraph/environment' do @protocol.request '$/solargraph/environment', {} response = @protocol.response expect(response['result']['content']).not_to be_nil end - it "handles shutdown" do + it 'handles shutdown' do @protocol.request 'shutdown', {} response = @protocol.response expect(response['error']).to be_nil end - it "handles exit" do + it 'handles exit' do @protocol.request 'exit', {} response = @protocol.response expect(response['error']).to be_nil diff --git a/spec/language_server/transport/adapter_spec.rb b/spec/language_server/transport/adapter_spec.rb index 3d30dd4e6..6ec360395 100644 --- a/spec/language_server/transport/adapter_spec.rb +++ b/spec/language_server/transport/adapter_spec.rb @@ -15,21 +15,21 @@ def flush end describe Solargraph::LanguageServer::Transport::Adapter do - it "creates a host on open" do + it 'creates a host on open' do tester = AdapterTester.new tester.opening expect(tester.host).to be_a(Solargraph::LanguageServer::Host) expect(tester.host).not_to be_stopped end - it "stops a host on close" do + it 'stops a host on close' do tester = AdapterTester.new tester.opening tester.closing expect(tester.host).to be_stopped end - it "stops Backport when the host stops" do + it 'stops Backport when the host stops' do tester = AdapterTester.new Backport.run do tester.opening @@ -40,13 +40,13 @@ def flush expect(tester.host).to be_stopped end - it "processes sent data" do + it 'processes sent data' do tester = AdapterTester.new tester.opening message = '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}' - expect { + expect do tester.receiving "Content-Length: #{message.length}\r\n\r\n#{message}" - }.not_to raise_error + end.not_to raise_error tester.closing end end diff --git a/spec/language_server/transport/data_reader_spec.rb b/spec/language_server/transport/data_reader_spec.rb index c035d2b2c..bf9bbcc15 100644 --- a/spec/language_server/transport/data_reader_spec.rb +++ b/spec/language_server/transport/data_reader_spec.rb @@ -1,8 +1,8 @@ describe Solargraph::LanguageServer::Transport::DataReader do - it "rescues exceptions for invalid JSON" do + it 'rescues exceptions for invalid JSON' do reader = Solargraph::LanguageServer::Transport::DataReader.new handled = 0 - reader.set_message_handler do |msg| + reader.set_message_handler do |_msg| handled += 1 end msg = { @@ -10,9 +10,9 @@ method: 'test' }.to_json msg += '}' - expect { + expect do reader.receive "Content-Length:#{msg.bytesize}\r\n\r\n#{msg}" - }.not_to raise_error + end.not_to raise_error expect(handled).to eq(0) end end diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 583512820..4a4108b03 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -2,7 +2,7 @@ require 'yard' describe Solargraph::Library do - it "does not open created files in the workspace" do + it 'does not open created files in the workspace' do Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path workspace_path = File.realpath(temp_dir_path) @@ -15,7 +15,7 @@ end end - it "returns a Completion" do + it 'returns a Completion' do library = Solargraph::Library.new library.attach Solargraph::Source.load_string(%( x = 1 @@ -27,15 +27,11 @@ end context 'with a require from a not-yet-cached external gem' do - before :context do - # avoid race conditions caching/uncaching backport - end - before do Solargraph::Shell.new.uncache('backport') end - it "returns a Completion", time_limit_seconds: 50 do + it 'returns a Completion', time_limit_seconds: 50 do library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( @@ -46,7 +42,6 @@ def foo(adapter) adapter.remo end ), 'file.rb', 0) - completion = nil # give Solargraph time to cache the gem while (completion = library.completions_at('file.rb', 5, 19)).pins.empty? sleep 0.25 @@ -57,15 +52,11 @@ def foo(adapter) end context 'with a require from an already-cached external gem' do - before :context do - # avoid race conditions caching/uncaching backport - end - before do Solargraph::Shell.new.gems('backport') end - it "returns a Completion" do + it 'returns a Completion' do library = Solargraph::Library.new(Solargraph::Workspace.new('', Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( @@ -82,7 +73,7 @@ def foo(adapter) end end - it "gets definitions from a file" do + it 'gets definitions from a file' do library = Solargraph::Library.new src = Solargraph::Source.load_string %( class Foo @@ -95,7 +86,7 @@ def bar expect(paths).to include('Foo#bar') end - it "gets type definitions from a file" do + it 'gets type definitions from a file' do library = Solargraph::Library.new src = Solargraph::Source.load_string %( class Bar; end @@ -111,7 +102,7 @@ def self.bar expect(paths).to include('Bar') end - it "signifies method arguments" do + it 'signifies method arguments' do library = Solargraph::Library.new src = Solargraph::Source.load_string %( class Foo @@ -126,14 +117,14 @@ def bar baz, key: '' expect(pins.first.path).to eq('Foo#bar') end - it "ignores invalid filenames in create_from_disk" do + it 'ignores invalid filenames in create_from_disk' do library = Solargraph::Library.new filename = 'not_a_real_file.rb' expect(library.create_from_disk(filename)).to be(false) expect(library.contain?(filename)).to be(false) end - it "adds mergeable files to the workspace in create_from_disk" do + it 'adds mergeable files to the workspace in create_from_disk' do Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path workspace_path = File.realpath(temp_dir_path) @@ -145,7 +136,7 @@ def bar baz, key: '' end end - it "ignores non-mergeable files in create_from_disk" do + it 'ignores non-mergeable files in create_from_disk' do Dir.mktmpdir do |dir| library = Solargraph::Library.load(dir) filename = File.join(dir, 'created.txt') @@ -155,7 +146,7 @@ def bar baz, key: '' end end - it "diagnoses files" do + it 'diagnoses files' do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( puts 'hello' @@ -180,7 +171,7 @@ def bar baz, key: '' expect(result.to_s).to include('rubocop') end - it "documents symbols" do + it 'documents symbols' do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( class Foo @@ -196,7 +187,7 @@ def bar end describe '#references_from' do - it "collects references to a new method on a constant from assignment of Class.new" do + it 'collects references to a new method on a constant from assignment of Class.new' do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) src1 = Solargraph::Source.load_string(%( @@ -210,10 +201,10 @@ def bar library.catalog locs = library.references_from('file1.rb', 1, 12) expect(locs.map { |l| [l.filename, l.range.start.line] }) - .to eq([["file1.rb", 1]]) + .to eq([['file1.rb', 1]]) end - it "collects references to a new method to a constant from assignment" do + it 'collects references to a new method to a constant from assignment' do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) src1 = Solargraph::Source.load_string(%( @@ -229,10 +220,10 @@ class Foo library.catalog locs = library.references_from('file2.rb', 3, 21) expect(locs.map { |l| [l.filename, l.range.start.line] }) - .to eq([["file1.rb", 1], ["file2.rb", 3]]) + .to eq([['file1.rb', 1], ['file2.rb', 3]]) end - it "collects references to an instance method symbol" do + it 'collects references to an instance method symbol' do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) src1 = Solargraph::Source.load_string(%( @@ -256,10 +247,10 @@ def bar; end library.catalog locs = library.references_from('file2.rb', 2, 11) expect(locs.length).to eq(3) - expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 6}).to be_empty + expect(locs.select { |l| l.filename == 'file2.rb' && l.range.start.line == 6 }).to be_empty end - it "collects references to a class method symbol" do + it 'collects references to a class method symbol' do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) src1 = Solargraph::Source.load_string(%( @@ -289,12 +280,12 @@ def bar; end library.catalog locs = library.references_from('file2.rb', 1, 11) expect(locs.length).to eq(3) - expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 2}).not_to be_empty - expect(locs.select{|l| l.filename == 'file1.rb' && l.range.start.line == 9}).not_to be_empty - expect(locs.select{|l| l.filename == 'file2.rb' && l.range.start.line == 1}).not_to be_empty + expect(locs.select { |l| l.filename == 'file1.rb' && l.range.start.line == 2 }).not_to be_empty + expect(locs.select { |l| l.filename == 'file1.rb' && l.range.start.line == 9 }).not_to be_empty + expect(locs.select { |l| l.filename == 'file2.rb' && l.range.start.line == 1 }).not_to be_empty end - it "collects stripped references to constant symbols" do + it 'collects stripped references to constant symbols' do workspace = Solargraph::Workspace.new('*') library = Solargraph::Library.new(workspace) src1 = Solargraph::Source.load_string(%( @@ -319,7 +310,7 @@ class Other code = library.read_text(l.filename) o1 = Solargraph::Position.to_offset(code, l.range.start) o2 = Solargraph::Position.to_offset(code, l.range.ending) - expect(code[o1..o2-1]).to eq('Foo') + expect(code[o1..o2 - 1]).to eq('Foo') end end @@ -342,20 +333,20 @@ def bar expect(obj_new_locs).to eq([Solargraph::Location.new('test.rb', Solargraph::Range.from_to(6, 12, 6, 15))]) end - it "searches the core for queries" do + it 'searches the core for queries' do library = Solargraph::Library.new result = library.search('String') expect(result).not_to be_empty end - it "returns YARD documentation from the core" do + it 'returns YARD documentation from the core' do library = Solargraph::Library.new - api_map, result = library.document('String') + _, result = library.document('String') expect(result).not_to be_empty expect(result.first).to be_a(Solargraph::Pin::Base) end - it "returns YARD documentation from sources" do + it 'returns YARD documentation from sources' do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( class Foo @@ -364,12 +355,12 @@ def bar; end end ), 'test.rb', 0) library.attach src - api_map, result = library.document('Foo#bar') + _, result = library.document('Foo#bar') expect(result).not_to be_empty expect(result.first).to be_a(Solargraph::Pin::Base) end - it "synchronizes sources from updaters" do + it 'synchronizes sources from updaters' do library = Solargraph::Library.new src = Solargraph::Source.load_string(%( class Foo @@ -390,7 +381,7 @@ def bar; end expect(library.current.code).to eq(repl) end - it "finds unique references" do + it 'finds unique references' do library = Solargraph::Library.new(Solargraph::Workspace.new('*')) src1 = Solargraph::Source.load_string(%( class Foo @@ -406,7 +397,7 @@ class Foo expect(refs.length).to eq(2) end - it "includes method parameters in references" do + it 'includes method parameters in references' do library = Solargraph::Library.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( class Foo @@ -434,7 +425,7 @@ def 🤦🏻foo♀️; 123; end expect(from_def.first.range.start.column).to eq(14) end - it "tells the truth about names when client can handle the truth" do + it 'tells the truth about names when client can handle the truth' do library = Solargraph::Library.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( class Foo @@ -446,7 +437,7 @@ def 🤦🏻foo♀️; 123; end expect(from_def.first.range.start.column).to eq(12) end - it "includes block parameters in references" do + it 'includes block parameters in references' do library = Solargraph::Library.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( 100.times do |foo| @@ -638,29 +629,29 @@ def bar; end describe 'Library#completions_at' do it 'gracefully handles unmapped sources' do - expect { + expect do library.completions_at(good_file, 0, 0) - }.not_to raise_error + end.not_to raise_error end it 'raises errors for nonexistent sources' do - expect { + expect do library.completions_at(bad_file, 0, 0) - }.to raise_error(Solargraph::FileNotFoundError) + end.to raise_error(Solargraph::FileNotFoundError) end end describe 'Library#definitions_at' do it 'gracefully handles unmapped sources' do - expect { + expect do library.definitions_at(good_file, 0, 0) - }.not_to raise_error + end.not_to raise_error end it 'raises errors for nonexistent sources' do - expect { + expect do library.definitions_at(bad_file, 0, 0) - }.to raise_error(Solargraph::FileNotFoundError) + end.to raise_error(Solargraph::FileNotFoundError) end end end diff --git a/spec/logging_spec.rb b/spec/logging_spec.rb index 7d38c087e..0c200594b 100644 --- a/spec/logging_spec.rb +++ b/spec/logging_spec.rb @@ -1,10 +1,10 @@ require 'tempfile' describe Solargraph::Logging do - it "logs messages with levels" do + it 'logs messages with levels' do file = Tempfile.new('log') Solargraph::Logging.logger.reopen file - Solargraph::Logging.logger.warn "Test" + Solargraph::Logging.logger.warn 'Test' file.rewind msg = file.read file.close diff --git a/spec/parser/flow_sensitive_typing_spec.rb b/spec/parser/flow_sensitive_typing_spec.rb index 10c9c0849..cee6afef1 100644 --- a/spec/parser/flow_sensitive_typing_spec.rb +++ b/spec/parser/flow_sensitive_typing_spec.rb @@ -979,7 +979,6 @@ def foo a expect(clip.infer.to_s).to eq('Integer') end - it 'supports !@x.nil && @x.y' do source = Solargraph::Source.load_string(%( class Bar diff --git a/spec/parser/node_chainer_spec.rb b/spec/parser/node_chainer_spec.rb index 5f3865b13..1c1213564 100644 --- a/spec/parser/node_chainer_spec.rb +++ b/spec/parser/node_chainer_spec.rb @@ -3,31 +3,31 @@ def chain_string str Solargraph::Parser.chain_string(str, 'file.rb', 0) end - it "recognizes self keywords" do + it 'recognizes self keywords' do chain = chain_string('self.foo') expect(chain.links.first.word).to eq('self') expect(chain.links.first).to be_a(Solargraph::Source::Chain::Head) end - it "recognizes super keywords" do + it 'recognizes super keywords' do chain = chain_string('super.foo') expect(chain.links.first.word).to eq('super') expect(chain.links.first).to be_a(Solargraph::Source::Chain::ZSuper) end - it "recognizes constants" do + it 'recognizes constants' do chain = chain_string('Foo::Bar') expect(chain.links.length).to eq(1) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Constant) expect(chain.links.map(&:word)).to eq(['Foo::Bar']) end - it "splits method calls with arguments and blocks" do + it 'splits method calls with arguments and blocks' do chain = chain_string('var.meth1(1, 2).meth2 do; end') - expect(chain.links.map(&:word)).to eq(['var', 'meth1', 'meth2']) + expect(chain.links.map(&:word)).to eq(%w[var meth1 meth2]) end - it "recognizes literals" do + it 'recognizes literals' do chain = chain_string('"string"') expect(chain).to be_literal chain = chain_string('100') @@ -38,22 +38,22 @@ def chain_string str expect(chain).to be_literal end - it "recognizes instance variables" do + it 'recognizes instance variables' do chain = chain_string('@foo') expect(chain.links.first).to be_a(Solargraph::Source::Chain::InstanceVariable) end - it "recognizes class variables" do + it 'recognizes class variables' do chain = chain_string('@@foo') expect(chain.links.first).to be_a(Solargraph::Source::Chain::ClassVariable) end - it "recognizes global variables" do + it 'recognizes global variables' do chain = chain_string('$foo') expect(chain.links.first).to be_a(Solargraph::Source::Chain::GlobalVariable) end - it "operates on nodes" do + it 'operates on nodes' do source = Solargraph::Source.load_string(%( class Foo Bar.meth1(1, 2).meth2{} @@ -61,7 +61,7 @@ class Foo )) node = source.node_at(2, 26) chain = Solargraph::Parser.chain(node) - expect(chain.links.map(&:word)).to eq(['Bar', 'meth1', 'meth2']) + expect(chain.links.map(&:word)).to eq(%w[Bar meth1 meth2]) end it 'chains and/or nodes' do diff --git a/spec/parser/node_methods_spec.rb b/spec/parser/node_methods_spec.rb index 867180bc6..039059b0f 100644 --- a/spec/parser/node_methods_spec.rb +++ b/spec/parser/node_methods_spec.rb @@ -5,64 +5,64 @@ def parse source Solargraph::Parser.parse(source, 'test.rb', 0) end - it "unpacks constant nodes into strings" do - ast = parse("Foo::Bar") - expect(Solargraph::Parser::NodeMethods.unpack_name(ast)).to eq "Foo::Bar" + it 'unpacks constant nodes into strings' do + ast = parse('Foo::Bar') + expect(Solargraph::Parser::NodeMethods.unpack_name(ast)).to eq 'Foo::Bar' end - it "infers literal strings" do + it 'infers literal strings' do ast = parse("x = 'string'") expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::String' end - it "infers literal hashes" do - ast = parse("x = {}") + it 'infers literal hashes' do + ast = parse('x = {}') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Hash' end - it "infers literal arrays" do - ast = parse("x = []") + it 'infers literal arrays' do + ast = parse('x = []') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Array' end - it "infers literal integers" do - ast = parse("x = 100") + it 'infers literal integers' do + ast = parse('x = 100') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Integer' end - it "infers literal floats" do - ast = parse("x = 10.1") + it 'infers literal floats' do + ast = parse('x = 10.1') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Float' end - it "infers literal symbols" do - ast = parse(":symbol") + it 'infers literal symbols' do + ast = parse(':symbol') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' end - it "infers double quoted symbols" do + it 'infers double quoted symbols' do ast = parse(':"symbol"') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' end - it "infers interpolated double quoted symbols" do + it 'infers interpolated double quoted symbols' do ast = parse(':"#{Object}"') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' end - it "infers single quoted symbols" do + it 'infers single quoted symbols' do ast = parse(":'symbol'") expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' end it 'infers literal booleans' do - true_ast = parse("true") + true_ast = parse('true') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(true_ast)).to eq '::Boolean' - false_ast = parse("false") + false_ast = parse('false') expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(false_ast)).to eq '::Boolean' end - it "handles return nodes with implicit nil values" do + it 'handles return nodes with implicit nil values' do node = parse(%( return if true )) @@ -72,7 +72,7 @@ def parse source expect(rets.length).to eq(2) end - it "handles return nodes with implicit nil values" do + it 'handles return nodes with implicit nil values' do node = parse(%( return bla if true )) @@ -130,7 +130,7 @@ def parse source expect(returns.length).to eq(2) end - it "handles return nodes in reduceable (begin) nodes" do + it 'handles return nodes in reduceable (begin) nodes' do # @todo Temporarily disabled. Result is 3 nodes instead of 2. # node = parse(%( # begin @@ -141,7 +141,7 @@ def parse source # expect(rets.length).to eq(2) end - it "handles return nodes after other nodes" do + it 'handles return nodes after other nodes' do node = parse(%( x = 1 return x @@ -150,7 +150,7 @@ def parse source expect(rets.length).to eq(1) end - it "handles return nodes with unreachable code" do + it 'handles return nodes with unreachable code' do node = parse(%( x = 1 return x @@ -160,7 +160,7 @@ def parse source expect(rets.length).to eq(1) end - it "handles conditional returns with following code" do + it 'handles conditional returns with following code' do node = parse(%( x = 1 return x if foo @@ -170,7 +170,7 @@ def parse source expect(rets.length).to eq(2) end - it "handles return nodes with reduceable code" do + it 'handles return nodes with reduceable code' do node = parse(%( return begin x if foo @@ -216,7 +216,7 @@ def parse source end )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:block, :lvar]) + expect(rets.map(&:type)).to eq(%i[block lvar]) end it 'finds correct return node line in begin expressions' do @@ -240,10 +240,10 @@ def parse source nil )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:lvar, :nil]) + expect(rets.map(&:type)).to eq(%i[lvar nil]) end - it "handles return nodes with implicit nil values" do + it 'handles return nodes with implicit nil values' do node = parse(%( return if true )) @@ -253,15 +253,15 @@ def parse source expect(rets.length).to eq(2) end - it "handles return nodes with implicit nil values" do + it 'handles return nodes with implicit nil values' do node = parse(%( return bla if true )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:send, :nil]) + expect(rets.map(&:type)).to eq(%i[send nil]) end - it "handles return nodes in reduceable (begin) nodes" do + it 'handles return nodes in reduceable (begin) nodes' do # @todo Temporarily disabled. Result is 3 nodes instead of 2 in legacy. # node = parse(%( # begin @@ -272,7 +272,7 @@ def parse source # expect(rets.length).to eq(2) end - it "handles return nodes after other nodes" do + it 'handles return nodes after other nodes' do node = parse(%( x = 1 return x @@ -281,7 +281,7 @@ def parse source expect(rets.map(&:type)).to eq([:lvar]) end - it "handles return nodes with unreachable code" do + it 'handles return nodes with unreachable code' do node = parse(%( x = 1 return x @@ -291,7 +291,7 @@ def parse source expect(rets.length).to eq(1) end - it "short-circuits return node finding after a raise statement in a begin expression" do + it 'short-circuits return node finding after a raise statement in a begin expression' do pending('case being handled') node = parse(%( @@ -302,7 +302,7 @@ def parse source expect(rets.length).to eq(0) end - it "does not short circuit return node finding after a raise statement in a conditional" do + it 'does not short circuit return node finding after a raise statement in a conditional' do node = parse(%( x = 1 raise "Error" if foo @@ -312,7 +312,7 @@ def parse source expect(rets.length).to eq(1) end - it "does not short circuit return node finding after a return statement in a conditional" do + it 'does not short circuit return node finding after a return statement in a conditional' do node = parse(%( x = 1 return "Error" if foo @@ -322,7 +322,7 @@ def parse source expect(rets.length).to eq(2) end - it "handles return nodes with reduceable code" do + it 'handles return nodes with reduceable code' do node = parse(%( return begin x if foo @@ -354,7 +354,7 @@ def parse source it "handles nested 'or' nodes from return" do node = parse('return 1 || "2"') rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:int, :str]) + expect(rets.map(&:type)).to eq(%i[int str]) end it 'finds return nodes in blocks' do @@ -364,7 +364,7 @@ def parse source end )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:block, :lvar]) + expect(rets.map(&:type)).to eq(%i[block lvar]) # expect(rets[1].type).to eq(:DVAR) end @@ -378,7 +378,7 @@ def parse source nil )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:lvar, :nil]) + expect(rets.map(&:type)).to eq(%i[lvar nil]) # expect(rets[0].type).to eq(:DVAR) end @@ -391,7 +391,7 @@ def parse source end )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:str, :str]) + expect(rets.map(&:type)).to eq(%i[str str]) end it 'handles return nodes from case statements without else' do @@ -402,7 +402,7 @@ def parse source end )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:str, :nil]) + expect(rets.map(&:type)).to eq(%i[str nil]) end it 'handles return nodes from case statements with super' do @@ -415,7 +415,7 @@ def parse source end )) rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) - expect(rets.map(&:type)).to eq([:send, :zsuper]) + expect(rets.map(&:type)).to eq(%i[send zsuper]) end describe 'convert_hash' do diff --git a/spec/parser/node_processor_spec.rb b/spec/parser/node_processor_spec.rb index 2033e21ca..6785b9a09 100644 --- a/spec/parser/node_processor_spec.rb +++ b/spec/parser/node_processor_spec.rb @@ -9,9 +9,9 @@ class Foo private_constant end )) - expect { + expect do Solargraph::Parser::NodeProcessor.process(node) - }.not_to raise_error + end.not_to raise_error end it 'orders optional args correctly' do diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 3c1e3cca0..1757bb90d 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -3,7 +3,7 @@ def parse source Solargraph::Parser.parse(source, 'file.rb', 0) end - it "parses nodes" do + it 'parses nodes' do node = parse('class Foo; end') expect(Solargraph::Parser.is_ast_node?(node)).to be(true) end diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index 296b1e349..6e38e3628 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -2,7 +2,7 @@ let(:zero_location) { Solargraph::Location.new('test.rb', Solargraph::Range.from_to(0, 0, 0, 0)) } let(:one_location) { Solargraph::Location.new('test.rb', Solargraph::Range.from_to(0, 0, 1, 0)) } - it "will not combine pins with directive changes" do + it 'does not combine pins with directive changes' do pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A Foo class', source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', @@ -14,7 +14,7 @@ end end - it "will not combine pins with different directives" do + it 'does not combine pins with different directives' do pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro other', @@ -25,26 +25,26 @@ end end - it "sees tag differences as not near or equal" do + it 'sees tag differences as not near or equal' do pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@return [Foo]') pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@return [Bar]') expect(pin1.nearly?(pin2)).to be(false) expect(pin1 == pin2).to be(false) end - it "sees comment differences as nearly but not equal" do + it 'sees comment differences as nearly but not equal' do pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A Foo class') pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A different Foo') expect(pin1.nearly?(pin2)).to be(true) expect(pin1 == pin2).to be(false) end - it "recognizes deprecated tags" do + it 'recognizes deprecated tags' do pin = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@deprecated Use Bar instead.') expect(pin).to be_deprecated end - it "does not link documentation for undefined return types" do + it 'does not link documentation for undefined return types' do pin = Solargraph::Pin::Base.new(name: 'Foo', comments: '@return [undefined]') expect(pin.link_documentation).to eq('Foo') end @@ -62,8 +62,8 @@ expect(pins.length).to eq(1) parser_method_pin = pins.first return_type = parser_method_pin.typify(api_map) - expect(parser_method_pin.closure.name).to eq("Docstring") - expect(parser_method_pin.closure.gates).to eq(["YARD::Docstring", "YARD", '']) + expect(parser_method_pin.closure.name).to eq('Docstring') + expect(parser_method_pin.closure.gates).to eq(['YARD::Docstring', 'YARD', '']) expect(return_type).to be_defined expect(parser_method_pin.typify(api_map).rooted_tags).to eq('::YARD::DocstringParser') end diff --git a/spec/pin/base_variable_spec.rb b/spec/pin/base_variable_spec.rb index 03e6b1a11..06d3fb1f8 100644 --- a/spec/pin/base_variable_spec.rb +++ b/spec/pin/base_variable_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Pin::BaseVariable do - it "checks assignments for equality" do + it 'checks assignments for equality' do smap = Solargraph::SourceMap.load_string('foo = "foo"') pin1 = smap.locals.first smap = Solargraph::SourceMap.load_string('foo = "foo"') @@ -46,7 +46,7 @@ def bar end xit "understands proc kwarg parameters aren't affected by @type" do - pending "understanding restarg in block param in Block#typify_parameters" + pending 'understanding restarg in block param in Block#typify_parameters' code = %( # @return [Proc] diff --git a/spec/pin/constant_spec.rb b/spec/pin/constant_spec.rb index 7a7fdb0c9..124c36174 100644 --- a/spec/pin/constant_spec.rb +++ b/spec/pin/constant_spec.rb @@ -1,23 +1,23 @@ describe Solargraph::Pin::Constant do - it "resolves constant paths" do + it 'resolves constant paths' do source = Solargraph::Source.new(%( class Foo BAR = 'bar' end )) map = Solargraph::SourceMap.map(source) - pin = map.pins.select{|pin| pin.name == 'BAR'}.first + pin = map.pins.select { |pin| pin.name == 'BAR' }.first expect(pin.path).to eq('Foo::BAR') end - it "is a constant kind" do + it 'is a constant kind' do source = Solargraph::Source.new(%( class Foo BAR = 'bar' end )) map = Solargraph::SourceMap.map(source) - pin = map.pins.select{|pin| pin.name == 'BAR'}.first + pin = map.pins.select { |pin| pin.name == 'BAR' }.first expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::CONSTANT) expect(pin.symbol_kind).to eq(Solargraph::LanguageServer::SymbolKinds::CONSTANT) end diff --git a/spec/pin/documenting_spec.rb b/spec/pin/documenting_spec.rb index 1fdf9b8de..42da36dd1 100644 --- a/spec/pin/documenting_spec.rb +++ b/spec/pin/documenting_spec.rb @@ -1,11 +1,11 @@ describe Solargraph::Pin::Documenting do - let(:object) { + let(:object) do Class.new do include Solargraph::Pin::Documenting attr_accessor :docstring end.new - } + end it 'parses indented code blocks' do object.docstring = YARD::Docstring.new(%(Method overview diff --git a/spec/pin/instance_variable_spec.rb b/spec/pin/instance_variable_spec.rb index fd6203398..c9fd036d7 100644 --- a/spec/pin/instance_variable_spec.rb +++ b/spec/pin/instance_variable_spec.rb @@ -1,13 +1,13 @@ describe Solargraph::Pin::InstanceVariable do - it "is a kind of variable" do + it 'is a kind of variable' do source = Solargraph::Source.load_string("@foo = 'foo'", 'file.rb') map = Solargraph::SourceMap.map(source) - pin = map.pins.select{ |p| p.is_a?(Solargraph::Pin::InstanceVariable) }.first + pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::InstanceVariable) }.first expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::VARIABLE) expect(pin.symbol_kind).to eq(Solargraph::LanguageServer::SymbolKinds::VARIABLE) end - it "does not link documentation for undefined return types" do + it 'does not link documentation for undefined return types' do pin = Solargraph::Pin::InstanceVariable.new(name: '@bar') expect(pin.link_documentation).to eq('@bar') end diff --git a/spec/pin/keyword_spec.rb b/spec/pin/keyword_spec.rb index 82bb373ef..99e51f287 100644 --- a/spec/pin/keyword_spec.rb +++ b/spec/pin/keyword_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Pin::Keyword do - it "is a kind of keyword" do + it 'is a kind of keyword' do pin = Solargraph::Pin::Keyword.new('foo') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end diff --git a/spec/pin/local_variable_spec.rb b/spec/pin/local_variable_spec.rb index 39fd22c28..9bf722280 100644 --- a/spec/pin/local_variable_spec.rb +++ b/spec/pin/local_variable_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Pin::LocalVariable do - xit "merges presence changes so that [not currently used]" do + xit 'merges presence changes so that [not currently used]' do map1 = Solargraph::SourceMap.load_string(%( class Foo foo = 'foo' @@ -26,7 +26,6 @@ class Foo expect { pin1.combine_with(pin2) }.to raise_error(RuntimeError, /Inconsistent :closure name/) end - expect(combined.source).to eq(:combined) # no choice behavior defined yet - if/when this is to be used, we # should indicate which one should override in the range situation @@ -40,7 +39,7 @@ class Foo ), 'test.rb') api_map = Solargraph::ApiMap.new api_map.map source - clip = api_map.clip_at('test.rb', [2, 0]) + api_map.clip_at('test.rb', [2, 0]) object_pin = api_map.source_map('test.rb').locals.find { |p| p.name == 'object' } expect(object_pin).not_to be_nil location = Solargraph::Location.new('test.rb', Solargraph::Range.from_to(2, 0, 2, 0)) @@ -85,14 +84,14 @@ class Foo; end expect(x_pin.visible_at?(block_pin, location)).to be true end - it "understands local lookup in root scope" do + it 'understands local lookup in root scope' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( # @type [Array] arr = [] - ), "test.rb") + ), 'test.rb') api_map.map source arr_pin = api_map.source_map('test.rb').locals.find { |p| p.name == 'arr' } expect(arr_pin).not_to be_nil @@ -142,7 +141,7 @@ def bar end it 'is visible within each block scope inside function' do - source = Solargraph::Source.load_string(%( + source = Solargraph::Source.load_string(%( class Foo def bar x = 1 @@ -152,17 +151,17 @@ def bar end end ), 'test.rb') - api_map = Solargraph::ApiMap.new - api_map.map source - x = api_map.source_map('test.rb').locals.find { |p| p.name == 'x' } - bar_method = api_map.get_path_pins('Foo#bar').first - each_block_pin = api_map.get_block_pins.find do |b| - b.location.range.start.line == 4 - end - expect(each_block_pin).not_to be_nil - range = Solargraph::Range.from_to(5, 24, 5, 25) - location = Solargraph::Location.new('test.rb', range) - expect(x.visible_at?(each_block_pin, location)).to be true + api_map = Solargraph::ApiMap.new + api_map.map source + x = api_map.source_map('test.rb').locals.find { |p| p.name == 'x' } + api_map.get_path_pins('Foo#bar').first + each_block_pin = api_map.get_block_pins.find do |b| + b.location.range.start.line == 4 + end + expect(each_block_pin).not_to be_nil + range = Solargraph::Range.from_to(5, 24, 5, 25) + location = Solargraph::Location.new('test.rb', range) + expect(x.visible_at?(each_block_pin, location)).to be true end it 'sees block parameter inside block' do diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index c4d33b0d0..feb55698a 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -116,14 +116,14 @@ def bazzle; end expect(pin.return_type).to be_undefined end - it 'will not merge with changes in parameters' do + it 'does not merge with changes in parameters' do # @todo Method pin parameters are pins now pin1 = Solargraph::Pin::Method.new(name: 'bar', parameters: %w[one two]) pin2 = Solargraph::Pin::Method.new(name: 'bar', parameters: ['three']) expect(pin1.nearly?(pin2)).to be(false) end - it 'will not merge with changes in YARD return types' do + it 'does not merge with changes in YARD return types' do pin1 = Solargraph::Pin::Method.new(name: 'foo', comments: '@return [String]') pin2 = Solargraph::Pin::Method.new(name: 'foo', comments: '@return [Integer]') expect(pin1.nearly?(pin2)).to be(false) @@ -485,8 +485,8 @@ def bar param1, param2 api_map.map source pin = api_map.get_path_pins('Example#bar').first pin.resolve_ref_tag(api_map) - expect(pin.docstring.tags(:param).map(&:name)).to eq(['param1', 'param2']) - expect(pin.docstring.tags(:param).map(&:type)).to eq(['String', 'Integer']) + expect(pin.docstring.tags(:param).map(&:name)).to eq(%w[param1 param2]) + expect(pin.docstring.tags(:param).map(&:type)).to eq(%w[String Integer]) end it 'resolves ref tags with namespaces' do @@ -508,8 +508,8 @@ def bar param1, param2 api_map.map source pin = api_map.get_path_pins('Example2#bar').first pin.resolve_ref_tag(api_map) - expect(pin.docstring.tags(:param).map(&:name)).to eq(['param1', 'param2']) - expect(pin.docstring.tags(:param).map(&:type)).to eq(['String', 'Integer']) + expect(pin.docstring.tags(:param).map(&:name)).to eq(%w[param1 param2]) + expect(pin.docstring.tags(:param).map(&:type)).to eq(%w[String Integer]) end context 'as attribute' do diff --git a/spec/pin/namespace_spec.rb b/spec/pin/namespace_spec.rb index 3c94a526e..0421def05 100644 --- a/spec/pin/namespace_spec.rb +++ b/spec/pin/namespace_spec.rb @@ -1,11 +1,11 @@ describe Solargraph::Pin::Namespace do - it "handles long namespaces" do + it 'handles long namespaces' do pin = Solargraph::Pin::Namespace.new(closure: Solargraph::Pin::Namespace.new(name: 'Foo'), name: 'Bar') expect(pin.path).to eq('Foo::Bar') end - it "has class scope" do - source = Solargraph::Source.load_string(%( + it 'has class scope' do + Solargraph::Source.load_string(%( class Foo end )) @@ -13,7 +13,7 @@ class Foo expect(pin.context.scope).to eq(:class) end - it "is a kind of namespace/class/module" do + it 'is a kind of namespace/class/module' do pin1 = Solargraph::Pin::Namespace.new(name: 'Foo') expect(pin1.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::CLASS) pin2 = Solargraph::Pin::Namespace.new(name: 'Foo', type: :module) diff --git a/spec/pin/symbol_spec.rb b/spec/pin/symbol_spec.rb index 16961cadc..141dbf463 100644 --- a/spec/pin/symbol_spec.rb +++ b/spec/pin/symbol_spec.rb @@ -1,53 +1,52 @@ describe Solargraph::Pin::Symbol do - context "as an unquoted literal" do - it "is a kind of keyword to the LSP" do + context 'as an unquoted literal' do + it 'is a kind of keyword to the LSP' do pin = Solargraph::Pin::Symbol.new(nil, ':symbol') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end - it "has global closure" do + it 'has global closure' do pin = Solargraph::Pin::Symbol.new(nil, ':symbol') expect(pin.closure).to eq(Solargraph::Pin::ROOT_PIN) end - it "has a Symbol return type" do + it 'has a Symbol return type' do pin = Solargraph::Pin::Symbol.new(nil, ':symbol') expect(pin.return_type.tag).to eq('Symbol') end end - context "as a double quoted literal" do - it "is a kind of keyword" do + context 'as a double quoted literal' do + it 'is a kind of keyword' do pin = Solargraph::Pin::Symbol.new(nil, ':"symbol"') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end - it "has a Symbol return type" do + it 'has a Symbol return type' do pin = Solargraph::Pin::Symbol.new(nil, ':"symbol"') expect(pin.return_type.tag).to eq('Symbol') end end - context "as a double quoted interpolatd literal" do - it "is a kind of keyword" do + context 'as a double quoted interpolatd literal' do + it 'is a kind of keyword' do pin = Solargraph::Pin::Symbol.new(nil, ':"symbol #{variable}"') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end - it "has a Symbol return type" do + it 'has a Symbol return type' do pin = Solargraph::Pin::Symbol.new(nil, ':"symbol #{variable}"') expect(pin.return_type.tag).to eq('Symbol') end end - - context "as a single quoted literal" do - it "is a kind of keyword" do + context 'as a single quoted literal' do + it 'is a kind of keyword' do pin = Solargraph::Pin::Symbol.new(nil, ":'symbol'") expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end - it "has a Symbol return type" do + it 'has a Symbol return type' do pin = Solargraph::Pin::Symbol.new(nil, ":'symbol'") expect(pin.return_type.tag).to eq('Symbol') end diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 0f766117a..83cf7a3b7 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -150,7 +150,8 @@ pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) # match arguments with regexp using rspec-matchers syntax - expect(File).to have_received(:write).with(%r{combined/.*/rubocop-yard-.*-export.ser$}, any_args, mode: 'wb').once + expect(File).to have_received(:write).with(%r{combined/.*/rubocop-yard-.*-export.ser$}, any_args, + mode: 'wb').once end end end diff --git a/spec/position_spec.rb b/spec/position_spec.rb index e8dab1960..5612e8a5e 100644 --- a/spec/position_spec.rb +++ b/spec/position_spec.rb @@ -1,12 +1,12 @@ describe Solargraph::Position do - it "normalizes arrays into positions" do + it 'normalizes arrays into positions' do pos = Solargraph::Position.normalize([0, 1]) expect(pos).to be_a(Solargraph::Position) expect(pos.line).to eq(0) expect(pos.column).to eq(1) end - it "returns original positions when normalizing" do + it 'returns original positions when normalizing' do orig = Solargraph::Position.new(0, 1) norm = Solargraph::Position.normalize(orig) expect(orig).to be(norm) @@ -29,10 +29,10 @@ expect(Solargraph::Position.from_offset(text, 44)).to eq(Solargraph::Position.new(2, 27)) end - it "raises an error for objects that cannot be normalized" do - expect { + it 'raises an error for objects that cannot be normalized' do + expect do Solargraph::Position.normalize('0, 1') - }.to raise_error(ArgumentError) + end.to raise_error(ArgumentError) end it 'avoids fencepost errors' do diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 3fc801fa6..0afd34e91 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -3,7 +3,7 @@ # create a temporary directory with the scope of the spec around do |example| require 'tmpdir' - Dir.mktmpdir("rspec-solargraph-") do |dir| + Dir.mktmpdir('rspec-solargraph-') do |dir| @temp_dir = dir example.run end @@ -97,7 +97,7 @@ def bar: () -> untyped # Use :context here instead of :all so that parallel_rspec runs these on the same worker and we only have to cache these gems on one worker before :context do @api_map = Solargraph::ApiMap.load('.') - gems = ['parser', 'ast', 'open3'] + gems = %w[parser ast open3] bench = Solargraph::Bench.new(workspace: @api_map.workspace, external_requires: gems) @api_map.catalog(bench) @api_map.cache_all_for_doc_map! @@ -107,11 +107,6 @@ def bar: () -> untyped let(:api_map) { @api_map } context 'with superclass pin for Parser::AST::Node' do - before :context do - # include this to flag to parallel rspec that it should not try - # to parallelize this context - end - let(:superclass_pin) do api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' @@ -126,12 +121,6 @@ def bar: () -> untyped # https://github.com/castwide/solargraph/issues/1042 context 'with Hash superclass with untyped value and alias' do - before :context do - # include this to flag to parallel rspec that it should not - # try to parallelize this context - it does not seem smart - # enough to use the above - end - let(:rbs) do <<~RBS class Sub < Hash[Symbol, untyped] diff --git a/spec/rbs_map/core_map_spec.rb b/spec/rbs_map/core_map_spec.rb index cada2754c..229db4cc2 100644 --- a/spec/rbs_map/core_map_spec.rb +++ b/spec/rbs_map/core_map_spec.rb @@ -16,12 +16,12 @@ store = Solargraph::ApiMap::Store.new(map.pins) # The core RBS contains: # class Mutex = Thread::Mutex - thread_mutex_pin = store.get_path_pins("Thread::Mutex").first + thread_mutex_pin = store.get_path_pins('Thread::Mutex').first expect(thread_mutex_pin).to be_a(Solargraph::Pin::Namespace) - mutex_pin = store.get_path_pins("Mutex").first + mutex_pin = store.get_path_pins('Mutex').first expect(mutex_pin).to be_a(Solargraph::Pin::Constant) - expect(mutex_pin.return_type.to_s).to eq("Class") + expect(mutex_pin.return_type.to_s).to eq('Class') end it 'understands RBS global variables' do @@ -39,10 +39,10 @@ it 'understands implied Enumerator#each method' do api_map = Solargraph::ApiMap.new methods = api_map.get_methods('Enumerable') - each_pins = methods.select{|pin| pin.path.end_with?('#each')} + each_pins = methods.select { |pin| pin.path.end_with?('#each') } # expect this to come from the _Each implied interface ("self # type") defined in the RBS - expect(each_pins.map(&:path)).to eq(["_Each#each"]) + expect(each_pins.map(&:path)).to eq(['_Each#each']) expect(each_pins.map(&:class)).to eq([Solargraph::Pin::Method]) each_pin = each_pins.first expect(each_pin.signatures.length).to eq(1) @@ -53,7 +53,7 @@ it 'populates types in block parameters from generics' do api_map = Solargraph::ApiMap.new methods = api_map.get_methods('Enumerable') - each_pins = methods.select{|pin| pin.path.end_with?('#each')} + each_pins = methods.select { |pin| pin.path.end_with?('#each') } each_pin = each_pins.first signature = each_pin.signatures.first expect(signature.block.parameters.map(&:return_type).map(&:to_s)).to eq(['String']) @@ -69,7 +69,7 @@ # api_map = Solargraph::ApiMap.new methods = api_map.get_methods('Enumerable') - each_pins = methods.select{|pin| pin.path.end_with?('#each')} + each_pins = methods.select { |pin| pin.path.end_with?('#each') } each_pin = each_pins.first signature = each_pin.signatures.first expect(signature.return_type.to_s).to eq('Enumerable') @@ -97,12 +97,12 @@ class Foo expect(clip.infer.to_s).to eq('Foo') end - it "generates rooted pins from RBS for core" do + it 'generates rooted pins from RBS for core' do map = Solargraph::RbsMap::CoreMap.new map.pins.each do |pin| expect(pin).to be_all_rooted unless pin.is_a?(Solargraph::Pin::Keyword) - expect(pin.closure).to_not be_nil, ->(){ "Pin #{pin.inspect} (#{pin.path}) has no closure" } + expect(pin.closure).not_to be_nil, -> { "Pin #{pin.inspect} (#{pin.path}) has no closure" } end end end diff --git a/spec/rbs_map/stdlib_map_spec.rb b/spec/rbs_map/stdlib_map_spec.rb index c9db9bb48..fe8c81b85 100644 --- a/spec/rbs_map/stdlib_map_spec.rb +++ b/spec/rbs_map/stdlib_map_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::RbsMap::StdlibMap do - it "finds stdlib require paths" do + it 'finds stdlib require paths' do rbs_map = Solargraph::RbsMap::StdlibMap.load('fileutils') pin = rbs_map.path_pin('FileUtils#chdir') expect(pin).to be diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 52301d5bb..e37b3d9b6 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -4,21 +4,16 @@ require 'open3' describe Solargraph::Shell do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:shell) { described_class.new } let(:temp_dir) { Dir.mktmpdir } before do File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| - file.puts "source 'https://rubygems.org'" - file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" + file.puts "source 'https://rubygems.org'" + file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" end - output, status = Open3.capture2e("bundle install", chdir: temp_dir) + output, status = Open3.capture2e('bundle install', chdir: temp_dir) raise "Failure installing bundle: #{output}" unless status.success? end @@ -37,11 +32,6 @@ def bundle_exec(*cmd) end describe '--version' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:output) { bundle_exec('solargraph', '--version') } it 'returns output' do @@ -54,11 +44,6 @@ def bundle_exec(*cmd) end describe 'uncache' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'uncaches without erroring out' do output = capture_stdout do shell.uncache('backport') @@ -77,17 +62,7 @@ def bundle_exec(*cmd) end describe 'scan' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - context 'with mocked dependencies' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:api_map) { instance_double(Solargraph::ApiMap) } before do @@ -107,17 +82,7 @@ def bundle_exec(*cmd) end describe 'typecheck' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - context 'with mocked dependencies' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:type_checker) { instance_double(Solargraph::TypeChecker) } let(:api_map) { instance_double(Solargraph::ApiMap) } @@ -139,17 +104,7 @@ def bundle_exec(*cmd) end describe 'gems' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - context 'without mocked ApiMap' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'complains when gem does not exist' do output = capture_both do shell.gems('nonexistentgem') @@ -176,11 +131,6 @@ def bundle_exec(*cmd) end context 'with mocked Workspace' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:workspace) { instance_double(Solargraph::Workspace) } let(:gemspec) { instance_double(Gem::Specification, name: 'backport') } @@ -211,11 +161,6 @@ def bundle_exec(*cmd) end describe 'cache' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'caches a stdlib gem without erroring out' do expect { shell.cache('stringio') }.not_to raise_error end @@ -223,11 +168,6 @@ def bundle_exec(*cmd) context 'when gem does not exist' do subject(:call) { shell.cache('nonexistentgem8675309') } - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'gives a good error message' do # capture stderr output expect { call }.to output(/not found/).to_stderr @@ -247,11 +187,6 @@ def bundle_exec(*cmd) end describe 'pin on a class' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:api_map) { instance_double(Solargraph::ApiMap) } let(:string_pin) { instance_double(Solargraph::Pin::Namespace, name: 'String') } @@ -263,11 +198,6 @@ def bundle_exec(*cmd) end context 'with --references option' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:object_pin) { instance_double(Solargraph::Pin::Namespace, name: 'Object') } before do @@ -287,11 +217,6 @@ def bundle_exec(*cmd) end describe 'pin on a method' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - let(:api_map) { instance_double(Solargraph::ApiMap) } let(:to_s_pin) { instance_double(Solargraph::Pin::Method, return_type: Solargraph::ComplexType.parse('String')) } @@ -302,11 +227,6 @@ def bundle_exec(*cmd) end context 'with no options' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints a pin' do allow(to_s_pin).to receive(:inspect).and_return('pin inspect result') @@ -317,11 +237,6 @@ def bundle_exec(*cmd) end context 'with --rbs option' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints a pin with RBS type' do allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') @@ -334,11 +249,6 @@ def bundle_exec(*cmd) end context 'with --stack option' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints a pin using stack results' do allow(to_s_pin).to receive(:to_rbs).and_return('pin RBS result') @@ -366,11 +276,6 @@ def bundle_exec(*cmd) end context 'with --typify option' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints a pin with typify type' do allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) @@ -383,11 +288,6 @@ def bundle_exec(*cmd) end context 'with --typify --rbs options' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints a pin with typify type' do allow(to_s_pin).to receive(:typify).and_return(Solargraph::ComplexType.parse('::String')) @@ -400,11 +300,6 @@ def bundle_exec(*cmd) end context 'with no pin' do - before :context do - # avoid race conditions re-installing solargraph by letting parallel - # rspec know to run these serially in the same worker - end - it 'prints error' do allow(api_map).to receive(:get_path_pins).with('Not#found').and_return([]) allow(Solargraph::Pin::Method).to receive(:===).with(nil).and_return(false) diff --git a/spec/source/chain/array_spec.rb b/spec/source/chain/array_spec.rb index b8ef9db23..34eeafe01 100644 --- a/spec/source/chain/array_spec.rb +++ b/spec/source/chain/array_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::Array do - it "resolves an instance of an array" do + it 'resolves an instance of an array' do literal = described_class.new([], nil) pin = literal.resolve(nil, nil, nil).first expect(pin.return_type.tag).to eq('Array') diff --git a/spec/source/chain/call_spec.rb b/spec/source/chain/call_spec.rb index eaedfa998..d342e7b18 100644 --- a/spec/source/chain/call_spec.rb +++ b/spec/source/chain/call_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::Call do - it "recognizes core methods that return subtypes" do + it 'recognizes core methods that return subtypes' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( # @type [Array] @@ -12,7 +12,7 @@ expect(type.tag).to eq('String') end - it "recognizes core methods that return self" do + it 'recognizes core methods that return self' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( arr = [] @@ -24,7 +24,7 @@ expect(type.tag).to eq('Array') end - it "handles super calls to same method" do + it 'handles super calls to same method' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( class Foo @@ -44,7 +44,7 @@ def my_method expect(type.tag).to eq('Integer') end - it "infers return types based on yield call and @yieldreturn" do + it 'infers return types based on yield call and @yieldreturn' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( class Foo @@ -61,7 +61,7 @@ def my_method(&block) expect(type.tag).to eq('Integer') end - it "infers return types based only on yield call and @yieldreturn" do + it 'infers return types based only on yield call and @yieldreturn' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( class Foo @@ -78,7 +78,7 @@ def my_method(&block) expect(type.tag).to eq('Integer') end - it "adds virtual constructors for .new calls with conflicting return types" do + it 'adds virtual constructors for .new calls with conflicting return types' do api_map = Solargraph::ApiMap.new source = Solargraph::Source.load_string(%( class Foo @@ -88,13 +88,13 @@ def self.new; end )) api_map.map source chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(4, 11)) - type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map(nil).locals) + chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map(nil).locals) # @todo This test looks invalid now. If `Foo.new` is an empty method, # shouldn't it return `nil` or `undefined`? # expect(type.tag).to eq('Foo') end - it "infers types from macros" do + it 'infers types from macros' do source = Solargraph::Source.load_string(%( class Foo # @!macro @@ -576,7 +576,7 @@ def d expect(type.rooted_tags).not_to eq('::A::C') end - it 'qualifies types in a second Array#+ ' do + it 'qualifies types in a second Array#+' do source = Solargraph::Source.load_string(%( module A1 class B1 diff --git a/spec/source/chain/class_variable_spec.rb b/spec/source/chain/class_variable_spec.rb index 4121e9948..65fcd0e5d 100644 --- a/spec/source/chain/class_variable_spec.rb +++ b/spec/source/chain/class_variable_spec.rb @@ -1,8 +1,8 @@ describe Solargraph::Source::Chain::ClassVariable do - it "resolves class variable pins" do + it 'resolves class variable pins' do foo_pin = Solargraph::Pin::ClassVariable.new(name: '@@foo') bar_pin = Solargraph::Pin::ClassVariable.new(name: '@@bar') - api_map = double(Solargraph::ApiMap, :get_class_variable_pins => [foo_pin, bar_pin]) + api_map = double(Solargraph::ApiMap, get_class_variable_pins: [foo_pin, bar_pin]) link = Solargraph::Source::Chain::ClassVariable.new('@@bar') pins = link.resolve(api_map, Solargraph::Pin::ROOT_PIN, []) expect(pins.length).to eq(1) diff --git a/spec/source/chain/constant_spec.rb b/spec/source/chain/constant_spec.rb index 4376650b3..f809783ef 100644 --- a/spec/source/chain/constant_spec.rb +++ b/spec/source/chain/constant_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::Constant do - it "resolves constants in the current context" do + it 'resolves constants in the current context' do foo_pin = Solargraph::Pin::Constant.new(name: 'Foo', closure: Solargraph::Pin::ROOT_PIN) api_map = Solargraph::ApiMap.new api_map.index [foo_pin] diff --git a/spec/source/chain/global_variable_spec.rb b/spec/source/chain/global_variable_spec.rb index 4c5bd6bce..4d0ed4056 100644 --- a/spec/source/chain/global_variable_spec.rb +++ b/spec/source/chain/global_variable_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::GlobalVariable do - it "resolves instance variable pins" do + it 'resolves instance variable pins' do closure = Solargraph::Pin::Namespace.new(name: 'Foo') foo_pin = Solargraph::Pin::GlobalVariable.new(closure: closure, name: '$foo') not_pin = Solargraph::Pin::InstanceVariable.new(closure: closure, name: '@bar') diff --git a/spec/source/chain/head_spec.rb b/spec/source/chain/head_spec.rb index cc63a54a3..7d4e5d9ea 100644 --- a/spec/source/chain/head_spec.rb +++ b/spec/source/chain/head_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::Head do - it "returns self pins" do + it 'returns self pins' do head = Solargraph::Source::Chain::Head.new('self') npin = Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.parse('Foo')) ipin = head.resolve(nil, npin, []).first diff --git a/spec/source/chain/instance_variable_spec.rb b/spec/source/chain/instance_variable_spec.rb index ee4604f91..fd956f770 100644 --- a/spec/source/chain/instance_variable_spec.rb +++ b/spec/source/chain/instance_variable_spec.rb @@ -1,7 +1,8 @@ describe Solargraph::Source::Chain::InstanceVariable do - it "resolves instance variable pins" do + it 'resolves instance variable pins' do closure = Solargraph::Pin::Namespace.new(name: 'Foo', - location: Solargraph::Location.new('test.rb', Solargraph::Range.from_to(1, 1, 9, 0)), + location: Solargraph::Location.new('test.rb', + Solargraph::Range.from_to(1, 1, 9, 0)), source: :closure) methpin = Solargraph::Pin::Method.new(closure: closure, name: 'imeth', scope: :instance, location: Solargraph::Location.new('test.rb', Solargraph::Range.from_to(1, 1, 3, 0)), @@ -17,7 +18,8 @@ api_map = Solargraph::ApiMap.new api_map.index [closure, methpin, foo_pin, bar_pin] - link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, Solargraph::Location.new('test.rb', Solargraph::Range.from_to(2, 2, 2, 3))) + link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, + Solargraph::Location.new('test.rb', Solargraph::Range.from_to(2, 2, 2, 3))) pins = link.resolve(api_map, methpin, []) expect(pins.length).to eq(1) @@ -27,7 +29,8 @@ name_pin = Solargraph::Pin::ProxyType.anonymous(closure.binder, # Closure is the class closure: closure) - link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 1, 5, 2))) + link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, + Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 1, 5, 2))) pins = link.resolve(api_map, name_pin, []) expect(pins.length).to eq(1) expect(pins.first.name).to eq('@foo') diff --git a/spec/source/chain/link_spec.rb b/spec/source/chain/link_spec.rb index 39143dbbd..c492d0ba3 100644 --- a/spec/source/chain/link_spec.rb +++ b/spec/source/chain/link_spec.rb @@ -1,26 +1,26 @@ describe Solargraph::Source::Chain::Link do - it "is undefined by default" do + it 'is undefined by default' do link = described_class.new expect(link).to be_undefined end - it "is not a constant by default" do + it 'is not a constant by default' do link = described_class.new expect(link).not_to be_constant end - it "resolves empty arrays by default" do + it 'resolves empty arrays by default' do link = described_class.new expect(link.resolve(nil, nil, nil)).to be_empty end - it "recognizes equivalent links" do + it 'recognizes equivalent links' do l1 = described_class.new('foo') l2 = described_class.new('foo') expect(l1).to eq(l2) end - it "recognizes inequivalent links" do + it 'recognizes inequivalent links' do l1 = described_class.new('foo') l2 = described_class.new('bar') expect(l1).not_to eq(l2) diff --git a/spec/source/chain/literal_spec.rb b/spec/source/chain/literal_spec.rb index a1431ae07..d3f759ea0 100644 --- a/spec/source/chain/literal_spec.rb +++ b/spec/source/chain/literal_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::Literal do - it "resolves an instance of a literal" do + it 'resolves an instance of a literal' do literal = described_class.new('String', nil) api_map = Solargraph::ApiMap.new pin = literal.resolve(api_map, nil, nil).first diff --git a/spec/source/chain/z_super_spec.rb b/spec/source/chain/z_super_spec.rb index aa412dda6..4739ee195 100644 --- a/spec/source/chain/z_super_spec.rb +++ b/spec/source/chain/z_super_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Chain::ZSuper do - it "resolves super" do + it 'resolves super' do head = Solargraph::Source::Chain::ZSuper.new('super') npin = Solargraph::Pin::Namespace.new(name: 'Substring') scpin = Solargraph::Pin::Reference::Superclass.new(closure: npin, name: 'String') diff --git a/spec/source/chain_spec.rb b/spec/source/chain_spec.rb index 4cccd285c..c784a430e 100644 --- a/spec/source/chain_spec.rb +++ b/spec/source/chain_spec.rb @@ -1,25 +1,25 @@ describe Solargraph::Source::Chain do - it "gets empty definitions for undefined links" do + it 'gets empty definitions for undefined links' do chain = described_class.new([Solargraph::Source::Chain::Link.new]) expect(chain.define(nil, nil, [])).to be_empty end - it "infers undefined types for undefined links" do + it 'infers undefined types for undefined links' do chain = described_class.new([Solargraph::Source::Chain::Link.new]) expect(chain.infer(nil, nil, [])).to be_undefined end - it "calls itself undefined if any of its links are undefined" do + it 'calls itself undefined if any of its links are undefined' do chain = described_class.new([Solargraph::Source::Chain::Link.new]) expect(chain).to be_undefined end - it "returns undefined bases for single links" do + it 'returns undefined bases for single links' do chain = described_class.new([Solargraph::Source::Chain::Link.new]) expect(chain.base).to be_undefined end - it "defines constants from core classes" do + it 'defines constants from core classes' do api_map = Solargraph::ApiMap.new chain = described_class.new([Solargraph::Source::Chain::Constant.new('String')]) pins = chain.define(api_map, Solargraph::Pin::ROOT_PIN, []) @@ -27,7 +27,7 @@ expect(pins.first.path).to eq('String') end - it "infers types from core classes" do + it 'infers types from core classes' do api_map = Solargraph::ApiMap.new chain = described_class.new([Solargraph::Source::Chain::Constant.new('String')]) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, []) @@ -35,25 +35,26 @@ expect(type.scope).to eq(:class) end - it "infers types from core methods" do + it 'infers types from core methods' do api_map = Solargraph::ApiMap.new - chain = described_class.new([Solargraph::Source::Chain::Constant.new('String'), Solargraph::Source::Chain::Call.new('new', nil)]) + chain = described_class.new([Solargraph::Source::Chain::Constant.new('String'), + Solargraph::Source::Chain::Call.new('new', nil)]) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, []) expect(type.namespace).to eq('String') expect(type.scope).to eq(:instance) end - it "recognizes literals" do + it 'recognizes literals' do chain = described_class.new([Solargraph::Source::Chain::Literal.new('String', nil)]) expect(chain.literal?).to be(true) end - it "recognizes constants" do + it 'recognizes constants' do chain = described_class.new([Solargraph::Source::Chain::Constant.new('String')]) expect(chain.constant?).to be(true) end - it "recognizes unfinished constants" do + it 'recognizes unfinished constants' do chain = described_class.new([Solargraph::Source::Chain::Constant.new('String'), Solargraph::Source::Chain::Constant.new('')]) expect(chain.constant?).to be(true) expect(chain.base.constant?).to be(true) @@ -61,7 +62,7 @@ expect(chain.base.undefined?).to be(false) end - it "infers types from new subclass calls without a subclass initialize method" do + it 'infers types from new subclass calls without a subclass initialize method' do code = %( class Sup def initialize; end @@ -80,7 +81,7 @@ def meth; end expect(type.name).to eq('Sub') end - it "follows constant chains" do + it 'follows constant chains' do source = Solargraph::Source.load_string(%( module Mixin; end module Container @@ -95,7 +96,7 @@ class Foo; end expect(pins).to be_empty end - it "rebases inner constants chains" do + it 'rebases inner constants chains' do source = Solargraph::Source.load_string(%( class Foo class Bar; end @@ -105,11 +106,12 @@ class Bar; end api_map = Solargraph::ApiMap.new api_map.map source chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(3, 16)) - pins = chain.define(api_map, Solargraph::Pin::ProxyType.new(closure: Solargraph::Pin::Namespace.new(name: 'Foo'), return_type: Solargraph::ComplexType.parse('Class')), []) + pins = chain.define(api_map, + Solargraph::Pin::ProxyType.new(closure: Solargraph::Pin::Namespace.new(name: 'Foo'), return_type: Solargraph::ComplexType.parse('Class')), []) expect(pins.first.path).to eq('Foo::Bar') end - it "resolves relative constant paths" do + it 'resolves relative constant paths' do source = Solargraph::Source.load_string(%( class Foo class Bar @@ -123,11 +125,12 @@ module Other api_map = Solargraph::ApiMap.new api_map.map source chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(6, 16)) - pins = chain.define(api_map, Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.parse('Class')), []) + pins = chain.define(api_map, + Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.parse('Class')), []) expect(pins.first.path).to eq('Foo::Bar::Baz') end - it "avoids recursive variable assignments" do + it 'avoids recursive variable assignments' do source = Solargraph::Source.load_string(%( @foo = @bar @bar = @foo.quz @@ -135,12 +138,12 @@ module Other api_map = Solargraph::ApiMap.new api_map.map source chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(2, 18)) - expect { + expect do chain.define(api_map, Solargraph::Pin::ROOT_PIN, []) - }.not_to raise_error + end.not_to raise_error end - it "pulls types from multiple lines of code" do + it 'pulls types from multiple lines of code' do source = Solargraph::Source.load_string(%( 123 'abc' @@ -152,7 +155,7 @@ module Other expect(type.simple_tags).to eq('String') end - it "uses last line of a begin expression as return type" do + it 'uses last line of a begin expression as return type' do source = Solargraph::Source.load_string(%( begin 123 @@ -166,7 +169,7 @@ module Other expect(type.simple_tags).to eq('String') end - it "matches constants on complete symbols" do + it 'matches constants on complete symbols' do source = Solargraph::Source.load_string(%( class Correct; end class NotCorrect; end diff --git a/spec/source/change_spec.rb b/spec/source/change_spec.rb index 247c1e204..7ef02984a 100644 --- a/spec/source/change_spec.rb +++ b/spec/source/change_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Change do - it "inserts a character" do + it 'inserts a character' do text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '.' @@ -8,7 +8,7 @@ expect(updated).to eq('var.') end - it "repairs nullable characters" do + it 'repairs nullable characters' do text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '.' @@ -17,7 +17,7 @@ expect(updated).to eq('var ') end - it "repairs entire changes" do + it 'repairs entire changes' do text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '._(!' @@ -26,14 +26,14 @@ expect(updated).to eq('var ') end - it "repairs nil ranges" do + it 'repairs nil ranges' do text = 'original' change = Solargraph::Source::Change.new(nil, '...') updated = change.repair(text) expect(updated).to eq(' ') end - it "overwrites nil ranges" do + it 'overwrites nil ranges' do text = 'foo' new_text = 'bar' change = Solargraph::Source::Change.new(nil, new_text) @@ -41,7 +41,7 @@ expect(updated).to eq('bar') end - it "blanks single colons in nullable changes" do + it 'blanks single colons in nullable changes' do text = 'bar' new_text = ':' range = Solargraph::Range.from_to(0, 3, 0, 3) @@ -50,7 +50,7 @@ expect(updated).to eq('bar ') end - it "blanks double colons in nullable changes" do + it 'blanks double colons in nullable changes' do text = 'bar:' new_text = ':' range = Solargraph::Range.from_to(0, 4, 0, 4) @@ -59,7 +59,7 @@ expect(updated).to eq('bar ') end - it "repairs preceding periods" do + it 'repairs preceding periods' do text = 'bar.' new_text = ' ' range = Solargraph::Range.from_to(0, 4, 0, 4) @@ -68,7 +68,7 @@ expect(updated).to eq('bar ') end - it "repairs preceding colons" do + it 'repairs preceding colons' do text = 'bar:' new_text = 'x' range = Solargraph::Range.from_to(0, 4, 0, 4) diff --git a/spec/source/cursor_spec.rb b/spec/source/cursor_spec.rb index 150e99449..f221232cf 100644 --- a/spec/source/cursor_spec.rb +++ b/spec/source/cursor_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Cursor do - it "detects cursors in strings" do + it 'detects cursors in strings' do source = Solargraph::Source.load_string('str = "string"') cursor = described_class.new(source, Solargraph::Position.new(0, 6)) expect(cursor).not_to be_string @@ -7,7 +7,7 @@ expect(cursor).to be_string end - it "detects cursors in comments" do + it 'detects cursors in comments' do source = Solargraph::Source.load_string(%( # @type [String] var = make_a_string @@ -20,48 +20,48 @@ expect(cursor).not_to be_comment end - it "detects arguments inside parentheses" do + it 'detects arguments inside parentheses' do source = Solargraph::Source.load_string('a(1); b') - cur = described_class.new(source, Solargraph::Position.new(0,2)) + cur = described_class.new(source, Solargraph::Position.new(0, 2)) expect(cur).to be_argument - cur = described_class.new(source, Solargraph::Position.new(0,3)) + cur = described_class.new(source, Solargraph::Position.new(0, 3)) expect(cur).to be_argument - cur = described_class.new(source, Solargraph::Position.new(0,4)) + cur = described_class.new(source, Solargraph::Position.new(0, 4)) expect(cur).not_to be_argument - cur = described_class.new(source, Solargraph::Position.new(0,5)) + cur = described_class.new(source, Solargraph::Position.new(0, 5)) expect(cur).not_to be_argument - cur = described_class.new(source, Solargraph::Position.new(0,7)) + cur = described_class.new(source, Solargraph::Position.new(0, 7)) expect(cur).not_to be_argument end it 'detects arguments at opening parentheses' do source = Solargraph::Source.load_string('String.new', 'test.rb') - change = Solargraph::Source::Change.new(Solargraph::Range.from_to(0, 10, 0, 10) ,'(') + change = Solargraph::Source::Change.new(Solargraph::Range.from_to(0, 10, 0, 10), '(') updater = Solargraph::Source::Updater.new('test.rb', 1, [change]) source = source.synchronize(updater) cursor = source.cursor_at([0, 11]) expect(cursor).to be_argument end - it "detects class variables" do - source = double(:Source, :code => '@@foo') + it 'detects class variables' do + source = double(:Source, code: '@@foo') cur = described_class.new(source, Solargraph::Position.new(0, 2)) expect(cur.word).to eq('@@foo') end - it "detects instance variables" do - source = double(:Source, :code => '@foo') + it 'detects instance variables' do + source = double(:Source, code: '@foo') cur = described_class.new(source, Solargraph::Position.new(0, 1)) expect(cur.word).to eq('@foo') end - it "detects global variables" do - source = double(:Source, :code => '@foo') + it 'detects global variables' do + source = double(:Source, code: '@foo') cur = described_class.new(source, Solargraph::Position.new(0, 1)) expect(cur.word).to eq('@foo') end - it "generates word ranges" do + it 'generates word ranges' do source = Solargraph::Source.load_string(%( foo = bar )) @@ -69,26 +69,26 @@ expect(source.at(cur.range)).to eq('bar') end - it "generates chains" do + it 'generates chains' do source = Solargraph::Source.load_string('foo.bar(1,2).baz{}') cur = described_class.new(source, Solargraph::Position.new(0, 18)) expect(cur.chain).to be_a(Solargraph::Source::Chain) - expect(cur.chain.links.map(&:word)).to eq(['foo', 'bar', 'baz']) + expect(cur.chain.links.map(&:word)).to eq(%w[foo bar baz]) end - it "detects constant words" do - source = double(:Source, :code => 'Foo::Bar') + it 'detects constant words' do + source = double(:Source, code: 'Foo::Bar') cur = described_class.new(source, Solargraph::Position.new(0, 5)) expect(cur.word).to eq('Bar') end - it "detects cursors in dynamic strings" do + it 'detects cursors in dynamic strings' do source = Solargraph::Source.load_string('"#{100}"') cursor = source.cursor_at(Solargraph::Position.new(0, 7)) expect(cursor).to be_string end - it "detects cursors in embedded strings" do + it 'detects cursors in embedded strings' do source = Solargraph::Source.load_string('"#{100}..."') cursor = source.cursor_at(Solargraph::Position.new(0, 10)) expect(cursor).to be_string @@ -118,8 +118,8 @@ class Foo; end "#{[]}" ', 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, [ - Solargraph::Source::Change.new(Solargraph::Range.from_to(1, 12, 1, 12), '.') - ]) + Solargraph::Source::Change.new(Solargraph::Range.from_to(1, 12, 1, 12), '.') + ]) updated = source.synchronize(updater) cursor = updated.cursor_at(Solargraph::Position.new(1, 13)) expect(cursor).to be_string diff --git a/spec/source/source_chainer_spec.rb b/spec/source/source_chainer_spec.rb index 7a8eb9fb8..ae672b095 100644 --- a/spec/source/source_chainer_spec.rb +++ b/spec/source/source_chainer_spec.rb @@ -1,12 +1,12 @@ describe Solargraph::Source::SourceChainer do - it "handles trailing colons that are not namespace separators" do + it 'handles trailing colons that are not namespace separators' do source = Solargraph::Source.load_string('Foo:') map = Solargraph::SourceMap.map(source) cursor = map.cursor_at(Solargraph::Position.new(0, 4)) expect(cursor.chain.links.first).to be_undefined end - it "recognizes literal strings" do + it 'recognizes literal strings' do map = Solargraph::SourceMap.load_string("'string'") cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain).not_to be_a(Solargraph::Source::Chain::Literal) @@ -15,8 +15,8 @@ expect(cursor.chain.links.first.word).to eq('<::String>') end - it "recognizes literal integers" do - map = Solargraph::SourceMap.load_string("100") + it 'recognizes literal integers' do + map = Solargraph::SourceMap.load_string('100') cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain).not_to be_a(Solargraph::Source::Chain::Literal) cursor = map.cursor_at(Solargraph::Position.new(0, 1)) @@ -24,42 +24,42 @@ expect(cursor.chain.links.first.word).to eq('<::Integer>') end - it "recognizes literal regexps" do - map = Solargraph::SourceMap.load_string("/[a-z]/") + it 'recognizes literal regexps' do + map = Solargraph::SourceMap.load_string('/[a-z]/') cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain.links.first).to be_a(Solargraph::Source::Chain::Literal) expect(cursor.chain.links.first.word).to eq('<::Regexp>') end - it "recognizes class variables" do + it 'recognizes class variables' do map = Solargraph::SourceMap.load_string('@@foo') cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain.links.first).to be_a(Solargraph::Source::Chain::ClassVariable) expect(cursor.chain.links.first.word).to eq('@@foo') end - it "recognizes instance variables" do + it 'recognizes instance variables' do map = Solargraph::SourceMap.load_string('@foo') cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain.links.first).to be_a(Solargraph::Source::Chain::InstanceVariable) expect(cursor.chain.links.first.word).to eq('@foo') end - it "recognizes global variables" do + it 'recognizes global variables' do map = Solargraph::SourceMap.load_string('$foo') cursor = map.cursor_at(Solargraph::Position.new(0, 0)) expect(cursor.chain.links.first).to be_a(Solargraph::Source::Chain::GlobalVariable) expect(cursor.chain.links.first.word).to eq('$foo') end - it "recognizes constants" do + it 'recognizes constants' do map = Solargraph::SourceMap.load_string('Foo::Bar') cursor = map.cursor_at(Solargraph::Position.new(0, 6)) expect(cursor.chain).to be_constant expect(cursor.chain.links.map(&:word)).to eq(['Foo::Bar']) end - it "recognizes unfinished constants" do + it 'recognizes unfinished constants' do map = Solargraph::SourceMap.load_string('Foo:: $something') cursor = map.cursor_at(Solargraph::Position.new(0, 5)) expect(cursor.chain).to be_constant @@ -67,11 +67,11 @@ expect(cursor.chain).to be_undefined end - it "recognizes unfinished calls" do + it 'recognizes unfinished calls' do orig = Solargraph::Source.load_string('foo.bar') updater = Solargraph::Source::Updater.new(nil, 1, [ - Solargraph::Source::Change.new(Solargraph::Range.from_to(0, 7, 0, 7), '.') - ]) + Solargraph::Source::Change.new(Solargraph::Range.from_to(0, 7, 0, 7), '.') + ]) source = orig.synchronize(updater) map = Solargraph::SourceMap.map(source) cursor = map.cursor_at(Solargraph::Position.new(0, 8)) @@ -80,25 +80,25 @@ expect(cursor.chain).to be_undefined end - it "chains signatures with square brackets" do + it 'chains signatures with square brackets' do map = Solargraph::SourceMap.load_string('foo[0].bar') cursor = map.cursor_at(Solargraph::Position.new(0, 8)) expect(cursor.chain.links.map(&:word)).to eq(['foo', '[]', 'bar']) end - it "chains signatures with curly brackets" do + it 'chains signatures with curly brackets' do map = Solargraph::SourceMap.load_string('foo{|x| x == y}.bar') cursor = map.cursor_at(Solargraph::Position.new(0, 16)) - expect(cursor.chain.links.map(&:word)).to eq(['foo', 'bar']) + expect(cursor.chain.links.map(&:word)).to eq(%w[foo bar]) end - it "chains signatures with parentheses" do + it 'chains signatures with parentheses' do map = Solargraph::SourceMap.load_string('foo(x, y).bar') cursor = map.cursor_at(Solargraph::Position.new(0, 10)) - expect(cursor.chain.links.map(&:word)).to eq(['foo', 'bar']) + expect(cursor.chain.links.map(&:word)).to eq(%w[foo bar]) end - it "chains from repaired sources with literal strings" do + it 'chains from repaired sources with literal strings' do orig = Solargraph::Source.load_string("''") updater = Solargraph::Source::Updater.new( nil, @@ -116,8 +116,8 @@ expect(chain.links.length).to eq(2) end - it "chains incomplete constants" do - source = Solargraph::Source.load_string("Foo::") + it 'chains incomplete constants' do + source = Solargraph::Source.load_string('Foo::') chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 5)) expect(chain.links.length).to eq(2) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Constant) @@ -125,18 +125,18 @@ expect(chain.links.last).to be_undefined end - it "works when source error ranges contain a nil range" do + it 'works when source error ranges contain a nil range' do orig = Solargraph::Source.load_string("msg = 'msg'\nmsg", 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, [ - Solargraph::Source::Change.new(nil, "msg = 'msg'\nmsg.") - ]) + Solargraph::Source::Change.new(nil, "msg = 'msg'\nmsg.") + ]) source = orig.synchronize(updater) - expect { + expect do Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 4)) - }.not_to raise_error + end.not_to raise_error end - it "stops phrases at opening brackets" do + it 'stops phrases at opening brackets' do source = Solargraph::Source.load_string(%( (aa1, 2, 3) [bb2, 2, 3] @@ -150,63 +150,61 @@ expect(chain.links.first.word).to eq('cc3') end - it "chains instance variables from unsynchronized sources" do + it 'chains instance variables from unsynchronized sources' do source = double(Solargraph::Source, - :synchronized? => false, - :code => '@foo.', - :filename => 'test.rb', - :string_at? => false, - :comment_at? => false, - :repaired? => false, - :parsed? => true, - :error_ranges => [], - :node_at => nil, - :tree_at => [] - ) + synchronized?: false, + code: '@foo.', + filename: 'test.rb', + string_at?: false, + comment_at?: false, + repaired?: false, + parsed?: true, + error_ranges: [], + node_at: nil, + tree_at: []) chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 5)) expect(chain.links.first.word).to eq('@foo') expect(chain.links.last.word).to eq('') end - it "chains class variables from unsynchronized sources" do + it 'chains class variables from unsynchronized sources' do source = double(Solargraph::Source, - :synchronized? => false, - :code => '@@foo.', - :filename => 'test.rb', - :string_at? => false, - :comment_at? => false, - :repaired? => false, - :parsed? => true, - :error_ranges => [], - :node_at => nil, - :tree_at => [] - ) + synchronized?: false, + code: '@@foo.', + filename: 'test.rb', + string_at?: false, + comment_at?: false, + repaired?: false, + parsed?: true, + error_ranges: [], + node_at: nil, + tree_at: []) chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 6)) expect(chain.links.first.word).to eq('@@foo') expect(chain.links.last.word).to eq('') end - it "detects literals from chains in unsynchronized sources" do + it 'detects literals from chains in unsynchronized sources' do source1 = Solargraph::Source.load_string(%( '' )) source2 = source1.synchronize(Solargraph::Source::Updater.new( - nil, - 2, - [ - Solargraph::Source::Change.new( - Solargraph::Range.from_to(1, 8, 1, 8), - '.' - ) - ] - )) + nil, + 2, + [ + Solargraph::Source::Change.new( + Solargraph::Range.from_to(1, 8, 1, 8), + '.' + ) + ] + )) chain = Solargraph::Source::SourceChainer.chain(source2, Solargraph::Position.new(1, 9)) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Literal) expect(chain.links.first.word).to eq('<::String>') expect(chain.links.last.word).to eq('') end - it "ignores ? and ! that are not method suffixes" do + it 'ignores ? and ! that are not method suffixes' do source = Solargraph::Source.load_string(%( if !t ), 'test.rb') @@ -215,14 +213,14 @@ expect(chain.links.first.word).to eq('t') end - it "chains from fixed phrases in repaired sources with missing nodes" do + it 'chains from fixed phrases in repaired sources with missing nodes' do source = Solargraph::Source.load_string(%( x = [] ), 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, [ - Solargraph::Source::Change.new(Solargraph::Range.from_to(2, 6, 2, 6), 'x.') - ]) + Solargraph::Source::Change.new(Solargraph::Range.from_to(2, 6, 2, 6), 'x.') + ]) updated = source.synchronize(updater) cursor = updated.cursor_at(Solargraph::Position.new(2, 8)) expect(cursor.chain.links.first.word).to eq('x') @@ -331,8 +329,8 @@ def strings; end end ), 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, [ - Solargraph::Source::Change.new(Solargraph::Range.from_to(5, 10, 5, 10), 'if s') - ]) + Solargraph::Source::Change.new(Solargraph::Range.from_to(5, 10, 5, 10), 'if s') + ]) updated = source.synchronize(updater) api_map = Solargraph::ApiMap.new api_map.map updated diff --git a/spec/source/updater_spec.rb b/spec/source/updater_spec.rb index 0c5f1f4c6..580d41521 100644 --- a/spec/source/updater_spec.rb +++ b/spec/source/updater_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source::Updater do - it "applies changes" do + it 'applies changes' do text = 'foo' changes = [] range = Solargraph::Range.from_to(0, 3, 0, 3) @@ -13,7 +13,7 @@ expect(updated).to eq('foo.bar') end - it "applies repairs" do + it 'applies repairs' do text = 'foo' changes = [] range = Solargraph::Range.from_to(0, 3, 0, 3) @@ -27,7 +27,7 @@ expect(updated).to eq('foo ') end - it "handles nil ranges" do + it 'handles nil ranges' do text = 'foo' changes = [] range = nil diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 1afa3f12c..71f5369ff 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -93,7 +93,7 @@ class Bar api_map = Solargraph::ApiMap.new api_map.map source clip = api_map.clip_at('test.rb', [6, 12]) - expect(clip.complete.pins.map(&:name)).to eq (['@foo']) + expect(clip.complete.pins.map(&:name)).to eq(['@foo']) end it 'completes instance variables' do @@ -667,7 +667,7 @@ def initialize clip = api_map.clip_at('test.rb', [7, 8]) # @todo expect(clip.infer.tags).to eq('""') expect(clip.infer.tags).to eq('String') - expect(clip.infer.simple_tags).to eq("String") + expect(clip.infer.simple_tags).to eq('String') end it 'completes instance variable methods in rebound blocks' do @@ -1660,7 +1660,7 @@ def foo; end api_map = Solargraph::ApiMap.new.map(source) array_names = api_map.clip_at('test.rb', [5, 22]).complete.pins.map(&:name) - expect(array_names).to eq(["byteindex", "byterindex", "bytes", "bytesize", "byteslice", "bytesplice"]) + expect(array_names).to eq(%w[byteindex byterindex bytes bytesize byteslice bytesplice]) string_names = api_map.clip_at('test.rb', [6, 22]).complete.pins.map(&:name) # can be brought in by solargraph-rails @@ -2753,7 +2753,7 @@ def bar; end api_map = Solargraph::ApiMap.new.map(source) clip = api_map.clip_at('test.rb', [7, 6]) # The order of the types can vary between platforms - expect(clip.infer.items.map(&:to_s).sort).to eq(["123", ":foo", "String"]) + expect(clip.infer.items.map(&:to_s).sort).to eq(['123', ':foo', 'String']) end it 'does not map Module methods into an Object' do diff --git a/spec/source_map/mapper_spec.rb b/spec/source_map/mapper_spec.rb index 96d2bdab5..f6cc7279e 100644 --- a/spec/source_map/mapper_spec.rb +++ b/spec/source_map/mapper_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::SourceMap::Mapper do - it "ignores include calls that are not attached to the current namespace" do + it 'ignores include calls that are not attached to the current namespace' do source = Solargraph::Source.new(%( class Foo include Direct @@ -8,14 +8,14 @@ class Foo end )) map = Solargraph::SourceMap.map(source) - pins = map.pins.select{|pin| pin.is_a?(Solargraph::Pin::Reference::Include) && pin.namespace == 'Foo'} + pins = map.pins.select { |pin| pin.is_a?(Solargraph::Pin::Reference::Include) && pin.namespace == 'Foo' } names = pins.map(&:name) expect(names).to include('Direct') expect(names).not_to include('Indirect') expect(names).to include('Interior') end - it "ignores prepend calls that are not attached to the current namespace" do + it 'ignores prepend calls that are not attached to the current namespace' do source = Solargraph::Source.new(%( class Foo prepend Direct @@ -24,14 +24,14 @@ class Foo end )) map = Solargraph::SourceMap.map(source) - pins = map.pins.select{|pin| pin.is_a?(Solargraph::Pin::Reference::Prepend) && pin.namespace == 'Foo'} + pins = map.pins.select { |pin| pin.is_a?(Solargraph::Pin::Reference::Prepend) && pin.namespace == 'Foo' } names = pins.map(&:name) expect(names).to include('Direct') expect(names).not_to include('Indirect') expect(names).to include('Interior') end - it "ignores extend calls that are not attached to the current namespace" do + it 'ignores extend calls that are not attached to the current namespace' do source = Solargraph::Source.new(%( class Foo extend Direct @@ -40,17 +40,17 @@ class Foo end )) map = Solargraph::SourceMap.map(source) - foo_pin = map.pins.select{|pin| pin.path == 'Foo'}.first + map.pins.select { |pin| pin.path == 'Foo' }.first # expect(foo_pin.extend_references.map(&:name)).to include('Direct') # expect(foo_pin.extend_references.map(&:name)).not_to include('Indirect') - pins = map.pins.select{|pin| pin.is_a?(Solargraph::Pin::Reference::Extend) && pin.namespace == 'Foo'} + pins = map.pins.select { |pin| pin.is_a?(Solargraph::Pin::Reference::Extend) && pin.namespace == 'Foo' } names = pins.map(&:name) expect(names).to include('Direct') expect(names).not_to include('Indirect') expect(names).to include('Interior') end - it "sets scopes for attributes" do + it 'sets scopes for attributes' do source = Solargraph::Source.new(%( module Foo attr_reader :bar1 @@ -60,13 +60,13 @@ class << self end )) map = Solargraph::SourceMap.map(source) - bar1 = map.pins.select{|pin| pin.name == 'bar1'}.first + bar1 = map.pins.select { |pin| pin.name == 'bar1' }.first expect(bar1.scope).to eq(:instance) - bar2 = map.pins.select{|pin| pin.name == 'bar2'}.first + bar2 = map.pins.select { |pin| pin.name == 'bar2' }.first expect(bar2.scope).to eq(:class) end - it "sets attribute visibility" do + it 'sets attribute visibility' do map = Solargraph::SourceMap.load_string(%( module Foo attr_reader :default_public_method @@ -84,7 +84,7 @@ module Foo expect(map.first_pin('Foo#explicit_public_method').visibility).to eq(:public) end - it "processes method directives" do + it 'processes method directives' do map = Solargraph::SourceMap.load_string(%( class Foo # @!method bar(baz) @@ -119,7 +119,7 @@ class Foo expect(pin.scope).to eq(:class) end - it "processes attribute reader directives" do + it 'processes attribute reader directives' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute [r] bar @@ -131,7 +131,7 @@ class Foo expect(pin.return_type.tag).to eq('String') end - it "processes attribute writer directives" do + it 'processes attribute writer directives' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute [w] bar @@ -143,7 +143,7 @@ class Foo expect(pin.return_type.tag).to eq('String') end - it "processes attribute accessor directives" do + it 'processes attribute accessor directives' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute [r,w] bar @@ -157,7 +157,7 @@ class Foo expect(pin.return_type.tag).to eq('String') end - it "processes default attribute directives" do + it 'processes default attribute directives' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute bar @@ -171,7 +171,7 @@ class Foo expect(pin.return_type.tag).to eq('String') end - it "processes attribute directives attached to methods" do + it 'processes attribute directives attached to methods' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute [r] bar @@ -184,7 +184,7 @@ def make_bar_attr expect(pin.return_type.tag).to eq('String') end - it "processes private visibility directives attached to methods" do + it 'processes private visibility directives attached to methods' do map = Solargraph::SourceMap.load_string(%( class Foo # @!visibility private @@ -195,7 +195,7 @@ def bar expect(map.first_pin('Foo#bar').visibility).to be(:private) end - it "processes protected visibility directives attached to methods" do + it 'processes protected visibility directives attached to methods' do map = Solargraph::SourceMap.load_string(%( class Foo # @!visibility protected @@ -206,7 +206,7 @@ def bar expect(map.first_pin('Foo#bar').visibility).to be(:protected) end - it "processes public visibility directives attached to methods" do + it 'processes public visibility directives attached to methods' do map = Solargraph::SourceMap.load_string(%( class Foo # @!visibility public @@ -217,7 +217,7 @@ def bar expect(map.first_pin('Foo#bar').visibility).to be(:public) end - it "does not process attached visibility directives on other methods" do + it 'does not process attached visibility directives on other methods' do map = Solargraph::SourceMap.load_string(%( class Example # @!visibility private @@ -232,7 +232,7 @@ def method2; end expect(method2.visibility).to be(:public) end - it "processes class-wide private visibility directives" do + it 'processes class-wide private visibility directives' do map = Solargraph::SourceMap.load_string(%( class Example # @!visibility private @@ -253,7 +253,7 @@ def method3; end expect(method3.visibility).to be(:public) end - it "processes attribute directives at class endings" do + it 'processes attribute directives at class endings' do map = Solargraph::SourceMap.load_string(%( class Foo # @!attribute [r] bar @@ -264,43 +264,43 @@ class Foo expect(pin.return_type.tag).to eq('String') end - it "finds assignment nodes for local variables using nil guards" do + it 'finds assignment nodes for local variables using nil guards' do map = Solargraph::SourceMap.load_string(%( x ||= [] )) pin = map.locals.first # @todo Dirty test - expect([:ZLIST, :ZARRAY, :array]).to include(pin.assignment.type) + expect(%i[ZLIST ZARRAY array]).to include(pin.assignment.type) end - it "finds assignment nodes for instance variables using nil guards" do + it 'finds assignment nodes for instance variables using nil guards' do map = Solargraph::SourceMap.load_string(%( @x ||= [] )) pin = map.pins.last # @todo Dirty test - expect([:ZLIST, :ZARRAY, :array]).to include(pin.assignment.type) + expect(%i[ZLIST ZARRAY array]).to include(pin.assignment.type) end - it "finds assignment nodes for class variables using nil guards" do + it 'finds assignment nodes for class variables using nil guards' do map = Solargraph::SourceMap.load_string(%( @@x ||= [] )) pin = map.pins.last # @todo Dirty test - expect([:ZLIST, :ZARRAY, :array]).to include(pin.assignment.type) + expect(%i[ZLIST ZARRAY array]).to include(pin.assignment.type) end - it "finds assignment nodes for global variables using nil guards" do + it 'finds assignment nodes for global variables using nil guards' do map = Solargraph::SourceMap.load_string(%( $x ||= [] )) pin = map.pins.last # @todo Dirty test - expect([:ZLIST, :ZARRAY, :array]).to include(pin.assignment.type) + expect(%i[ZLIST ZARRAY array]).to include(pin.assignment.type) end - it "requalifies namespace definitions with leading colons" do + it 'requalifies namespace definitions with leading colons' do map = Solargraph::SourceMap.load_string(%( class Foo class ::Bar; end @@ -311,7 +311,7 @@ class ::Bar; end expect(map.pins.map(&:path)).not_to include('Foo::Bar') end - it "maps method parameters" do + it 'maps method parameters' do map = Solargraph::SourceMap.load_string(%( class Foo def bar baz, boo = 'boo', key: 'value' @@ -319,16 +319,16 @@ def bar baz, boo = 'boo', key: 'value' end )) pin = map.first_pin('Foo#bar') - expect(pin.parameter_names).to eq(['baz', 'boo', 'key']) - pin = map.locals.select{|p| p.name == 'baz'}.first + expect(pin.parameter_names).to eq(%w[baz boo key]) + pin = map.locals.select { |p| p.name == 'baz' }.first expect(pin).to be_a(Solargraph::Pin::Parameter) - pin = map.locals.select{|p| p.name == 'boo'}.first + pin = map.locals.select { |p| p.name == 'boo' }.first expect(pin).to be_a(Solargraph::Pin::Parameter) - pin = map.locals.select{|p| p.name == 'key'}.first + pin = map.locals.select { |p| p.name == 'key' }.first expect(pin).to be_a(Solargraph::Pin::Parameter) end - it "maps method splat parameters" do + it 'maps method splat parameters' do map = Solargraph::SourceMap.load_string(%( class Foo def bar *baz @@ -340,7 +340,7 @@ def bar *baz expect(pin.parameters.first.name).to eq('baz') end - it "maps method block parameters" do + it 'maps method block parameters' do map = Solargraph::SourceMap.load_string(%( class Foo def bar &block @@ -352,18 +352,18 @@ def bar &block expect(pin.parameters.first.name).to eq('block') end - it "adds superclasses to class pins" do + it 'adds superclasses to class pins' do map = Solargraph::SourceMap.load_string(%( class Sub < Sup; end )) # pin = map.first_pin('Sub') # expect(pin.superclass_reference.name).to eq('Sup') - pin = map.pins.select{|p| p.is_a?(Solargraph::Pin::Reference::Superclass)}.first + pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::Reference::Superclass) }.first expect(pin.namespace).to eq('Sub') expect(pin.name).to eq('Sup') end - it "modifies scope and visibility for module functions" do + it 'modifies scope and visibility for module functions' do map = Solargraph::SourceMap.load_string(%( module Functions module_function @@ -376,7 +376,7 @@ def foo; end expect(pin.visibility).to eq(:private) end - it "recognizes single module functions" do + it 'recognizes single module functions' do map = Solargraph::SourceMap.load_string(%( module Functions module_function def foo; end @@ -391,7 +391,7 @@ def bar; end expect(pin.visibility).to eq(:public) end - it "remaps methods for module_function symbol arguments" do + it 'remaps methods for module_function symbol arguments' do map = Solargraph::SourceMap.load_string(%( module Functions def foo @@ -409,13 +409,13 @@ def bar expect(pin.visibility).to eq(:private) pin = map.first_pin('Functions#bar') expect(pin.visibility).to eq(:public) - pin = map.pins.select{|p| p.name == '@foo' and p.context.scope == :class}.first + pin = map.pins.select { |p| p.name == '@foo' and p.context.scope == :class }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) - pin = map.pins.select{|p| p.name == '@foo' and p.context.scope == :instance}.first + pin = map.pins.select { |p| p.name == '@foo' and p.context.scope == :instance }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) end - it "modifies instance variables in module functions" do + it 'modifies instance variables in module functions' do map = Solargraph::SourceMap.load_string(%( module Functions module_function @@ -425,46 +425,46 @@ def foo end end )) - pin = map.pins.select{|p| p.name == '@foo' and p.context.scope == :class}.first + pin = map.pins.select { |p| p.name == '@foo' and p.context.scope == :class }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) - pin = map.pins.select{|p| p.name == '@foo' and p.context.scope == :instance}.first + pin = map.pins.select { |p| p.name == '@foo' and p.context.scope == :instance }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) - pin = map.pins.select{|p| p.name == '@bar' and p.context.scope == :class}.first + pin = map.pins.select { |p| p.name == '@bar' and p.context.scope == :class }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) - pin = map.pins.select{|p| p.name == '@bar' and p.context.scope == :instance}.first + pin = map.pins.select { |p| p.name == '@bar' and p.context.scope == :instance }.first expect(pin).to be_a(Solargraph::Pin::InstanceVariable) end - it "maps class variables" do + it 'maps class variables' do map = Solargraph::SourceMap.load_string(%( class Foo @@bar = 'bar' @@baz ||= 'baz' end )) - pin = map.pins.select{|p| p.name == '@@bar'}.first + pin = map.pins.select { |p| p.name == '@@bar' }.first expect(pin).to be_a(Solargraph::Pin::ClassVariable) - pin = map.pins.select{|p| p.name == '@@baz'}.first + pin = map.pins.select { |p| p.name == '@@baz' }.first expect(pin).to be_a(Solargraph::Pin::ClassVariable) end - it "maps local variables" do + it 'maps local variables' do map = Solargraph::SourceMap.load_string(%( x = y )) - pin = map.locals.select{|p| p.name == 'x'}.first + pin = map.locals.select { |p| p.name == 'x' }.first expect(pin).to be_a(Solargraph::Pin::LocalVariable) end - it "maps global variables" do + it 'maps global variables' do map = Solargraph::SourceMap.load_string(%( $x = y )) - pin = map.pins.select{|p| p.name == '$x'}.first + pin = map.pins.select { |p| p.name == '$x' }.first expect(pin).to be_a(Solargraph::Pin::GlobalVariable) end - it "maps constants" do + it 'maps constants' do map = Solargraph::SourceMap.load_string(%( module Foo BAR = 'bar' @@ -474,7 +474,7 @@ module Foo expect(pin).to be_a(Solargraph::Pin::Constant) end - it "maps singleton methods" do + it 'maps singleton methods' do map = Solargraph::SourceMap.load_string(%( class Foo def self.bar; end @@ -485,7 +485,7 @@ def self.bar; end expect(pin.context.scope).to be(:class) end - it "maps requalified singleton methods" do + it 'maps requalified singleton methods' do map = Solargraph::SourceMap.load_string(%( class Foo; end class Bar @@ -505,7 +505,7 @@ def boo; end expect(pin.context.scope).to be(:instance) end - it "maps private class methods" do + it 'maps private class methods' do map = Solargraph::SourceMap.load_string(%( class Foo def self.bar; end @@ -517,7 +517,7 @@ def self.bar; end expect(pin.visibility).to be(:private) end - it "maps singly defined private class methods" do + it 'maps singly defined private class methods' do map = Solargraph::SourceMap.load_string(%( class Foo private_class_method def bar; end @@ -528,7 +528,7 @@ class Foo expect(pin.visibility).to be(:private) end - it "maps private constants" do + it 'maps private constants' do map = Solargraph::SourceMap.load_string(%( class Foo BAR = 'bar' @@ -540,7 +540,7 @@ class Foo expect(pin.visibility).to be(:private) end - it "maps private namespaces" do + it 'maps private namespaces' do map = Solargraph::SourceMap.load_string(%( class Foo class Bar; end @@ -552,7 +552,7 @@ class Bar; end expect(pin.visibility).to be(:private) end - it "maps attribute writers" do + it 'maps attribute writers' do map = Solargraph::SourceMap.load_string(%( class Foo attr_writer :bar @@ -562,7 +562,7 @@ class Foo expect(map.pins.map(&:path)).not_to include('Foo#bar') end - it "maps attribute accessors" do + it 'maps attribute accessors' do map = Solargraph::SourceMap.load_string(%( class Foo attr_accessor :bar @@ -572,29 +572,29 @@ class Foo expect(map.pins.map(&:path)).to include('Foo#bar') end - it "maps extend self" do + it 'maps extend self' do map = Solargraph::SourceMap.load_string(%( class Foo extend self def bar; end end )) - pin = map.first_pin('Foo') + map.first_pin('Foo') # expect(pin.extend_references.map(&:name)).to include('Foo') - pin = map.pins.select{|p| p.is_a?(Solargraph::Pin::Reference::Extend)}.first + pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::Reference::Extend) }.first expect(pin.namespace).to eq('Foo') expect(pin.name).to eq('Foo') end - it "maps require calls" do + it 'maps require calls' do map = Solargraph::SourceMap.load_string(%( require 'set' )) - pin = map.pins.select{|p| p.is_a?(Solargraph::Pin::Reference::Require)}.first + pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::Reference::Require) }.first expect(pin.name).to eq('set') end - it "ignores dynamic require calls" do + it 'ignores dynamic require calls' do map = Solargraph::SourceMap.load_string(%( path = 'solargraph' require path @@ -602,16 +602,16 @@ def bar; end expect(map.requires.length).to eq(0) end - it "maps block parameters" do + it 'maps block parameters' do map = Solargraph::SourceMap.load_string(%( x.each do |y| end )) - pin = map.locals.select{|p| p.name == 'y'}.first + pin = map.locals.select { |p| p.name == 'y' }.first expect(pin).to be_a(Solargraph::Pin::Parameter) end - it "forces initialize methods to be private" do + it 'forces initialize methods to be private' do map = Solargraph::SourceMap.load_string(' class Foo def initialize name @@ -622,7 +622,7 @@ def initialize name expect(pin.visibility).to be(:private) end - it "maps top-level methods" do + it 'maps top-level methods' do map = Solargraph::SourceMap.load_string(%( def foo(bar, baz) end @@ -632,18 +632,18 @@ def foo(bar, baz) expect(pin).to be_a(Solargraph::Pin::Method) end - it "maps root blocks to class scope" do + it 'maps root blocks to class scope' do smap = Solargraph::SourceMap.load_string(%( @a = some_array @a.each do |b| b end ), 'test.rb') - pin = smap.pins.select{|p| p.is_a?(Solargraph::Pin::Block)}.first + pin = smap.pins.select { |p| p.is_a?(Solargraph::Pin::Block) }.first expect(pin.context.scope).to eq(:class) end - it "maps class method blocks to class scope" do + it 'maps class method blocks to class scope' do smap = Solargraph::SourceMap.load_string(%( class Foo def self.bar @@ -654,11 +654,11 @@ def self.bar end end )) - pin = smap.pins.select{|p| p.is_a?(Solargraph::Pin::Block)}.first + pin = smap.pins.select { |p| p.is_a?(Solargraph::Pin::Block) }.first expect(pin.context.scope).to eq(:class) end - it "maps instance method blocks to instance scope" do + it 'maps instance method blocks to instance scope' do smap = Solargraph::SourceMap.load_string(%( class Foo def bar @@ -669,11 +669,11 @@ def bar end end )) - pin = smap.pins.select{|p| p.is_a?(Solargraph::Pin::Block)}.first + pin = smap.pins.select { |p| p.is_a?(Solargraph::Pin::Block) }.first expect(pin.context.scope).to eq(:instance) end - it "maps rebased namespaces without leading colons" do + it 'maps rebased namespaces without leading colons' do smap = Solargraph::SourceMap.load_string(%( class Foo class ::Bar @@ -686,79 +686,79 @@ def baz; end expect(smap.first_pin('Bar#baz')).to be_a(Solargraph::Pin::Method) end - it "maps contexts of constants" do + it 'maps contexts of constants' do var = 'BAR' smap = Solargraph::SourceMap.load_string("#{var} = nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) smap = Solargraph::SourceMap.load_string("#{var} ||= nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) end - it "maps contexts of instance variables" do + it 'maps contexts of instance variables' do var = '@bar' smap = Solargraph::SourceMap.load_string("#{var} = nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) smap = Solargraph::SourceMap.load_string("#{var} ||= nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) end - it "maps contexts of class variables" do + it 'maps contexts of class variables' do var = '@@bar' smap = Solargraph::SourceMap.load_string("#{var} = nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) smap = Solargraph::SourceMap.load_string("#{var} ||= nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) end - it "maps contexts of global variables" do + it 'maps contexts of global variables' do var = '$bar' smap = Solargraph::SourceMap.load_string("#{var} = nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) smap = Solargraph::SourceMap.load_string("#{var} ||= nil") - pin = smap.pins.select{|p| p.name == var}.first + pin = smap.pins.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) end - it "maps contexts of local variables" do + it 'maps contexts of local variables' do var = 'bar' smap = Solargraph::SourceMap.load_string("#{var} = nil") - pin = smap.locals.select{|p| p.name == var}.first + pin = smap.locals.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) smap = Solargraph::SourceMap.load_string("#{var} ||= nil") - pin = smap.locals.select{|p| p.name == var}.first + pin = smap.locals.select { |p| p.name == var }.first expect(pin.context).to be_a(Solargraph::ComplexType) end - it "maps method aliases" do + it 'maps method aliases' do smap = Solargraph::SourceMap.load_string(%( class Foo def bar; end alias baz bar end )) - pin = smap.pins.select{|p| p.path == 'Foo#baz'}.first + pin = smap.pins.select { |p| p.path == 'Foo#baz' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) end - it "maps attribute aliases" do + it 'maps attribute aliases' do smap = Solargraph::SourceMap.load_string(%( class Foo attr_accessor :bar alias baz bar end )) - pin = smap.pins.select{|p| p.path == 'Foo#baz'}.first + pin = smap.pins.select { |p| p.path == 'Foo#baz' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) end - it "maps class method aliases" do + it 'maps class method aliases' do smap = Solargraph::SourceMap.load_string(%( class Foo class << self @@ -767,12 +767,12 @@ def bar; end end end )) - pin = smap.pins.select{|p| p.path == 'Foo.baz'}.first + pin = smap.pins.select { |p| p.path == 'Foo.baz' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) expect(pin.location.range.start.line).to eq(4) end - it "maps method macros" do + it 'maps method macros' do smap = Solargraph::SourceMap.load_string(%( class Foo # @!macro @@ -780,23 +780,23 @@ class Foo def make klass; end end ), 'test.rb') - pin = smap.pins.select{|p| p.path == 'Foo#make'}.first + pin = smap.pins.select { |p| p.path == 'Foo#make' }.first expect(pin.macros).not_to be_empty end - it "maps method directives" do + it 'maps method directives' do smap = Solargraph::SourceMap.load_string(%( class Foo # @!method bar(baz) # @return [String] end ), 'test.rb') - pin = smap.pins.select{|p| p.path == 'Foo#bar'}.first + pin = smap.pins.select { |p| p.path == 'Foo#bar' }.first expect(pin.return_type.tag).to eq('String') expect(pin.location.filename).to eq('test.rb') end - it "maps aliases from alias_method" do + it 'maps aliases from alias_method' do smap = Solargraph::SourceMap.load_string(%( class Foo class << self @@ -805,22 +805,22 @@ def bar; end end end )) - pin = smap.pins.select{|p| p.path == 'Foo.baz'}.first + pin = smap.pins.select { |p| p.path == 'Foo.baz' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) expect(pin.location.range.start.line).to eq(4) end - it "maps aliases with unknown bases" do + it 'maps aliases with unknown bases' do smap = Solargraph::SourceMap.load_string(%( class Foo alias bar baz end )) - pin = smap.pins.select{|p| p.path == 'Foo#bar'}.first + pin = smap.pins.select { |p| p.path == 'Foo#bar' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) end - it "maps aliases to superclass methods" do + it 'maps aliases to superclass methods' do smap = Solargraph::SourceMap.load_string(%( class Sup # My foo method @@ -830,33 +830,33 @@ class Sub < Sup alias bar foo end )) - pin = smap.pins.select{|p| p.path == 'Sub#bar'}.first + pin = smap.pins.select { |p| p.path == 'Sub#bar' }.first expect(pin).to be_a(Solargraph::Pin::MethodAlias) end - it "uses nodes for method parameter assignments" do + it 'uses nodes for method parameter assignments' do smap = Solargraph::SourceMap.load_string(%( class Foo def bar(baz = quz) end end )) - pin = smap.locals.select{|p| p.name == 'baz'}.first + pin = smap.locals.select { |p| p.name == 'baz' }.first # expect(pin.assignment).to be_a(Parser::AST::Node) expect(Solargraph::Parser.is_ast_node?(pin.assignment)).to be(true) end - it "defers resolution of distant alias_method aliases" do + it 'defers resolution of distant alias_method aliases' do smap = Solargraph::SourceMap.load_string(%( class MyClass alias_method :foo, :bar end )) - pin = smap.pins.select{|p| p.is_a?(Solargraph::Pin::MethodAlias)}.first + pin = smap.pins.select { |p| p.is_a?(Solargraph::Pin::MethodAlias) }.first expect(pin).not_to be_nil end - it "maps explicit begin nodes" do + it 'maps explicit begin nodes' do smap = Solargraph::SourceMap.load_string(%( def foo begin @@ -864,11 +864,11 @@ def foo end end )) - pin = smap.pins.select{|p| p.name == '@x'}.first + pin = smap.pins.select { |p| p.name == '@x' }.first expect(pin).not_to be_nil end - it "maps rescue nodes" do + it 'maps rescue nodes' do smap = Solargraph::SourceMap.load_string(%( def foo @x = make_x @@ -876,13 +876,13 @@ def foo @y = y end )) - err_pin = smap.locals{|p| p.name == 'err'}.first + err_pin = smap.locals { |p| p.name == 'err' }.first expect(err_pin).not_to be_nil - var_pin = smap.pins.select{|p| p.name == '@y'}.first + var_pin = smap.pins.select { |p| p.name == '@y' }.first expect(var_pin).not_to be_nil end - it "maps begin/rescue nodes" do + it 'maps begin/rescue nodes' do smap = Solargraph::SourceMap.load_string(%( def foo begin @@ -892,70 +892,70 @@ def foo end end )) - err_pin = smap.locals{|p| p.name == 'err'}.first + err_pin = smap.locals { |p| p.name == 'err' }.first expect(err_pin).not_to be_nil - var_pin = smap.pins.select{|p| p.name == '@y'}.first + var_pin = smap.pins.select { |p| p.name == '@y' }.first expect(var_pin).not_to be_nil end - it "maps classes with long namespaces" do + it 'maps classes with long namespaces' do smap = Solargraph::SourceMap.load_string(%( class Foo::Bar end ), 'test.rb') - pin = smap.pins.select{|p| p.path == 'Foo::Bar'}.first + pin = smap.pins.select { |p| p.path == 'Foo::Bar' }.first expect(pin).not_to be_nil expect(pin.namespace).to eq('Foo') expect(pin.name).to eq('Bar') expect(pin.path).to eq('Foo::Bar') end - it "ignores aliases that do not map to methods or attributes" do - expect { - smap = Solargraph::SourceMap.load_string(%( + it 'ignores aliases that do not map to methods or attributes' do + expect do + Solargraph::SourceMap.load_string(%( class Foo xyz = String alias foo xyz alias_method :foo, :xyz end ), 'test.rb') - }.not_to raise_error + end.not_to raise_error end - it "ignores private_class_methods that do not map to methods or attributes" do - expect { - smap = Solargraph::SourceMap.load_string(%( + it 'ignores private_class_methods that do not map to methods or attributes' do + expect do + Solargraph::SourceMap.load_string(%( class Foo var = some_method private_class_method :var end ), 'test.rb') - }.not_to raise_error + end.not_to raise_error end - it "ignores private_constants that do not map to namespaces or constants" do - expect { - smap = Solargraph::SourceMap.load_string(%( + it 'ignores private_constants that do not map to namespaces or constants' do + expect do + Solargraph::SourceMap.load_string(%( class Foo var = some_method private_constant :var end ), 'test.rb') - }.not_to raise_error + end.not_to raise_error end - it "ignores module_functions that do not map to methods or attributes" do - expect { - smap = Solargraph::SourceMap.load_string(%( + it 'ignores module_functions that do not map to methods or attributes' do + expect do + Solargraph::SourceMap.load_string(%( class Foo var = some_method module_function :var end ), 'test.rb') - }.not_to raise_error + end.not_to raise_error end - it "handles parse directives" do + it 'handles parse directives' do smap = Solargraph::SourceMap.load_string(%( class Foo # @!parse @@ -965,18 +965,18 @@ class Foo expect(smap.pins.map(&:path)).to include('Foo::Bar') end - it "ignores syntax errors in parse directives" do - expect { + it 'ignores syntax errors in parse directives' do + expect do Solargraph::SourceMap.load_string(%( class Foo # @!parse # def end )) - }.not_to raise_error + end.not_to raise_error end - it "sets visibility for symbol parameters" do + it 'sets visibility for symbol parameters' do smap = Solargraph::SourceMap.load_string(%( class Foo def pub; end @@ -987,33 +987,33 @@ def pro; end protected 'pro' end )) - pub = smap.pins.select{|pin| pin.path == 'Foo#pub'}.first + pub = smap.pins.select { |pin| pin.path == 'Foo#pub' }.first expect(pub.visibility).to eq(:public) - bar = smap.pins.select{|pin| pin.path == 'Foo#bar'}.first + bar = smap.pins.select { |pin| pin.path == 'Foo#bar' }.first expect(bar.visibility).to eq(:private) - baz = smap.pins.select{|pin| pin.path == 'Foo#baz'}.first + baz = smap.pins.select { |pin| pin.path == 'Foo#baz' }.first expect(baz.visibility).to eq(:public) - pro = smap.pins.select{|pin| pin.path == 'Foo#pro'}.first + pro = smap.pins.select { |pin| pin.path == 'Foo#pro' }.first expect(pro.visibility).to eq(:protected) end - it "ignores errors in method directives" do - expect { + it 'ignores errors in method directives' do + expect do Solargraph::SourceMap.load_string(%[ class Foo # @!method bar( end ]) - }.not_to raise_error + end.not_to raise_error end - it "handles invalid byte sequences" do - expect { + it 'handles invalid byte sequences' do + expect do Solargraph::SourceMap.load(File.join('spec', 'fixtures', 'invalid_utf8.rb')) - }.not_to raise_error + end.not_to raise_error end - it "applies private_class_method to attributes" do + it 'applies private_class_method to attributes' do smap = Solargraph::SourceMap.load_string(%( module Foo class << self @@ -1022,7 +1022,7 @@ class << self private_class_method :bar end )) - pin = smap.pins.select{|pin| pin.path == 'Foo.bar'}.first + pin = smap.pins.select { |pin| pin.path == 'Foo.bar' }.first expect(pin.visibility).to eq(:private) end @@ -1352,9 +1352,9 @@ class Foo private_class_method end ) - expect { + expect do Solargraph::SourceMap.load_string(code, 'test.rb') - }.not_to raise_error + end.not_to raise_error end it 'positions method directive pins' do @@ -1431,7 +1431,7 @@ def foo bar:, **splat end it 'gracefully handles misunderstood macros' do - expect { + expect do Solargraph::SourceMap.load_string(%( module Foo # @!macro macro1 @@ -1442,7 +1442,7 @@ module Foo class Bar; end end )) - }.not_to raise_error + end.not_to raise_error end it 'maps autoload paths' do @@ -1570,15 +1570,15 @@ def barbaz; end end it 'handles invalid byte sequences' do - expect { + expect do Solargraph::SourceMap.load('spec/fixtures/invalid_byte.rb') - }.not_to raise_error + end.not_to raise_error end it 'handles invalid byte sequences in stringified node comments' do - expect { + expect do Solargraph::SourceMap.load('spec/fixtures/invalid_node_comment.rb') - }.not_to raise_error + end.not_to raise_error end it 'parses method directives that start with multiple hashes' do diff --git a/spec/source_map_spec.rb b/spec/source_map_spec.rb index 5d587e27c..8dba8c0b4 100644 --- a/spec/source_map_spec.rb +++ b/spec/source_map_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::SourceMap do - it "locates named path pins" do + it 'locates named path pins' do map = Solargraph::SourceMap.load_string(%( class Foo def bar; end @@ -9,15 +9,17 @@ def bar; end expect(pin.path).to eq('Foo#bar') end - it "queries symbols using fuzzy matching" do + it 'queries symbols using fuzzy matching' do map = Solargraph::SourceMap.load_string(%( class FooBar def baz_qux; end end )) - expect(map.query_symbols("foo")).to eq(map.document_symbols) - expect(map.query_symbols("foobar")).to eq(map.document_symbols) - expect(map.query_symbols("bazqux")).to eq(map.document_symbols.select{ |pin_namespace| pin_namespace.name == "baz_qux" }) + expect(map.query_symbols('foo')).to eq(map.document_symbols) + expect(map.query_symbols('foobar')).to eq(map.document_symbols) + expect(map.query_symbols('bazqux')).to eq(map.document_symbols.select { |pin_namespace| + pin_namespace.name == 'baz_qux' + }) end it 'returns all pins, except for references as document symbols' do @@ -37,7 +39,7 @@ def baz_qux; end it 'includes convention pins in document symbols' do dummy_convention = Class.new(Solargraph::Convention::Base) do - def local(source_map) + def local source_map source_map.document_symbols # call memoized method Solargraph::Environ.new( @@ -65,7 +67,7 @@ def baz_qux; end Solargraph::Convention.unregister dummy_convention end - it "locates block pins" do + it 'locates block pins' do map = Solargraph::SourceMap.load_string(%( class Foo 100.times do @@ -163,7 +165,7 @@ class Foo; end end ), 'test.rb') locals = map.locals_at(Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 0, 5, 0))).map(&:name) - expect(locals).to eq(['x', 'foo']) + expect(locals).to eq(%w[x foo]) end it 'updates cached inference when the ApiMap changes' do diff --git a/spec/source_spec.rb b/spec/source_spec.rb index 93624313c..53c7ba4d3 100644 --- a/spec/source_spec.rb +++ b/spec/source_spec.rb @@ -1,5 +1,5 @@ describe Solargraph::Source do - it "parses code" do + it 'parses code' do code = 'class Foo;def bar;end;end' source = described_class.new(code) expect(source.code).to eq(code) @@ -7,7 +7,7 @@ expect(source).to be_parsed end - it "fixes invalid code" do + it 'fixes invalid code' do code = 'class Foo; def bar; x.' source = described_class.new(code) expect(source.code).to eq(code) @@ -17,7 +17,7 @@ expect(source).not_to be_parsed end - it "finds ranges" do + it 'finds ranges' do code = %( class Foo def bar @@ -29,7 +29,7 @@ def bar expect(source.at(range)).to eq('def bar') end - it "finds nodes" do + it 'finds nodes' do code = 'class Foo;def bar;end;end' source = described_class.new(code) node = source.node_at(0, 0) @@ -38,7 +38,7 @@ def bar expect(node.type).to eq(:def) end - it "synchronizes from incremental updates" do + it 'synchronizes from incremental updates' do code = 'class Foo;def bar;end;end' source = described_class.new(code) updater = Solargraph::Source::Updater.new( @@ -55,19 +55,19 @@ def bar expect(changed.node.children[0].children[1]).to eq(:Food) end - it "synchronizes from full updates" do + it 'synchronizes from full updates' do code1 = 'class Foo;end' code2 = 'class Bar;end' source = described_class.new(code1) updater = Solargraph::Source::Updater.new(nil, 0, [ - Solargraph::Source::Change.new(nil, code2) - ]) + Solargraph::Source::Change.new(nil, code2) + ]) changed = source.synchronize(updater) expect(changed.code).to eq(code2) expect(changed.node.children[0].children[1]).to eq(:Bar) end - it "repairs broken incremental updates" do + it 'repairs broken incremental updates' do code = %( class Foo def bar @@ -89,18 +89,18 @@ def bar expect(changed).to be_repaired end - it "flags irreparable updates" do + it 'flags irreparable updates' do code = 'class Foo;def bar;end;end' source = described_class.new(code) updater = Solargraph::Source::Updater.new(nil, 0, [ - Solargraph::Source::Change.new(nil, 'end;end') - ]) + Solargraph::Source::Change.new(nil, 'end;end') + ]) changed = source.synchronize(updater) expect(changed).to be_parsed expect(changed).to be_repaired end - it "finds references" do + it 'finds references' do source = Solargraph::Source.load_string(%( class Foo def bar @@ -113,17 +113,17 @@ def bar= 𐐀.bar = 1 )) foos = source.references('Foo') - foobacks = foos.map{|f| source.at(f.range)} - expect(foobacks).to eq(['Foo', 'Foo']) + foobacks = foos.map { |f| source.at(f.range) } + expect(foobacks).to eq(%w[Foo Foo]) bars = source.references('bar') - barbacks = bars.map{|b| source.at(b.range)} - expect(barbacks).to eq(['bar', 'bar']) + barbacks = bars.map { |b| source.at(b.range) } + expect(barbacks).to eq(%w[bar bar]) assign_bars = source.references('bar=') - assign_barbacks = assign_bars.map{|b| source.at(b.range)} + assign_barbacks = assign_bars.map { |b| source.at(b.range) } expect(assign_barbacks).to eq(['bar=', 'bar =']) end - it "allows escape sequences incompatible with UTF-8" do + it 'allows escape sequences incompatible with UTF-8' do source = Solargraph::Source.new(' x = " Un bUen café \x92" puts x @@ -131,19 +131,19 @@ def bar= expect(source.parsed?).to be(true) end - it "fixes invalid byte sequences in UTF-8 encoding" do - expect { + it 'fixes invalid byte sequences in UTF-8 encoding' do + expect do Solargraph::Source.load('spec/fixtures/invalid_byte.rb') - }.not_to raise_error + end.not_to raise_error end - it "loads files with Unicode characters" do - expect { + it 'loads files with Unicode characters' do + expect do Solargraph::Source.load('spec/fixtures/unicode.rb') - }.not_to raise_error + end.not_to raise_error end - it "updates itself when code does not change" do + it 'updates itself when code does not change' do original = Solargraph::Source.load_string('x = y', 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, []) updated = original.synchronize(updater) @@ -151,7 +151,7 @@ def bar= expect(updated.version).to eq(1) end - it "handles unparseable code" do + it 'handles unparseable code' do source = Solargraph::Source.load_string(%( 100.times do |num| )) @@ -161,7 +161,7 @@ def bar= expect(source.parsed?).to be(false) end - it "finds foldable ranges" do + it 'finds foldable ranges' do # Of the 7 possible ranges, 2 are too short to be foldable source = Solargraph::Source.load_string(%( =begin @@ -236,7 +236,7 @@ def range_2 expect(source.folding_ranges.first.start.line).to eq(4) end - it "finishes synchronizations for unbalanced lines" do + it 'finishes synchronizations for unbalanced lines' do source1 = Solargraph::Source.load_string('x = 1', 'test.rb') source2 = source1.synchronize Solargraph::Source::Updater.new( 'test.rb', @@ -252,7 +252,7 @@ def range_2 expect(source2).to be_synchronized end - it "handles comment arrays that overlap lines" do + it 'handles comment arrays that overlap lines' do # Fixes negative argument error (castwide/solargraph#141) source = Solargraph::Source.load_string(%( =begin @@ -260,12 +260,12 @@ def range_2 y = 1 #foo )) node = source.node_at(3, 0) - expect { + expect do source.comments_for(node) - }.not_to raise_error + end.not_to raise_error end - it "formats comments with multiple hash prefixes" do + it 'formats comments with multiple hash prefixes' do source = Solargraph::Source.load_string(%( ## # one @@ -274,7 +274,7 @@ class Foo; end )) node = source.node_at(4, 7) comments = source.comments_for(node) - expect(comments.lines.map(&:chomp)).to eq(['one', 'two']) + expect(comments.lines.map(&:chomp)).to eq(%w[one two]) end it 'does not include inner comments' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ccf7b0897..62280f335 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -40,10 +40,9 @@ def set_logging # execute any logging blocks to make sure they don't blow up Solargraph::Logging.logger.sev_threshold = Logger::DEBUG # ...but still suppress logger output in specs (if possible) - if Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') - Solargraph::Logging.logger.reopen(File::NULL) - warn "Logging set to null" - end + return unless Solargraph::Logging.logger.respond_to?(:reopen) && !ENV.key?('SOLARGRAPH_LOG') + Solargraph::Logging.logger.reopen(File::NULL) + warn 'Logging set to null' end set_logging @@ -58,14 +57,14 @@ def set_logging # @param name [String] # @param value [String] -def with_env_var(name, value) - old_value = ENV[name] # Store the old value - ENV[name] = value # Set to new value +def with_env_var name, value + old_value = ENV.fetch(name, nil) # Store the old value + ENV[name] = value # Set to new value begin - yield # Execute the block + yield # Execute the block ensure - ENV[name] = old_value # Restore the old value + ENV[name] = old_value # Restore the old value end end diff --git a/spec/type_checker/levels/alpha_spec.rb b/spec/type_checker/levels/alpha_spec.rb index ac164dec2..8d317ddf1 100644 --- a/spec/type_checker/levels/alpha_spec.rb +++ b/spec/type_checker/levels/alpha_spec.rb @@ -122,7 +122,8 @@ def bar end )) - expect(checker.problems.map(&:message)).to eq(["Foo#bar return type could not be inferred", "Unresolved call to round on Integer, nil"]) + expect(checker.problems.map(&:message)).to eq(['Foo#bar return type could not be inferred', + 'Unresolved call to round on Integer, nil']) end it 'understands &. in return position' do diff --git a/spec/type_checker/levels/normal_spec.rb b/spec/type_checker/levels/normal_spec.rb index 668e62886..1a9e589da 100644 --- a/spec/type_checker/levels/normal_spec.rb +++ b/spec/type_checker/levels/normal_spec.rb @@ -1,6 +1,6 @@ describe Solargraph::TypeChecker do context 'when checking at normal level' do - def type_checker(code) + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :normal) end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index d327199e3..100f91ddb 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -1,7 +1,7 @@ describe Solargraph::TypeChecker do context 'strict level' do # @return [Solargraph::TypeChecker] - def type_checker(code) + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :strict) end @@ -787,7 +787,6 @@ def test(foo: nil) expect(checker.problems).to be_empty end - it 'validates parameters in function calls' do checker = type_checker(%( # @param bar [String] @@ -937,7 +936,7 @@ def foo *path, baz; end expect(checker.problems.map(&:message)).to eq([]) end - it "understands enough of define_method not to think the block is in class scope" do + it 'understands enough of define_method not to think the block is in class scope' do checker = type_checker(%( class Foo def initialize @@ -964,7 +963,7 @@ def bar expect(checker.problems.map(&:message)).to be_empty end - it "Uses flow scope to specialize understanding of cvar types" do + it 'Uses flow scope to specialize understanding of cvar types' do pending 'better cvar support' checker = type_checker(%( @@ -992,10 +991,10 @@ def foo end end )) - expect(checker.problems.map(&:message)).to eq(["Unresolved call to upcase!"]) + expect(checker.problems.map(&:message)).to eq(['Unresolved call to upcase!']) end - it "does not lose track of place and false alarm when using kwargs after a splat" do + it 'does not lose track of place and false alarm when using kwargs after a splat' do checker = type_checker(%( def foo(a, b, c); end def bar(*args, **kwargs, &blk) @@ -1005,7 +1004,7 @@ def bar(*args, **kwargs, &blk) expect(checker.problems.map(&:message)).to eq([]) end - it "understands Array#+ overloads" do + it 'understands Array#+ overloads' do checker = type_checker(%( c = ['a'] + ['a'] c @@ -1013,7 +1012,7 @@ def bar(*args, **kwargs, &blk) expect(checker.problems.map(&:message)).to eq([]) end - it "understands String#+ overloads" do + it 'understands String#+ overloads' do checker = type_checker(%( detail = '' detail += "foo" @@ -1022,7 +1021,7 @@ def bar(*args, **kwargs, &blk) expect(checker.problems.map(&:message)).to eq([]) end - it "understands Enumerable#each via _Each self type" do + it 'understands Enumerable#each via _Each self type' do checker = type_checker(%( class Blah # @param e [Enumerable] diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index a2f053ef9..e2c871407 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -1,6 +1,6 @@ describe Solargraph::TypeChecker do context 'strong level' do - def type_checker(code) + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :strong) end @@ -763,7 +763,8 @@ def bar end )) - expect(checker.problems.map(&:message)).to eq(["Foo#bar return type could not be inferred", "Unresolved call to round on Integer, nil"]) + expect(checker.problems.map(&:message)).to eq(['Foo#bar return type could not be inferred', + 'Unresolved call to round on Integer, nil']) end it 'performs simple flow-sensitive typing on lvars' do diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index 4add0903d..0cbdb83d9 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -1,6 +1,6 @@ describe Solargraph::TypeChecker do context 'typed level' do - def type_checker(code) + def type_checker code Solargraph::TypeChecker.load_string(code, 'test.rb', :typed) end diff --git a/spec/type_checker_spec.rb b/spec/type_checker_spec.rb index 33c2125e7..4597c6d75 100644 --- a/spec/type_checker_spec.rb +++ b/spec/type_checker_spec.rb @@ -2,12 +2,12 @@ describe Solargraph::TypeChecker do it 'does not raise errors checking unparsed sources' do - expect { + expect do checker = Solargraph::TypeChecker.load_string(%( foo{ )) checker.problems - }.not_to raise_error + end.not_to raise_error end it 'ignores tagged problems' do @@ -38,7 +38,7 @@ def documentation end ), nil, :strict) timed_out = true - Timeout::timeout(5) do # seconds + Timeout.timeout(5) do # seconds checker.problems timed_out = false end diff --git a/spec/workspace/config_spec.rb b/spec/workspace/config_spec.rb index 9b14d4337..4d8245d1b 100644 --- a/spec/workspace/config_spec.rb +++ b/spec/workspace/config_spec.rb @@ -2,17 +2,18 @@ require 'tmpdir' describe Solargraph::Workspace::Config do - let(:dir_path) { File.realpath(Dir.mktmpdir) } - after(:each) { FileUtils.remove_entry(dir_path) } + let(:dir_path) { File.realpath(Dir.mktmpdir) } - it "includes .rb files by default" do + after { FileUtils.remove_entry(dir_path) } + + it 'includes .rb files by default' do file = File.join(dir_path, 'file.rb') File.write(file, 'exit') config = Solargraph::Workspace::Config.new(dir_path) expect(config.calculated).to include(file) end - it "includes .rb files in subdirectories by default" do + it 'includes .rb files in subdirectories by default' do Dir.mkdir(File.join(dir_path, 'lib')) file = File.join(dir_path, 'lib', 'file.rb') File.write(file, 'exit') @@ -20,7 +21,7 @@ expect(config.calculated).to include(file) end - it "excludes test directories by default" do + it 'excludes test directories by default' do Dir.mkdir(File.join(dir_path, 'test')) file = File.join(dir_path, 'test', 'file.rb') File.write(file, 'exit') @@ -28,7 +29,7 @@ expect(config.calculated).not_to include(file) end - it "excludes spec directories by default" do + it 'excludes spec directories by default' do Dir.mkdir(File.join(dir_path, 'spec')) file = File.join(dir_path, 'spec', 'file.rb') File.write(file, 'exit') @@ -36,7 +37,7 @@ expect(config.calculated).not_to include(file) end - it "excludes vendor directories by default" do + it 'excludes vendor directories by default' do Dir.mkdir(File.join(dir_path, 'vendor')) file = File.join(dir_path, 'vendor', 'file.rb') File.write(file, 'exit') @@ -44,7 +45,7 @@ expect(config.calculated).not_to include(file) end - it "includes base reporters by default" do + it 'includes base reporters by default' do config = Solargraph::Workspace::Config.new(dir_path) expect(config.reporters).to include('rubocop') expect(config.reporters).to include('require_not_found') diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 78b71ce3b..0489b06a4 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -6,15 +6,15 @@ let(:dir_path) { File.realpath(Dir.mktmpdir) } let(:file_path) { File.join(dir_path, 'file.rb') } - before(:each) { File.write(file_path, 'exit') } - after(:each) { FileUtils.remove_entry(dir_path) } + before { File.write(file_path, 'exit') } + after { FileUtils.remove_entry(dir_path) } - it "loads sources from a directory" do + it 'loads sources from a directory' do expect(workspace.filenames).to include(file_path) expect(workspace.has_file?(file_path)).to be(true) end - it "ignores non-Ruby files by default" do + it 'ignores non-Ruby files by default' do not_ruby = File.join(dir_path, 'not_ruby.txt') File.write not_ruby, 'text' @@ -22,14 +22,14 @@ expect(workspace.filenames).not_to include(not_ruby) end - it "does not merge non-workspace sources" do + it 'does not merge non-workspace sources' do source = Solargraph::Source.load_string('exit', 'not_ruby.txt') workspace.merge source expect(workspace.filenames).not_to include(source.filename) end - it "updates sources" do + it 'updates sources' do original = workspace.source(file_path) updated = Solargraph::Source.load_string('puts "updated"', file_path) workspace.merge updated @@ -39,7 +39,7 @@ expect(workspace.source(file_path)).to eq(updated) end - it "removes deleted sources" do + it 'removes deleted sources' do expect(workspace.filenames).to include(file_path) original = workspace.source(file_path) @@ -49,37 +49,37 @@ expect(workspace.filenames).not_to include(file_path) end - it "raises an exception for workspace size limits" do + it 'raises an exception for workspace size limits' do config = double(:config, calculated: Array.new(Solargraph::Workspace::Config::MAX_FILES + 1), max_files: Solargraph::Workspace::Config::MAX_FILES) - expect { + expect do Solargraph::Workspace.new('.', config) - }.to raise_error(Solargraph::WorkspaceTooLargeError) + end.to raise_error(Solargraph::WorkspaceTooLargeError) end - it "allows for unlimited files in config" do + it 'allows for unlimited files in config' do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, '') calculated = Array.new(Solargraph::Workspace::Config::MAX_FILES + 1) { gemspec_file } # @todo Mock reveals tight coupling config = double(:config, calculated: calculated, max_files: 0, allow?: true, require_paths: [], plugins: []) - expect { + expect do Solargraph::Workspace.new('.', config) - }.not_to raise_error + end.not_to raise_error end - it "detects gemspecs in workspaces" do + it 'detects gemspecs in workspaces' do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, '') expect(workspace.gemspec?).to be(true) expect(workspace.gemspec_files).to eq([gemspec_file]) end - it "generates default require path" do + it 'generates default require path' do expect(workspace.require_paths).to eq([File.join(dir_path, 'lib')]) end - it "generates require paths from gemspecs" do + it 'generates require paths from gemspecs' do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, %( Gem::Specification.new do |s| @@ -93,7 +93,7 @@ expect(workspace.require_paths).to eq([File.join(dir_path, 'other_lib')]) end - it "rescues errors in gemspecs" do + it 'rescues errors in gemspecs' do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, %( raise 'Error' @@ -101,7 +101,7 @@ expect(workspace.require_paths).to eq([File.join(dir_path, 'lib')]) end - it "rescues syntax errors in gemspecs" do + it 'rescues syntax errors in gemspecs' do gemspec_file = File.join(dir_path, 'test.gemspec') File.write(gemspec_file, %( 123. @@ -109,7 +109,7 @@ expect(workspace.require_paths).to eq([File.join(dir_path, 'lib')]) end - it "detects locally required paths" do + it 'detects locally required paths' do required_file = File.join(dir_path, 'lib', 'test.rb') Dir.mkdir(File.join(dir_path, 'lib')) File.write(required_file, 'exit') @@ -122,7 +122,7 @@ expect(workspace.would_require?('emptydir')).to be(false) end - it "uses configured require paths" do + it 'uses configured require paths' do workspace = Solargraph::Workspace.new('spec/fixtures/workspace') expect(workspace.require_paths).to eq([File.absolute_path('spec/fixtures/workspace/lib'), File.absolute_path('spec/fixtures/workspace/ext')]) @@ -135,10 +135,11 @@ end it 'rescues errors loading files into sources' do - config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, require_paths: [], plugins: []) - expect { + config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, + require_paths: [], plugins: []) + expect do Solargraph::Workspace.new('./path', config) - }.not_to raise_error + end.not_to raise_error end describe '#cache_all_for_workspace!' do diff --git a/spec/yard_map/mapper/to_method_spec.rb b/spec/yard_map/mapper/to_method_spec.rb index c90fe75ed..e406f00a4 100644 --- a/spec/yard_map/mapper/to_method_spec.rb +++ b/spec/yard_map/mapper/to_method_spec.rb @@ -1,38 +1,38 @@ describe Solargraph::YardMap::Mapper::ToMethod do - let(:code_object) { + let(:code_object) do namespace = YARD::CodeObjects::ModuleObject.new(nil, 'Example') YARD::CodeObjects::MethodObject.new(namespace, 'foo') - } + end it 'parses args' do - code_object.parameters = [["bar", nil]] + code_object.parameters = [['bar', nil]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:arg) - expect(param.name).to eq("bar") - expect(param.full).to eq("bar") + expect(param.name).to eq('bar') + expect(param.full).to eq('bar') end it 'parses optargs' do - code_object.parameters = [["bar", "'baz'"]] + code_object.parameters = [['bar', "'baz'"]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:optarg) - expect(param.name).to eq("bar") + expect(param.name).to eq('bar') expect(param.full).to eq("bar = 'baz'") end it 'parses kwargs' do - code_object.parameters = [["bar:", nil]] + code_object.parameters = [['bar:', nil]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.name).to eq('bar') expect(param.decl).to be(:kwarg) - expect(param.full).to eq("bar:") + expect(param.full).to eq('bar:') end it 'parses kwoptargs' do - code_object.parameters = [["bar:", "'baz'"]] + code_object.parameters = [['bar:', "'baz'"]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:kwoptarg) @@ -41,43 +41,43 @@ end it 'parses restargs' do - code_object.parameters = [["*bar", nil]] + code_object.parameters = [['*bar', nil]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:restarg) expect(param.name).to eq('bar') - expect(param.full).to eq("*bar") + expect(param.full).to eq('*bar') end it 'parses kwrestargs' do - code_object.parameters = [["**bar", nil]] + code_object.parameters = [['**bar', nil]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:kwrestarg) expect(param.name).to eq('bar') - expect(param.full).to eq("**bar") + expect(param.full).to eq('**bar') end it 'parses blockargs' do - code_object.parameters = [["&bar", nil]] + code_object.parameters = [['&bar', nil]] pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) param = pin.parameters.first expect(param.decl).to be(:blockarg) expect(param.name).to eq('bar') - expect(param.full).to eq("&bar") + expect(param.full).to eq('&bar') end it 'parses undeclared but typed blockargs' do pending('block args coming from YARD alone') code_object.parameters = [] - code_object.docstring = < Date: Sat, 31 Jan 2026 16:53:57 -0500 Subject: [PATCH 059/206] rubocop -A --- .rubocop_todo.yml | 234 ++---------------- Gemfile | 2 + Rakefile | 2 + bin/solargraph | 1 + lib/solargraph/api_map.rb | 19 +- lib/solargraph/api_map/index.rb | 2 +- lib/solargraph/api_map/source_to_yard.rb | 2 +- lib/solargraph/api_map/store.rb | 2 +- lib/solargraph/bench.rb | 3 +- lib/solargraph/complex_type.rb | 20 +- lib/solargraph/complex_type/type_methods.rb | 4 +- lib/solargraph/complex_type/unique_type.rb | 18 +- .../data_definition/data_definition_node.rb | 2 +- lib/solargraph/convention/gemfile.rb | 2 +- lib/solargraph/convention/gemspec.rb | 2 +- lib/solargraph/convention/rakefile.rb | 2 +- lib/solargraph/converters/dd.rb | 2 + lib/solargraph/converters/dl.rb | 2 + lib/solargraph/converters/dt.rb | 2 + lib/solargraph/converters/misc.rb | 2 + lib/solargraph/diagnostics/rubocop.rb | 2 +- lib/solargraph/diagnostics/rubocop_helpers.rb | 2 +- lib/solargraph/diagnostics/update_errors.rb | 10 +- lib/solargraph/doc_map.rb | 4 +- lib/solargraph/gem_pins.rb | 4 +- lib/solargraph/language_server/host.rb | 2 +- lib/solargraph/language_server/message.rb | 2 +- .../language_server/message/base.rb | 2 +- .../message/completion_item/resolve.rb | 2 +- .../message/text_document/definition.rb | 76 +++--- .../text_document/document_highlight.rb | 28 ++- .../message/text_document/document_symbol.rb | 50 ++-- .../message/text_document/hover.rb | 2 +- .../message/text_document/prepare_rename.rb | 20 +- .../message/text_document/references.rb | 28 ++- .../message/text_document/rename.rb | 34 +-- .../message/text_document/signature_help.rb | 2 +- .../message/text_document/type_definition.rb | 44 ++-- .../workspace/did_change_configuration.rb | 62 ++--- .../workspace/did_change_watched_files.rb | 69 +++--- .../workspace/did_change_workspace_folders.rb | 40 +-- .../message/workspace/workspace_symbol.rb | 46 ++-- lib/solargraph/language_server/request.rb | 2 +- .../language_server/transport/data_reader.rb | 2 +- lib/solargraph/library.rb | 22 +- lib/solargraph/location.rb | 10 +- lib/solargraph/logging.rb | 6 +- lib/solargraph/page.rb | 2 +- lib/solargraph/parser.rb | 2 + lib/solargraph/parser/comment_ripper.rb | 6 +- .../parser/flow_sensitive_typing.rb | 2 + lib/solargraph/parser/parser_gem.rb | 2 + .../parser/parser_gem/node_chainer.rb | 9 +- .../parser/parser_gem/node_methods.rb | 34 +-- .../parser_gem/node_processors/sclass_node.rb | 2 +- .../parser_gem/node_processors/send_node.rb | 16 +- lib/solargraph/parser/snippet.rb | 2 + lib/solargraph/pin/base.rb | 20 +- lib/solargraph/pin/breakable.rb | 2 + lib/solargraph/pin/callable.rb | 6 +- lib/solargraph/pin/closure.rb | 2 +- lib/solargraph/pin/compound_statement.rb | 2 + lib/solargraph/pin/constant.rb | 4 +- lib/solargraph/pin/conversions.rb | 2 +- lib/solargraph/pin/delegated_method.rb | 2 +- lib/solargraph/pin/local_variable.rb | 4 +- lib/solargraph/pin/method.rb | 24 +- lib/solargraph/pin/namespace.rb | 4 +- lib/solargraph/pin/parameter.rb | 13 +- lib/solargraph/pin/search.rb | 2 +- lib/solargraph/pin/signature.rb | 6 +- lib/solargraph/pin_cache.rb | 8 +- lib/solargraph/position.rb | 12 +- lib/solargraph/range.rb | 12 +- lib/solargraph/rbs_map.rb | 4 +- lib/solargraph/rbs_map/conversions.rb | 45 ++-- lib/solargraph/rbs_map/core_fills.rb | 6 +- lib/solargraph/shell.rb | 10 +- lib/solargraph/source.rb | 10 +- lib/solargraph/source/chain.rb | 12 +- lib/solargraph/source/chain/array.rb | 6 +- lib/solargraph/source/chain/call.rb | 16 +- lib/solargraph/source/chain/constant.rb | 2 +- lib/solargraph/source/chain/hash.rb | 14 +- lib/solargraph/source/chain/if.rb | 14 +- lib/solargraph/source/chain/link.rb | 22 +- lib/solargraph/source/chain/q_call.rb | 2 + lib/solargraph/source/change.rb | 6 +- lib/solargraph/source/cursor.rb | 6 +- lib/solargraph/source/source_chainer.rb | 31 +-- lib/solargraph/source/updater.rb | 2 +- lib/solargraph/source_map/clip.rb | 2 +- lib/solargraph/source_map/mapper.rb | 8 +- lib/solargraph/type_checker.rb | 9 +- lib/solargraph/workspace.rb | 4 +- lib/solargraph/yard_map/helpers.rb | 2 + lib/solargraph/yard_map/mapper.rb | 9 +- lib/solargraph/yard_map/mapper/to_method.rb | 2 +- solargraph.gemspec | 4 +- spec/api_map/cache_spec.rb | 4 +- spec/api_map/config_spec.rb | 2 + spec/api_map/source_to_yard_spec.rb | 16 +- spec/api_map/store_spec.rb | 16 +- spec/api_map_spec.rb | 18 +- spec/complex_type_spec.rb | 8 +- spec/convention/struct_definition_spec.rb | 2 + spec/convention_spec.rb | 2 + spec/diagnostics/base_spec.rb | 4 +- spec/diagnostics/require_not_found_spec.rb | 4 +- spec/diagnostics/rubocop_helpers_spec.rb | 10 +- spec/diagnostics/rubocop_spec.rb | 8 +- spec/diagnostics/type_check_spec.rb | 16 +- spec/diagnostics/update_errors_spec.rb | 6 +- spec/diagnostics_spec.rb | 8 +- spec/doc_map_spec.rb | 4 +- spec/language_server/host/diagnoser_spec.rb | 4 +- spec/language_server/host/dispatch_spec.rb | 4 +- .../host/message_worker_spec.rb | 4 +- spec/language_server/host_spec.rb | 46 ++-- .../message/completion_item/resolve_spec.rb | 14 +- .../extended/check_gem_version_spec.rb | 12 +- .../message/initialize_spec.rb | 92 +++---- .../message/text_document/definition_spec.rb | 52 ++-- .../message/text_document/formatting_spec.rb | 4 +- .../message/text_document/hover_spec.rb | 46 ++-- .../message/text_document/rename_spec.rb | 112 ++++----- .../text_document/type_definition_spec.rb | 24 +- .../did_change_configuration_spec.rb | 8 +- .../did_change_watched_files_spec.rb | 90 +++---- spec/language_server/message_spec.rb | 6 +- spec/language_server/protocol_spec.rb | 6 +- .../language_server/transport/adapter_spec.rb | 2 + .../transport/data_reader_spec.rb | 4 +- spec/language_server/uri_helpers_spec.rb | 14 +- spec/library_spec.rb | 86 +++---- spec/logging_spec.rb | 8 +- spec/parser/node_chainer_spec.rb | 2 + spec/parser/node_methods_spec.rb | 112 +++++---- spec/parser/node_processor_spec.rb | 18 +- spec/parser_spec.rb | 4 +- spec/pin/base_spec.rb | 30 +-- spec/pin/base_variable_spec.rb | 2 + spec/pin/block_spec.rb | 2 + spec/pin/class_variable_spec.rb | 2 + spec/pin/constant_spec.rb | 2 + spec/pin/delegated_method_spec.rb | 6 +- spec/pin/documenting_spec.rb | 2 + spec/pin/instance_variable_spec.rb | 6 +- spec/pin/keyword_spec.rb | 4 +- spec/pin/local_variable_spec.rb | 2 + spec/pin/method_spec.rb | 56 +++-- spec/pin/namespace_spec.rb | 14 +- spec/pin/parameter_spec.rb | 8 +- spec/pin/search_spec.rb | 8 +- spec/pin/symbol_spec.rb | 20 +- spec/position_spec.rb | 34 +-- spec/rbs_map/conversions_spec.rb | 4 +- spec/rbs_map/core_map_spec.rb | 12 +- spec/rbs_map/stdlib_map_spec.rb | 10 +- spec/rbs_map_spec.rb | 14 +- spec/source/chain/array_spec.rb | 2 + spec/source/chain/call_spec.rb | 2 + spec/source/chain/class_variable_spec.rb | 4 +- spec/source/chain/constant_spec.rb | 2 + spec/source/chain/global_variable_spec.rb | 4 +- spec/source/chain/head_spec.rb | 4 +- spec/source/chain/instance_variable_spec.rb | 10 +- spec/source/chain/link_spec.rb | 2 + spec/source/chain/literal_spec.rb | 2 + spec/source/chain/or_spec.rb | 2 + spec/source/chain/q_call_spec.rb | 2 + spec/source/chain/z_super_spec.rb | 4 +- spec/source/change_spec.rb | 20 +- spec/source/source_chainer_spec.rb | 44 ++-- spec/source/updater_spec.rb | 8 +- spec/source_map/clip_spec.rb | 4 +- spec/source_map/mapper_spec.rb | 12 +- spec/source_map/node_processor_spec.rb | 2 + spec/source_map_spec.rb | 30 +-- spec/source_spec.rb | 38 +-- spec/spec_helper.rb | 2 + spec/type_checker/levels/normal_spec.rb | 12 +- spec/type_checker/levels/strict_spec.rb | 4 +- spec/type_checker/levels/strong_spec.rb | 2 + spec/type_checker/levels/typed_spec.rb | 2 + spec/type_checker/rules_spec.rb | 10 +- spec/type_checker_spec.rb | 8 +- spec/workspace/config_spec.rb | 14 +- spec/workspace_spec.rb | 12 +- spec/yard_map/mapper/to_method_spec.rb | 18 +- spec/yard_map/mapper_spec.rb | 4 +- 191 files changed, 1436 insertions(+), 1338 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 23837b6d5..7686ee277 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -32,16 +32,18 @@ Gemspec/RequiredRubyVersion: - 'spec/fixtures/rubocop-custom-version/specifications/rubocop-0.0.0.gemspec' - 'spec/fixtures/vendored/vendor/do_not_use.gemspec' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: RequireParenthesesForMethodChains. -Lint/AmbiguousRange: - Enabled: false +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith, Severity. +# SupportedStylesAlignWith: keyword, variable, start_of_line +Layout/EndAlignment: + Exclude: + - 'lib/solargraph/shell.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowSafeAssignment. -Lint/AssignmentInCondition: +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, AllowedPatterns. +Layout/IndentationWidth: Exclude: - - 'lib/solargraph/library.rb' + - 'lib/solargraph/shell.rb' Lint/BinaryOperatorWithIdenticalOperands: Exclude: @@ -82,8 +84,6 @@ Lint/EmptyFile: # This cop supports unsafe autocorrection (--autocorrect-all). Lint/InterpolationCheck: Exclude: - - 'spec/complex_type_spec.rb' - - 'spec/parser/node_methods_spec.rb' - 'spec/source/chain_spec.rb' - 'spec/source/cursor_spec.rb' @@ -96,20 +96,6 @@ Lint/MissingSuper: - 'lib/solargraph/source/chain/literal.rb' - 'lib/solargraph/source/chain/or.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -Lint/NonAtomicFileOperation: - Exclude: - - 'spec/diagnostics/rubocop_spec.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods. -# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? -# AdditionalNilMethods: present?, blank?, try, try! -Lint/RedundantSafeNavigation: - Exclude: - - 'lib/solargraph/api_map/source_to_yard.rb' - - 'lib/solargraph/rbs_map.rb' - # Configuration parameters: AllowKeywordBlockArguments. Lint/UnderscorePrefixedVariableName: Exclude: @@ -142,11 +128,6 @@ Lint/UselessConstantScoping: Exclude: - 'lib/solargraph/rbs_map/conversions.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -Lint/UselessMethodDefinition: - Exclude: - - 'lib/solargraph/pin/signature.rb' - # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Enabled: false @@ -211,12 +192,6 @@ Naming/HeredocDelimiterNaming: Exclude: - 'spec/yard_map/mapper/to_method_spec.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyleForLeadingUnderscores. -# SupportedStylesForLeadingUnderscores: disallowed, required, optional -Naming/MemoizedInstanceVariableName: - Enabled: false - # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: @@ -258,12 +233,6 @@ RSpec/Be: - 'spec/rbs_map/stdlib_map_spec.rb' - 'spec/source/source_chainer_spec.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -RSpec/BeEq: - Exclude: - - 'spec/complex_type_spec.rb' - - 'spec/pin/method_spec.rb' - RSpec/BeforeAfterAll: Exclude: - '**/spec/spec_helper.rb' @@ -292,12 +261,6 @@ RSpec/DescribeClass: - 'spec/complex_type_spec.rb' - 'spec/source_map/node_processor_spec.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. -# SupportedStyles: described_class, explicit -RSpec/DescribedClass: - Enabled: false - # Configuration parameters: Max, CountAsOne. RSpec/ExampleLength: Enabled: false @@ -341,19 +304,6 @@ RSpec/PendingWithoutReason: - 'spec/pin/local_variable_spec.rb' - 'spec/type_checker/levels/strict_spec.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers. -# SupportedStyles: inflected, explicit -RSpec/PredicateMatcher: - Exclude: - - 'spec/language_server/message/workspace/did_change_configuration_spec.rb' - - 'spec/source_spec.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -RSpec/ReceiveMessages: - Exclude: - - 'spec/language_server/host_spec.rb' - RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' @@ -381,13 +331,8 @@ Security/MarshalLoad: # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols, AllowModifiersOnAttrs, AllowModifiersOnAliasMethod. # SupportedStyles: inline, group Style/AccessModifierDeclarations: - Enabled: false - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, conditionals -Style/AndOr: - Enabled: false + Exclude: + - 'lib/solargraph/source/chain/literal.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames. @@ -398,48 +343,6 @@ Style/ArgumentsForwarding: Exclude: - 'lib/solargraph/complex_type.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: MinBranchesCount. -Style/CaseLikeIf: - Exclude: - - 'lib/solargraph/language_server/message/workspace/did_change_watched_files.rb' - - 'lib/solargraph/pin/parameter.rb' - - 'lib/solargraph/rbs_map/conversions.rb' - - 'lib/solargraph/source/source_chainer.rb' - - 'lib/solargraph/yard_map/mapper.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules. -# SupportedStyles: nested, compact -# SupportedStylesForClasses: ~, nested, compact -# SupportedStylesForModules: ~, nested, compact -Style/ClassAndModuleChildren: - Enabled: false - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedMethods, AllowedPatterns. -# AllowedMethods: ==, equal?, eql? -Style/ClassEqualityComparison: - Exclude: - - 'lib/solargraph/gem_pins.rb' - - 'lib/solargraph/pin/base.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedReceivers. -Style/CollectionCompact: - Exclude: - - 'lib/solargraph/pin/constant.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/CombinableLoops: - Exclude: - - 'lib/solargraph/pin/parameter.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/ConcatArrayLiterals: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition @@ -460,79 +363,22 @@ Style/EmptyMethod: - 'spec/fixtures/rdoc-lib/lib/example.rb' - 'spec/fixtures/workspace-with-gemfile/lib/thing.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv -Style/FloatDivision: - Exclude: - - 'lib/solargraph/library.rb' - - 'lib/solargraph/pin/search.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/GlobalStdStream: - Exclude: - - 'lib/solargraph/logging.rb' - - 'lib/solargraph/shell.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Exclude: - 'lib/solargraph/source_map/clip.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowSplatArgument. -Style/HashConversion: - Exclude: - - 'lib/solargraph/doc_map.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedReceivers. -# AllowedReceivers: Thread.current -Style/HashEachMethods: - Exclude: - - 'lib/solargraph/library.rb' - - 'lib/solargraph/source.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/IdenticalConditionalBranches: - Exclude: - - 'lib/solargraph/library.rb' - - 'lib/solargraph/type_checker.rb' - # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Enabled: false -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: InverseMethods, InverseBlocks. -Style/InverseMethods: - Exclude: - - 'lib/solargraph/source.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/MapIntoArray: - Exclude: - - 'lib/solargraph/diagnostics/update_errors.rb' - - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/MapToHash: - Exclude: - - 'lib/solargraph/bench.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/MapToSet: - Exclude: - - 'lib/solargraph/library.rb' - - 'spec/source_map/clip_spec.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline @@ -544,17 +390,14 @@ Style/MultilineBlockChain: Exclude: - 'lib/solargraph/pin/search.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: literals, strict -Style/MutableConstant: - Enabled: false - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: - Enabled: false + Exclude: + - 'spec/**/*' + - 'lib/solargraph/language_server/message/extended/check_gem_version.rb' + - 'lib/solargraph/language_server/message/extended/document_gems.rb' Style/OpenStructUse: Exclude: @@ -565,30 +408,12 @@ Style/OpenStructUse: Style/OptionalBooleanParameter: Enabled: false -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: short, verbose -Style/PreferredHashMethods: - Exclude: - - 'lib/solargraph/language_server/message.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: Methods. -Style/RedundantArgument: - Exclude: - - 'lib/solargraph/source_map/mapper.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/RedundantInterpolation: - Exclude: - - 'lib/solargraph/parser/parser_gem/node_chainer.rb' - - 'lib/solargraph/source_map/mapper.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: - Enabled: false + Exclude: + - 'lib/solargraph/pin/base.rb' # Configuration parameters: Max. Style/SafeNavigationChainLength: @@ -597,12 +422,8 @@ Style/SafeNavigationChainLength: # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: - Enabled: false - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: Mode. -Style/StringConcatenation: - Enabled: false + Exclude: + - 'lib/solargraph/convention/struct_definition/struct_definition_node.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. @@ -616,21 +437,6 @@ Style/SuperArguments: Exclude: - 'lib/solargraph/pin/callable.rb' - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/pin/signature.rb' - -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. -# AllowedMethods: define_method -Style/SymbolProc: - Enabled: false - -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/ZeroLengthPredicate: - Exclude: - - 'lib/solargraph/language_server/host.rb' - - 'lib/solargraph/pin/method.rb' - - 'lib/solargraph/source/chain/array.rb' - - 'spec/language_server/protocol_spec.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStylePrototypeName. diff --git a/Gemfile b/Gemfile index 6849d21b5..fafce06ec 100755 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec name: 'solargraph' diff --git a/Rakefile b/Rakefile index cbb5a9da1..71c82e4dc 100755 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rake' require 'bundler/gem_tasks' require 'fileutils' diff --git a/bin/solargraph b/bin/solargraph index 8f47bbda6..cd5341820 100755 --- a/bin/solargraph +++ b/bin/solargraph @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # turn off warning diagnostics from Ruby $VERBOSE = nil diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 118baab5e..66f68533f 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -127,13 +127,6 @@ def catalog bench self end - # @todo need to model type def statement in chains as a symbol so - # that this overload of 'protected' will typecheck @sg-ignore - # @sg-ignore - protected def equality_fields - [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs, @loose_unions] - end - # @return [DocMap] def doc_map @doc_map ||= DocMap.new([], Workspace.new('.')) @@ -505,7 +498,7 @@ def get_complex_type_methods complex_type, context = '', internal = false result = Set.new complex_type.each do |type| if type.duck_type? - result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map) + result.add Pin::DuckMethod.new(name: type.to_s[1..], source: :api_map) result.merge get_methods('Object') else unless type.nil? || type.name == 'void' @@ -1013,5 +1006,15 @@ def has_generics? namespace_pin def can_resolve_generics? namespace_pin, rooted_type has_generics?(namespace_pin) && !rooted_type.all_params.empty? end + + protected + + # @todo need to model type def statement in chains as a symbol so + # that this overload of 'protected' will typecheck @sg-ignore + # @sg-ignore + def equality_fields + [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs, + @loose_unions] + end end end diff --git a/lib/solargraph/api_map/index.rb b/lib/solargraph/api_map/index.rb index 3d7405e0f..881d048a6 100644 --- a/lib/solargraph/api_map/index.rb +++ b/lib/solargraph/api_map/index.rb @@ -168,7 +168,7 @@ def map_overrides # YARD::Docstring#delete_tags: name expected String, # received String, Symbol - delete_tags is ok with a # _ToS, but we should fix anyway - new_pin.docstring.delete_tags tag if new_pin + new_pin&.docstring&.delete_tags tag end ovr.tags.each do |tag| pin.docstring.add_tag(tag) diff --git a/lib/solargraph/api_map/source_to_yard.rb b/lib/solargraph/api_map/source_to_yard.rb index f971d285d..a121a348b 100644 --- a/lib/solargraph/api_map/source_to_yard.rb +++ b/lib/solargraph/api_map/source_to_yard.rb @@ -14,7 +14,7 @@ module SourceToYard # @return [generic, nil] def code_object_at path, klass = YARD::CodeObjects::Base obj = code_object_map[path] - obj if obj&.is_a?(klass) + obj if obj.is_a?(klass) end # @return [Array] diff --git a/lib/solargraph/api_map/store.rb b/lib/solargraph/api_map/store.rb index cca195337..29b58bb1a 100644 --- a/lib/solargraph/api_map/store.rb +++ b/lib/solargraph/api_map/store.rb @@ -32,7 +32,7 @@ def update *pinsets # @todo Fix this map @fqns_pins_map = nil - return catalog(pinsets) if changed == 0 + return catalog(pinsets) if changed.zero? # @sg-ignore Need to add nil check here pinsets[changed..].each_with_index do |pins, idx| diff --git a/lib/solargraph/bench.rb b/lib/solargraph/bench.rb index ed72c5a82..de50e3df0 100644 --- a/lib/solargraph/bench.rb +++ b/lib/solargraph/bench.rb @@ -32,8 +32,7 @@ def initialize source_maps: [], workspace: Workspace.new, live_map: nil, externa # @return [Hash{String => SourceMap}] def source_map_hash # @todo Work around #to_h bug in current Ruby head (3.5) with #map#to_h - @source_map_hash ||= source_maps.map { |s| [s.filename, s] } - .to_h + @source_map_hash ||= source_maps.to_h { |s| [s.filename, s] } end # @return [Set] diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 5b6af7126..940e8a972 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -29,10 +29,6 @@ def initialize types = [UniqueType::UNDEFINED] @items = items end - protected def equality_fields - [self.class, items] - end - # @param api_map [ApiMap] # @param context [String] # @return [ComplexType] @@ -398,6 +394,10 @@ def intersect_with intersection_type, api_map protected + def equality_fields + [self.class, items] + end + # @return [ComplexType] def reduce_object new_items = items.flat_map do |ut| @@ -458,7 +458,7 @@ def parse *strings, partial: false elsif char == '<' point_stack += 1 elsif char == '>' - if subtype_string.end_with?('=') && curly_stack > 0 + if subtype_string.end_with?('=') && curly_stack.positive? subtype_string += char elsif base.end_with?('=') raise ComplexTypeError, 'Invalid hash thing' unless key_types.nil? @@ -474,7 +474,7 @@ def parse *strings, partial: false subtype_string.clear next else - raise ComplexTypeError, "Invalid close in type #{type_string}" if point_stack == 0 + raise ComplexTypeError, "Invalid close in type #{type_string}" if point_stack.zero? point_stack -= 1 subtype_string += char end @@ -484,23 +484,23 @@ def parse *strings, partial: false elsif char == '}' curly_stack -= 1 subtype_string += char - raise ComplexTypeError, "Invalid close in type #{type_string}" if curly_stack < 0 + raise ComplexTypeError, "Invalid close in type #{type_string}" if curly_stack.negative? next elsif char == '(' paren_stack += 1 elsif char == ')' paren_stack -= 1 subtype_string += char - raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack < 0 + raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack.negative? next - elsif char == ',' && point_stack == 0 && curly_stack == 0 && paren_stack == 0 + elsif char == ',' && point_stack.zero? && curly_stack.zero? && paren_stack.zero? # types.push ComplexType.new([UniqueType.new(base.strip, subtype_string.strip)]) types.push UniqueType.parse(base.strip, subtype_string.strip) base.clear subtype_string.clear next end - if point_stack == 0 && curly_stack == 0 && paren_stack == 0 + if point_stack.zero? && curly_stack.zero? && paren_stack.zero? base.concat char else subtype_string.concat char diff --git a/lib/solargraph/complex_type/type_methods.rb b/lib/solargraph/complex_type/type_methods.rb index 465f36502..4670e34aa 100644 --- a/lib/solargraph/complex_type/type_methods.rb +++ b/lib/solargraph/complex_type/type_methods.rb @@ -54,11 +54,11 @@ def duck_type? # @return [Boolean] def nil_type? - @nil_type ||= (name.casecmp('nil') == 0) + @nil_type ||= name.casecmp('nil').zero? end def tuple? - @tuple_type ||= (name == 'Tuple') || (name == 'Array' && subtypes.length >= 1 && fixed_parameters?) + @tuple ||= (name == 'Tuple') || (name == 'Array' && subtypes.length >= 1 && fixed_parameters?) end def void? diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 202626141..393009596 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -11,10 +11,6 @@ class UniqueType attr_reader :all_params, :subtypes, :key_types - protected def equality_fields - [@name, @all_params, @subtypes, @key_types] - end - # Create a UniqueType with the specified name and an optional substring. # The substring is the parameter section of a parametrized type, e.g., # for the type `Array`, the name is `Array` and the substring is @@ -27,7 +23,7 @@ class UniqueType def self.parse name, substring = '', make_rooted: nil raise ComplexTypeError, "Illegal prefix: #{name}" if name.start_with?(':::') if name.start_with?('::') - name = name[2..-1] + name = name[2..] rooted = true elsif !can_root_name?(name) rooted = true @@ -46,7 +42,7 @@ def self.parse name, substring = '', make_rooted: nil # @sg-ignore Need to add nil check here parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0]) if parameters_type == :hash - unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType) + unless !subs.is_a?(ComplexType) && (subs.length == 2) && !subs[0].is_a?(UniqueType) && !subs[1].is_a?(UniqueType) raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" end @@ -454,7 +450,7 @@ def resolve_generics definitions, context_type idx = definitions.generics.index(generic_name) next t if idx.nil? if context_type.parameters_type == :hash - if idx == 0 + if idx.zero? next ComplexType.new(context_type.key_types) elsif idx == 1 next ComplexType.new(context_type.subtypes) @@ -462,7 +458,7 @@ def resolve_generics definitions, context_type next ComplexType::UNDEFINED end elsif context_type.all?(&:implicit_union?) - if idx == 0 && !context_type.all_params.empty? + if idx.zero? && !context_type.all_params.empty? ComplexType.new(context_type.all_params) else ComplexType::UNDEFINED @@ -636,6 +632,12 @@ def self.can_root_name? name }.freeze include Logging + + protected + + def equality_fields + [@name, @all_params, @subtypes, @key_types] + end end end end diff --git a/lib/solargraph/convention/data_definition/data_definition_node.rb b/lib/solargraph/convention/data_definition/data_definition_node.rb index d14817933..5c4b2276a 100644 --- a/lib/solargraph/convention/data_definition/data_definition_node.rb +++ b/lib/solargraph/convention/data_definition/data_definition_node.rb @@ -85,7 +85,7 @@ def data_node # @return [Array] def data_attribute_nodes # @sg-ignore Need to add nil check here - data_node.children[2..-1] + data_node.children[2..] end end end diff --git a/lib/solargraph/convention/gemfile.rb b/lib/solargraph/convention/gemfile.rb index 8484f6247..bcebe1de9 100644 --- a/lib/solargraph/convention/gemfile.rb +++ b/lib/solargraph/convention/gemfile.rb @@ -5,7 +5,7 @@ module Convention class Gemfile < Base def local source_map return EMPTY_ENVIRON unless File.basename(source_map.filename) == 'Gemfile' - @environ ||= Environ.new( + @local ||= Environ.new( requires: ['bundler'], domains: ['Bundler::Dsl'] ) diff --git a/lib/solargraph/convention/gemspec.rb b/lib/solargraph/convention/gemspec.rb index 66357ecbb..ce4ce78c7 100644 --- a/lib/solargraph/convention/gemspec.rb +++ b/lib/solargraph/convention/gemspec.rb @@ -5,7 +5,7 @@ module Convention class Gemspec < Base def local source_map return Convention::Base::EMPTY_ENVIRON unless File.basename(source_map.filename).end_with?('.gemspec') - @environ ||= Environ.new( + @local ||= Environ.new( requires: ['rubygems'], pins: [ Solargraph::Pin::Reference::Override.from_comment( diff --git a/lib/solargraph/convention/rakefile.rb b/lib/solargraph/convention/rakefile.rb index 17be306ae..c5286bd54 100644 --- a/lib/solargraph/convention/rakefile.rb +++ b/lib/solargraph/convention/rakefile.rb @@ -7,7 +7,7 @@ def local source_map basename = File.basename(source_map.filename) return EMPTY_ENVIRON unless basename.end_with?('.rake') || basename == 'Rakefile' - @environ ||= Environ.new( + @local ||= Environ.new( requires: ['rake'], domains: ['Rake::DSL'] ) diff --git a/lib/solargraph/converters/dd.rb b/lib/solargraph/converters/dd.rb index 98085da56..31c9e4b0f 100644 --- a/lib/solargraph/converters/dd.rb +++ b/lib/solargraph/converters/dd.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'nokogiri' module ReverseMarkdown diff --git a/lib/solargraph/converters/dl.rb b/lib/solargraph/converters/dl.rb index 92785622a..c4401ce87 100644 --- a/lib/solargraph/converters/dl.rb +++ b/lib/solargraph/converters/dl.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ReverseMarkdown module Converters class Dl < Base diff --git a/lib/solargraph/converters/dt.rb b/lib/solargraph/converters/dt.rb index edd420678..c7027e2ae 100644 --- a/lib/solargraph/converters/dt.rb +++ b/lib/solargraph/converters/dt.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ReverseMarkdown module Converters class Dt < Base diff --git a/lib/solargraph/converters/misc.rb b/lib/solargraph/converters/misc.rb index e45b5bc9e..febacb9ee 100644 --- a/lib/solargraph/converters/misc.rb +++ b/lib/solargraph/converters/misc.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + ReverseMarkdown::Converters.register :tt, ReverseMarkdown::Converters::Code.new diff --git a/lib/solargraph/diagnostics/rubocop.rb b/lib/solargraph/diagnostics/rubocop.rb index 23dbbfe63..39b79d9b7 100644 --- a/lib/solargraph/diagnostics/rubocop.rb +++ b/lib/solargraph/diagnostics/rubocop.rb @@ -17,7 +17,7 @@ class Rubocop < Base 'warning' => Severities::WARNING, 'error' => Severities::ERROR, 'fatal' => Severities::ERROR - } + }.freeze # @param source [Solargraph::Source] # @param _api_map [Solargraph::ApiMap] diff --git a/lib/solargraph/diagnostics/rubocop_helpers.rb b/lib/solargraph/diagnostics/rubocop_helpers.rb index 52a205465..e97ca628e 100644 --- a/lib/solargraph/diagnostics/rubocop_helpers.rb +++ b/lib/solargraph/diagnostics/rubocop_helpers.rb @@ -52,7 +52,7 @@ def generate_options filename, code def fix_drive_letter path return path unless path.match(/^[a-z]:/) # @sg-ignore Need to add nil check here - path[0].upcase + path[1..-1] + path[0].upcase + path[1..] end # @todo This is a smelly way to redirect output, but the RuboCop specs do diff --git a/lib/solargraph/diagnostics/update_errors.rb b/lib/solargraph/diagnostics/update_errors.rb index 6da4c5835..c2ca02408 100644 --- a/lib/solargraph/diagnostics/update_errors.rb +++ b/lib/solargraph/diagnostics/update_errors.rb @@ -4,16 +4,12 @@ module Solargraph module Diagnostics class UpdateErrors < Base def diagnose source, api_map - result = [] - combine_ranges(source.code, source.error_ranges).each do |range| - result.push( - range: range.to_hash, + combine_ranges(source.code, source.error_ranges).map do |range| + { range: range.to_hash, severity: Diagnostics::Severities::ERROR, source: 'Solargraph', - message: 'Syntax error' - ) + message: 'Syntax error' } end - result end private diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index f3cdfa94a..2a55f55c0 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -136,9 +136,9 @@ def load_serialized_gem_pins out: @out with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v } # @sg-ignore Need better typing for Hash[] # @type [Array] - missing_paths = Hash[without_gemspecs].keys + missing_paths = without_gemspecs.to_h.keys # @type [Array] - gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies(out: out).to_a + gemspecs = with_gemspecs.to_h.values.flatten.compact + dependencies(out: out).to_a # if we are type checking a gem project, we should not include # pins from rbs or yard from that gem here - we use our own diff --git a/lib/solargraph/gem_pins.rb b/lib/solargraph/gem_pins.rb index 340746a8f..9be1e8eb7 100644 --- a/lib/solargraph/gem_pins.rb +++ b/lib/solargraph/gem_pins.rb @@ -14,7 +14,7 @@ class << self # @param pins [Array] # @return [Array] def self.combine_method_pins_by_path pins - method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method } + method_pins, alias_pins = pins.partition { |pin| pin.instance_of?(Pin::Method) } by_path = method_pins.group_by(&:path) by_path.transform_values! do |pins| GemPins.combine_method_pins(*pins) @@ -92,7 +92,7 @@ class << self # @param choices [Array] # @return [ComplexType] def best_return_type *choices - choices.find { |pin| pin.defined? } || choices.first || ComplexType::UNDEFINED + choices.find(&:defined?) || choices.first || ComplexType::UNDEFINED end end end diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index 42ef92c59..49d3ceae3 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -773,7 +773,7 @@ def check_diff uri, change source = sources.find(uri) return change if source.code.length + 1 != change['text'].length diffs = Diff::LCS.diff(source.code, change['text']) - return change if diffs.length.zero? || diffs.length > 1 || diffs.first.length > 1 + return change if diffs.empty? || diffs.length > 1 || diffs.first.length > 1 # @type [Diff::LCS::Change] diff = diffs.first.first return change unless diff.adding? && ['.', ':', '(', ',', ' '].include?(diff.element) diff --git a/lib/solargraph/language_server/message.rb b/lib/solargraph/language_server/message.rb index ab74c2eb4..170bfdb4f 100644 --- a/lib/solargraph/language_server/message.rb +++ b/lib/solargraph/language_server/message.rb @@ -38,7 +38,7 @@ def register path, message_class # @param path [String] # @return [Class] def select path - if method_map.has_key?(path) + if method_map.key?(path) method_map[path] elsif path.start_with?('$/') MethodNotImplemented diff --git a/lib/solargraph/language_server/message/base.rb b/lib/solargraph/language_server/message/base.rb index cc72d99b5..6b61101c4 100644 --- a/lib/solargraph/language_server/message/base.rb +++ b/lib/solargraph/language_server/message/base.rb @@ -69,7 +69,7 @@ def send_response } response[:result] = result unless result.nil? response[:error] = error unless error.nil? - response[:result] = nil if result.nil? and error.nil? + response[:result] = nil if result.nil? && error.nil? json = response.to_json envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}" Solargraph.logger.debug envelope diff --git a/lib/solargraph/language_server/message/completion_item/resolve.rb b/lib/solargraph/language_server/message/completion_item/resolve.rb index 171ec6a6c..83cc5c1fe 100644 --- a/lib/solargraph/language_server/message/completion_item/resolve.rb +++ b/lib/solargraph/language_server/message/completion_item/resolve.rb @@ -46,7 +46,7 @@ def join_docs pins pins.each do |pin| this_link = host.options['enablePages'] ? pin.link_documentation : pin.text_documentation result.push this_link if this_link && this_link != last_link && this_link != 'undefined' - result.push pin.documentation unless result.last && result.last.end_with?(pin.documentation) + result.push pin.documentation unless result.last&.end_with?(pin.documentation) last_link = this_link end result.join("\n\n") diff --git a/lib/solargraph/language_server/message/text_document/definition.rb b/lib/solargraph/language_server/message/text_document/definition.rb index f88583f48..1bac9b36c 100644 --- a/lib/solargraph/language_server/message/text_document/definition.rb +++ b/lib/solargraph/language_server/message/text_document/definition.rb @@ -1,43 +1,49 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class Definition < Base - def process - @line = params['position']['line'] - @column = params['position']['character'] - set_result(code_location || require_location || []) - end +module Solargraph + module LanguageServer + module Message + module TextDocument + class Definition < Base + def process + @line = params['position']['line'] + @column = params['position']['character'] + set_result(code_location || require_location || []) + end - private + private - # @return [Array, nil] - def code_location - suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) - # @sg-ignore Need to add nil check here - return nil if suggestions.empty? - # @sg-ignore Need to add nil check here - suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| - { - uri: file_to_uri(pin.best_location.filename), - range: pin.best_location.range.to_hash - } - end - end + # @return [Array, nil] + def code_location + suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) + # @sg-ignore Need to add nil check here + return nil if suggestions.empty? + # @sg-ignore Need to add nil check here + suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| + { + uri: file_to_uri(pin.best_location.filename), + range: pin.best_location.range.to_hash + } + end + end - # @return [Array, nil] - def require_location - # @todo Terrible hack - lib = host.library_for(params['textDocument']['uri']) - rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']), - Solargraph::Range.from_to(@line, @column, @line, @column)) - dloc = lib.locate_ref(rloc) - return nil if dloc.nil? - [ - { - uri: file_to_uri(dloc.filename), - range: dloc.range.to_hash - } - ] + # @return [Array, nil] + def require_location + # @todo Terrible hack + lib = host.library_for(params['textDocument']['uri']) + rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']), + Solargraph::Range.from_to(@line, @column, @line, @column)) + dloc = lib.locate_ref(rloc) + return nil if dloc.nil? + [ + { + uri: file_to_uri(dloc.filename), + range: dloc.range.to_hash + } + ] + end + end + end end end end diff --git a/lib/solargraph/language_server/message/text_document/document_highlight.rb b/lib/solargraph/language_server/message/text_document/document_highlight.rb index a0541762a..e1ecfcb29 100644 --- a/lib/solargraph/language_server/message/text_document/document_highlight.rb +++ b/lib/solargraph/language_server/message/text_document/document_highlight.rb @@ -1,17 +1,23 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class DocumentHighlight < Base - def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], - params['position']['character'], strip: true, only: true) - result = locs.map do |loc| - { - range: loc.range.to_hash, - kind: 1 - } +module Solargraph + module LanguageServer + module Message + module TextDocument + class DocumentHighlight < Base + def process + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character'], strip: true, only: true) + result = locs.map do |loc| + { + range: loc.range.to_hash, + kind: 1 + } + end + set_result result + end + end end - set_result result end end end diff --git a/lib/solargraph/language_server/message/text_document/document_symbol.rb b/lib/solargraph/language_server/message/text_document/document_symbol.rb index 3d3cccbde..04b706358 100644 --- a/lib/solargraph/language_server/message/text_document/document_symbol.rb +++ b/lib/solargraph/language_server/message/text_document/document_symbol.rb @@ -1,28 +1,36 @@ # frozen_string_literal: true -class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solargraph::LanguageServer::Message::Base - include Solargraph::LanguageServer::UriHelpers +module Solargraph + module LanguageServer + module Message + module TextDocument + class DocumentSymbol < Solargraph::LanguageServer::Message::Base + include Solargraph::LanguageServer::UriHelpers - def process - pins = host.document_symbols params['textDocument']['uri'] - info = pins.map do |pin| - next nil unless pin.best_location&.filename + def process + pins = host.document_symbols params['textDocument']['uri'] + info = pins.map do |pin| + next nil unless pin.best_location&.filename - result = { - name: pin.name, - containerName: pin.namespace, - kind: pin.symbol_kind, - location: { - # @sg-ignore Need to add nil check here - uri: file_to_uri(pin.best_location.filename), - # @sg-ignore Need to add nil check here - range: pin.best_location.range.to_hash - }, - deprecated: pin.deprecated? - } - result - end.compact + result = { + name: pin.name, + containerName: pin.namespace, + kind: pin.symbol_kind, + location: { + # @sg-ignore Need to add nil check here + uri: file_to_uri(pin.best_location.filename), + # @sg-ignore Need to add nil check here + range: pin.best_location.range.to_hash + }, + deprecated: pin.deprecated? + } + result + end.compact - set_result info + set_result info + end + end + end + end end end diff --git a/lib/solargraph/language_server/message/text_document/hover.rb b/lib/solargraph/language_server/message/text_document/hover.rb index 379403258..6b60969e1 100644 --- a/lib/solargraph/language_server/message/text_document/hover.rb +++ b/lib/solargraph/language_server/message/text_document/hover.rb @@ -42,7 +42,7 @@ def process def contents_or_nil contents stripped = contents .map(&:strip) - .reject { |c| c.empty? } + .reject(&:empty?) return nil if stripped.empty? { contents: { diff --git a/lib/solargraph/language_server/message/text_document/prepare_rename.rb b/lib/solargraph/language_server/message/text_document/prepare_rename.rb index 770d126ad..2f742047a 100644 --- a/lib/solargraph/language_server/message/text_document/prepare_rename.rb +++ b/lib/solargraph/language_server/message/text_document/prepare_rename.rb @@ -1,12 +1,18 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class PrepareRename < Base - def process - line = params['position']['line'] - col = params['position']['character'] - set_result host.sources.find(params['textDocument']['uri']).cursor_at(Solargraph::Position.new(line, - col)).range.to_hash +module Solargraph + module LanguageServer + module Message + module TextDocument + class PrepareRename < Base + def process + line = params['position']['line'] + col = params['position']['character'] + set_result host.sources.find(params['textDocument']['uri']).cursor_at(Solargraph::Position.new(line, + col)).range.to_hash + end + end + end end end end diff --git a/lib/solargraph/language_server/message/text_document/references.rb b/lib/solargraph/language_server/message/text_document/references.rb index 08ae35c9d..6a894ecd9 100644 --- a/lib/solargraph/language_server/message/text_document/references.rb +++ b/lib/solargraph/language_server/message/text_document/references.rb @@ -1,17 +1,23 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class References < Base - def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], - params['position']['character']) - result = locs.map do |loc| - { - uri: file_to_uri(loc.filename), - range: loc.range.to_hash - } +module Solargraph + module LanguageServer + module Message + module TextDocument + class References < Base + def process + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character']) + result = locs.map do |loc| + { + uri: file_to_uri(loc.filename), + range: loc.range.to_hash + } + end + set_result result + end + end end - set_result result end end end diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index 66bdce274..b0aff3c8a 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -1,20 +1,26 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class Rename < Base - def process - locs = host.references_from(params['textDocument']['uri'], params['position']['line'], - params['position']['character'], strip: true) - changes = {} - locs.each do |loc| - uri = file_to_uri(loc.filename) - changes[uri] ||= [] - changes[uri].push({ - range: loc.range.to_hash, - newText: params['newName'] - }) +module Solargraph + module LanguageServer + module Message + module TextDocument + class Rename < Base + def process + locs = host.references_from(params['textDocument']['uri'], params['position']['line'], + params['position']['character'], strip: true) + changes = {} + locs.each do |loc| + uri = file_to_uri(loc.filename) + changes[uri] ||= [] + changes[uri].push({ + range: loc.range.to_hash, + newText: params['newName'] + }) + end + set_result changes: changes + end + end end - set_result changes: changes end end end diff --git a/lib/solargraph/language_server/message/text_document/signature_help.rb b/lib/solargraph/language_server/message/text_document/signature_help.rb index 5a460b205..675daaebe 100644 --- a/lib/solargraph/language_server/message/text_document/signature_help.rb +++ b/lib/solargraph/language_server/message/text_document/signature_help.rb @@ -10,7 +10,7 @@ def process col = params['position']['character'] suggestions = host.signatures_at(params['textDocument']['uri'], line, col) set_result({ - signatures: suggestions.flat_map { |pin| pin.signature_help } + signatures: suggestions.flat_map(&:signature_help) }) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" diff --git a/lib/solargraph/language_server/message/text_document/type_definition.rb b/lib/solargraph/language_server/message/text_document/type_definition.rb index 3a2431659..1035869c9 100644 --- a/lib/solargraph/language_server/message/text_document/type_definition.rb +++ b/lib/solargraph/language_server/message/text_document/type_definition.rb @@ -1,26 +1,32 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::TextDocument - class TypeDefinition < Base - def process - @line = params['position']['line'] - @column = params['position']['character'] - set_result(code_location || []) - end +module Solargraph + module LanguageServer + module Message + module TextDocument + class TypeDefinition < Base + def process + @line = params['position']['line'] + @column = params['position']['character'] + set_result(code_location || []) + end - private + private - # @return [Array, nil] - def code_location - suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column) - # @sg-ignore Need to add nil check here - return nil if suggestions.empty? - # @sg-ignore Need to add nil check here - suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| - { - uri: file_to_uri(pin.best_location.filename), - range: pin.best_location.range.to_hash - } + # @return [Array, nil] + def code_location + suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column) + # @sg-ignore Need to add nil check here + return nil if suggestions.empty? + # @sg-ignore Need to add nil check here + suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin| + { + uri: file_to_uri(pin.best_location.filename), + range: pin.best_location.range.to_hash + } + end + end + end end end end diff --git a/lib/solargraph/language_server/message/workspace/did_change_configuration.rb b/lib/solargraph/language_server/message/workspace/did_change_configuration.rb index 5a15ab0a5..2c9bd8d1c 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_configuration.rb @@ -1,35 +1,41 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::Workspace - class DidChangeConfiguration < Solargraph::LanguageServer::Message::Base - def process - return unless params['settings'] - update = params['settings']['solargraph'] - host.configure update - register_from_options - end +module Solargraph + module LanguageServer + module Message + module Workspace + class DidChangeConfiguration < Solargraph::LanguageServer::Message::Base + def process + return unless params['settings'] + update = params['settings']['solargraph'] + host.configure update + register_from_options + end - private + private - # @return [void] - def register_from_options - Solargraph.logger.debug "Registering capabilities from options: #{host.options.inspect}" - # @type [Array] - y = [] - # @type [Array] - n = [] - (host.options['completion'] ? y : n).push('textDocument/completion') - (host.options['hover'] ? y : n).push('textDocument/hover', 'textDocument/signatureHelp') - (host.options['autoformat'] ? y : n).push('textDocument/onTypeFormatting') - (host.options['formatting'] ? y : n).push('textDocument/formatting') - (host.options['symbols'] ? y : n).push('textDocument/documentSymbol', 'workspace/symbol') - (host.options['definitions'] ? y : n).push('textDocument/definition') - (host.options['typeDefinitions'] ? y : n).push('textDocument/typeDefinition') - (host.options['references'] ? y : n).push('textDocument/references') - (host.options['folding'] ? y : n).push('textDocument/folding') - (host.options['highlights'] ? y : n).push('textDocument/documentHighlight') - host.register_capabilities y - host.unregister_capabilities n + # @return [void] + def register_from_options + Solargraph.logger.debug "Registering capabilities from options: #{host.options.inspect}" + # @type [Array] + y = [] + # @type [Array] + n = [] + (host.options['completion'] ? y : n).push('textDocument/completion') + (host.options['hover'] ? y : n).push('textDocument/hover', 'textDocument/signatureHelp') + (host.options['autoformat'] ? y : n).push('textDocument/onTypeFormatting') + (host.options['formatting'] ? y : n).push('textDocument/formatting') + (host.options['symbols'] ? y : n).push('textDocument/documentSymbol', 'workspace/symbol') + (host.options['definitions'] ? y : n).push('textDocument/definition') + (host.options['typeDefinitions'] ? y : n).push('textDocument/typeDefinition') + (host.options['references'] ? y : n).push('textDocument/references') + (host.options['folding'] ? y : n).push('textDocument/folding') + (host.options['highlights'] ? y : n).push('textDocument/documentHighlight') + host.register_capabilities y + host.unregister_capabilities n + end + end + end end end end diff --git a/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb b/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb index 133cf883d..d44c24097 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb @@ -1,41 +1,48 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::Workspace - class DidChangeWatchedFiles < Solargraph::LanguageServer::Message::Base - CREATED = 1 - CHANGED = 2 - DELETED = 3 +module Solargraph + module LanguageServer + module Message + module Workspace + class DidChangeWatchedFiles < Solargraph::LanguageServer::Message::Base + CREATED = 1 + CHANGED = 2 + DELETED = 3 - include Solargraph::LanguageServer::UriHelpers + include Solargraph::LanguageServer::UriHelpers - def process - need_catalog = false - to_create = [] - to_delete = [] + def process + need_catalog = false + to_create = [] + to_delete = [] - # @param change [Hash] - params['changes'].each do |change| - if change['type'] == CREATED - to_create << change['uri'] - need_catalog = true - elsif change['type'] == CHANGED - next if host.open?(change['uri']) - to_create << change['uri'] - need_catalog = true - elsif change['type'] == DELETED - to_delete << change['uri'] - need_catalog = true - else - set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, - "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}" - end - end + # @param change [Hash] + params['changes'].each do |change| + case change['type'] + when CREATED + to_create << change['uri'] + need_catalog = true + when CHANGED + next if host.open?(change['uri']) + to_create << change['uri'] + need_catalog = true + when DELETED + to_delete << change['uri'] + need_catalog = true + else + set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, + "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}" + end + end - host.create(*to_create) - host.delete(*to_delete) + host.create(*to_create) + host.delete(*to_delete) - # Force host to catalog libraries after file changes (see castwide/solargraph#139) - host.catalog if need_catalog + # Force host to catalog libraries after file changes (see castwide/solargraph#139) + host.catalog if need_catalog + end + end + end end end end diff --git a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb index 8f93c02e7..7ba16b66c 100644 --- a/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +++ b/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb @@ -1,25 +1,31 @@ # frozen_string_literal: true -module Solargraph::LanguageServer::Message::Workspace - class DidChangeWorkspaceFolders < Solargraph::LanguageServer::Message::Base - def process - add_folders - remove_folders - end +module Solargraph + module LanguageServer + module Message + module Workspace + class DidChangeWorkspaceFolders < Solargraph::LanguageServer::Message::Base + def process + add_folders + remove_folders + end - private + private - # @return [void] - def add_folders - return unless params['event'] && params['event']['added'] - host.prepare_folders params['event']['added'] - end + # @return [void] + def add_folders + return unless params['event'] && params['event']['added'] + host.prepare_folders params['event']['added'] + end - # @return [void] - def remove_folders - return unless params['event'] && params['event']['removed'] - params['event']['removed'].each do |_folder| - host.remove_folders params['event']['removed'] + # @return [void] + def remove_folders + return unless params['event'] && params['event']['removed'] + params['event']['removed'].each do |_folder| + host.remove_folders params['event']['removed'] + end + end + end end end end diff --git a/lib/solargraph/language_server/message/workspace/workspace_symbol.rb b/lib/solargraph/language_server/message/workspace/workspace_symbol.rb index fe7e5efa5..8e8c884a7 100644 --- a/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +++ b/lib/solargraph/language_server/message/workspace/workspace_symbol.rb @@ -1,25 +1,33 @@ # frozen_string_literal: true -class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargraph::LanguageServer::Message::Base - include Solargraph::LanguageServer::UriHelpers +module Solargraph + module LanguageServer + module Message + module Workspace + class WorkspaceSymbol < Solargraph::LanguageServer::Message::Base + include Solargraph::LanguageServer::UriHelpers - def process - pins = host.query_symbols(params['query']) - info = pins.map do |pin| - # @sg-ignore Need to add nil check here - uri = file_to_uri(pin.best_location.filename) - { - name: pin.path, - containerName: pin.namespace, - kind: pin.symbol_kind, - location: { - uri: uri, - # @sg-ignore Need to add nil check here - range: pin.best_location.range.to_hash - }, - deprecated: pin.deprecated? - } + def process + pins = host.query_symbols(params['query']) + info = pins.map do |pin| + # @sg-ignore Need to add nil check here + uri = file_to_uri(pin.best_location.filename) + { + name: pin.path, + containerName: pin.namespace, + kind: pin.symbol_kind, + location: { + uri: uri, + # @sg-ignore Need to add nil check here + range: pin.best_location.range.to_hash + }, + deprecated: pin.deprecated? + } + end + set_result info + end + end + end end - set_result info end end diff --git a/lib/solargraph/language_server/request.rb b/lib/solargraph/language_server/request.rb index 2cc874613..f97367597 100644 --- a/lib/solargraph/language_server/request.rb +++ b/lib/solargraph/language_server/request.rb @@ -15,7 +15,7 @@ def initialize id, &block # @yieldreturn [generic] # @return [generic, nil] def process result - @block.call(result) unless @block.nil? + @block&.call(result) end # @return [void] diff --git a/lib/solargraph/language_server/transport/data_reader.rb b/lib/solargraph/language_server/transport/data_reader.rb index c24118f03..5145e41e6 100644 --- a/lib/solargraph/language_server/transport/data_reader.rb +++ b/lib/solargraph/language_server/transport/data_reader.rb @@ -57,7 +57,7 @@ def prepare_to_parse_message # @return [void] def parse_message_from_buffer msg = JSON.parse(@buffer) - @message_handler.call msg unless @message_handler.nil? + @message_handler&.call msg rescue JSON::ParserError => e Solargraph::Logging.logger.warn "Failed to parse request: #{e.message}" Solargraph::Logging.logger.debug "Buffer: #{@buffer}" diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 85d20a465..7e5b3623a 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -195,10 +195,10 @@ def definitions_at filename, line, column offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column)) # @sg-ignore Need to add nil check here # @type [MatchData, nil] - lft = source.code[0..offset - 1].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i) + lft = source.code[0..(offset - 1)].match(/\[[a-z0-9_:<, ]*?([a-z0-9_:]*)\z/i) # @sg-ignore Need to add nil check here # @type [MatchData, nil] - rgt = source.code[offset..-1].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i) + rgt = source.code[offset..].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i) if lft && rgt # @sg-ignore Need to add nil check here tag = (lft[1] + rgt[1]).sub(/:+$/, '') @@ -291,7 +291,7 @@ def references_from filename, line, column, strip: false, only: false end end # HACK: for language clients that exclude special characters from the start of variable names - if strip && match = cursor.word.match(/^[^a-z0-9_]+/i) + if strip && (match = cursor.word.match(/^[^a-z0-9_]+/i)) found.map! do |loc| Solargraph::Location.new(loc.filename, # @sg-ignore flow sensitive typing needs to handle if foo = bar @@ -543,7 +543,7 @@ def source_map_external_require_hash # @return [void] def find_external_requires source_map # @type [Set] - new_set = source_map.requires.map(&:name).to_set + new_set = source_map.requires.to_set(&:name) # return if new_set == source_map_external_require_hash[source_map.filename] _filenames = nil filenames = -> { _filenames ||= workspace.filenames.to_set } @@ -679,13 +679,11 @@ def report_cache_progress gem_name, pending 0 else # @sg-ignore flow sensitive typing needs better handling of ||= on lvars - ((finished.to_f / @total.to_f) * 100).to_i + ((finished.to_f / @total) * 100).to_i end - message = "#{gem_name}#{" (+#{pending})" if pending > 0}" + message = "#{gem_name}#{" (+#{pending})" if pending.positive?}" # " - if @cache_progress - @cache_progress.report(message, pct) - else + unless @cache_progress @cache_progress = LanguageServer::Progress.new('Caching gem') # If we don't send both a begin and a report, the progress notification # might get stuck in the status bar forever @@ -694,8 +692,8 @@ def report_cache_progress gem_name, pending changed notify_observers @cache_progress # @sg-ignore flow sensitive typing should be able to handle redefinition - @cache_progress.report(message, pct) end + @cache_progress.report(message, pct) changed notify_observers @cache_progress end @@ -710,11 +708,11 @@ def end_cache_progress # @return [void] def sync_catalog - return if @sync_count == 0 + return if @sync_count.zero? mutex.synchronize do logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}" - source_map_hash.values.each { |map| find_external_requires(map) } + source_map_hash.each_value { |map| find_external_requires(map) } api_map.catalog bench logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs" diff --git a/lib/solargraph/location.rb b/lib/solargraph/location.rb index 686b072aa..ed90b4a7e 100644 --- a/lib/solargraph/location.rb +++ b/lib/solargraph/location.rb @@ -23,10 +23,6 @@ def initialize filename, range @range = range end - protected def equality_fields - [filename, range] - end - # @param other [self] def <=> other return nil unless other.is_a?(Location) @@ -83,5 +79,11 @@ def == other def inspect "#<#{self.class} #{filename}, #{range.inspect}>" end + + protected + + def equality_fields + [filename, range] + end end end diff --git a/lib/solargraph/logging.rb b/lib/solargraph/logging.rb index 60ca51f17..d472438e2 100644 --- a/lib/solargraph/logging.rb +++ b/lib/solargraph/logging.rb @@ -10,7 +10,7 @@ module Logging 'warn' => Logger::WARN, 'info' => Logger::INFO, 'debug' => Logger::DEBUG - } + }.freeze configured_level = ENV.fetch('SOLARGRAPH_LOG', nil) level = if LOG_LEVELS.keys.include?(configured_level) LOG_LEVELS.fetch(configured_level) @@ -21,7 +21,7 @@ module Logging end DEFAULT_LOG_LEVEL end - @@logger = Logger.new(STDERR, level: level) + @@logger = Logger.new($stderr, level: level) # @sg-ignore Fix cvar issue @@logger.formatter = proc do |severity, _datetime, _progname, msg| "[#{severity}] #{msg}\n" @@ -45,7 +45,7 @@ def logger @@logger else new_log_level = LOG_LEVELS[log_level.to_s] - logger = Logger.new(STDERR, level: new_log_level) + logger = Logger.new($stderr, level: new_log_level) # @sg-ignore Wrong argument type for Logger#formatter=: arg_0 # expected nil, received Logger::_Formatter, nil diff --git a/lib/solargraph/page.rb b/lib/solargraph/page.rb index 12782da90..207b84665 100644 --- a/lib/solargraph/page.rb +++ b/lib/solargraph/page.rb @@ -49,7 +49,7 @@ def ruby_to_html code # @param directory [String] def initialize directory = VIEWS_PATH - directory = VIEWS_PATH if directory.nil? or !File.directory?(directory) + directory = VIEWS_PATH if directory.nil? || !File.directory?(directory) directories = [directory] directories.push VIEWS_PATH if directory != VIEWS_PATH # @type [Proc] diff --git a/lib/solargraph/parser.rb b/lib/solargraph/parser.rb index 1f02befe7..7479f4fcc 100644 --- a/lib/solargraph/parser.rb +++ b/lib/solargraph/parser.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Parser autoload :CommentRipper, 'solargraph/parser/comment_ripper' diff --git a/lib/solargraph/parser/comment_ripper.rb b/lib/solargraph/parser/comment_ripper.rb index 41e68e744..89d4a2c91 100644 --- a/lib/solargraph/parser/comment_ripper.rb +++ b/lib/solargraph/parser/comment_ripper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'ripper' module Solargraph @@ -26,8 +28,8 @@ def on_comment *args # @sg-ignore Need to add nil check here if @buffer_lines[result[2][0]][0..result[2][1]].strip =~ /^#/ chomped = result[1].chomp - if result[2][0] == 0 && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, - replace: '').match(/^#\s*frozen_string_literal:/) + if result[2][0].zero? && chomped.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, + replace: '').match(/^#\s*frozen_string_literal:/) chomped = '#' end @comments[result[2][0]] = diff --git a/lib/solargraph/parser/flow_sensitive_typing.rb b/lib/solargraph/parser/flow_sensitive_typing.rb index 26fb646b4..32e4866d1 100644 --- a/lib/solargraph/parser/flow_sensitive_typing.rb +++ b/lib/solargraph/parser/flow_sensitive_typing.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Parser class FlowSensitiveTyping diff --git a/lib/solargraph/parser/parser_gem.rb b/lib/solargraph/parser/parser_gem.rb index 857e17768..3aca3804f 100644 --- a/lib/solargraph/parser/parser_gem.rb +++ b/lib/solargraph/parser/parser_gem.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Parser module ParserGem diff --git a/lib/solargraph/parser/parser_gem/node_chainer.rb b/lib/solargraph/parser/parser_gem/node_chainer.rb index 5803424d0..be6e287cf 100644 --- a/lib/solargraph/parser/parser_gem/node_chainer.rb +++ b/lib/solargraph/parser/parser_gem/node_chainer.rb @@ -63,10 +63,9 @@ def generate_links n result.concat generate_links(n.children[0]) result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n)) elsif n.children[0].nil? - args = [] # @sg-ignore Need to add nil check here - n.children[2..-1].each do |c| - args.push NodeChainer.chain(c, @filename, n) + n.children[2..].map do |c| + NodeChainer.chain(c, @filename, n) end result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n)) else @@ -142,7 +141,7 @@ def generate_links n # added in Ruby 3.1 - https://bugs.ruby-lang.org/issues/11256 result.push Chain::BlockVariable.new(nil) elsif block_variable_name_node.type == :sym - result.push Chain::BlockSymbol.new("#{block_variable_name_node.children[0]}") + result.push Chain::BlockSymbol.new(block_variable_name_node.children[0].to_s) else result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0]}") end @@ -182,7 +181,7 @@ def passed_block node # @return [Array] def node_args node # @sg-ignore Need to add nil check here - node.children[2..-1].map do |child| + node.children[2..].map do |child| NodeChainer.chain(child, @filename, node) end end diff --git a/lib/solargraph/parser/parser_gem/node_methods.rb b/lib/solargraph/parser/parser_gem/node_methods.rb index c73af64dc..e3391175a 100644 --- a/lib/solargraph/parser/parser_gem/node_methods.rb +++ b/lib/solargraph/parser/parser_gem/node_methods.rb @@ -163,15 +163,15 @@ def call_nodes_from node result.push node if Parser.is_ast_node?(node.children[0]) && node.children[0].children.length > 2 # @sg-ignore Need to add nil check here - node.children[0].children[2..-1].each { |child| result.concat call_nodes_from(child) } + node.children[0].children[2..].each { |child| result.concat call_nodes_from(child) } end # @sg-ignore Need to add nil check here - node.children[1..-1].each { |child| result.concat call_nodes_from(child) } + node.children[1..].each { |child| result.concat call_nodes_from(child) } elsif node.type == :send result.push node result.concat call_nodes_from(node.children.first) # @sg-ignore Need to add nil check here - node.children[2..-1].each { |child| result.concat call_nodes_from(child) } + node.children[2..].each { |child| result.concat call_nodes_from(child) } elsif %i[super zsuper].include?(node.type) result.push node node.children.each { |child| result.concat call_nodes_from(child) } @@ -218,7 +218,7 @@ def find_recipient_node cursor offset = cursor.offset tree = if source.synchronized? # @sg-ignore Need to add nil check here - match = source.code[0..offset - 1].match(/,\s*\z/) + match = source.code[0..(offset - 1)].match(/,\s*\z/) if match # @sg-ignore Need to add nil check here source.tree_at(position.line, position.column - match[0].length) @@ -232,14 +232,14 @@ def find_recipient_node cursor prev = nil tree.each do |node| if node.type == :send - args = node.children[2..-1] + args = node.children[2..] # @sg-ignore Need to add nil check here if !args.empty? # @sg-ignore Need to add nil check here return node if prev && args.include?(prev) elsif source.synchronized? - return node if source.code[0..offset - 1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/ - elsif source.code[0..offset - 1] =~ /\([^(]*\z/ + return node if source.code[0..(offset - 1)] =~ /\(\s*\z/ && source.code[offset..] =~ /^\s*\)/ + elsif source.code[0..(offset - 1)] =~ /\([^(]*\z/ return node end end @@ -310,13 +310,13 @@ def repaired_find_recipient_node cursor # statements in value positions. module DeepInference class << self - CONDITIONAL_ALL_BUT_FIRST = %i[if unless] - ONLY_ONE_CHILD = [:return] - FIRST_TWO_CHILDREN = [:rescue] - COMPOUND_STATEMENTS = %i[begin kwbegin] - SKIPPABLE = %i[def defs class sclass module] - FUNCTION_VALUE = [:block] - CASE_STATEMENT = [:case] + CONDITIONAL_ALL_BUT_FIRST = %i[if unless].freeze + ONLY_ONE_CHILD = [:return].freeze + FIRST_TWO_CHILDREN = [:rescue].freeze + COMPOUND_STATEMENTS = %i[begin kwbegin].freeze + SKIPPABLE = %i[def defs class sclass module].freeze + FUNCTION_VALUE = [:block].freeze + CASE_STATEMENT = [:case].freeze # @param node [AST::Node] a method body compound statement # @param include_explicit_returns [Boolean] If true, @@ -356,7 +356,7 @@ def from_value_position_statement node, include_explicit_returns: true result.concat from_value_position_compound_statement node elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type) # @sg-ignore Need to add nil check here - result.concat reduce_to_value_nodes(node.children[1..-1]) + result.concat reduce_to_value_nodes(node.children[1..]) # result.push NIL_NODE unless node.children[2] elsif ONLY_ONE_CHILD.include?(node.type) result.concat reduce_to_value_nodes([node.children[0]]) @@ -373,7 +373,7 @@ def from_value_position_statement node, include_explicit_returns: true end elsif CASE_STATEMENT.include?(node.type) # @sg-ignore Need to add nil check here - node.children[1..-1].each do |cc| + node.children[1..].each do |cc| if cc.nil? result.push NIL_NODE elsif cc.type == :when @@ -478,7 +478,7 @@ def reduce_to_value_nodes nodes # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type) # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check - result.concat reduce_to_value_nodes(node.children[1..-1]) + result.concat reduce_to_value_nodes(node.children[1..]) # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check elsif node.type == :return # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check diff --git a/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb b/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb index 2c8ef1c7b..2d3d967cc 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb @@ -24,7 +24,7 @@ def process if sclass.children[0].nil? && names.last != sclass.children[1].to_s names << sclass.children[1].to_s else - names.concat [NodeMethods.unpack_name(sclass.children[0]), sclass.children[1].to_s] + names.push NodeMethods.unpack_name(sclass.children[0]), sclass.children[1].to_s end name = names.reject(&:empty?).join('::') closure = Solargraph::Pin::Namespace.new(name: name, location: region.closure.location, source: :parser) diff --git a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb index 81404fcbf..a9e60cb65 100644 --- a/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +++ b/lib/solargraph/parser/parser_gem/node_processors/send_node.rb @@ -59,7 +59,7 @@ def process def process_visibility if node.children.length > 2 # @sg-ignore Need to add nil check here - node.children[2..-1].each do |child| + node.children[2..].each do |child| # @sg-ignore Variable type could not be inferred for method_name # @type [Symbol] visibility = node.children[1] @@ -90,7 +90,7 @@ def process_visibility # @return [void] def process_attribute # @sg-ignore Need to add nil check here - node.children[2..-1].each do |a| + node.children[2..].each do |a| loc = get_node_location(node) clos = region.closure cmnt = comments_for(node) @@ -132,7 +132,7 @@ def process_include return unless node.children[2].is_a?(AST::Node) && node.children[2].type == :const cp = region.closure # @sg-ignore Need to add nil check here - node.children[2..-1].each do |i| + node.children[2..].each do |i| type = region.scope == :class ? Pin::Reference::Extend : Pin::Reference::Include pins.push type.new( location: get_node_location(i), @@ -148,7 +148,7 @@ def process_prepend return unless node.children[2].is_a?(AST::Node) && node.children[2].type == :const cp = region.closure # @sg-ignore Need to add nil check here - node.children[2..-1].each do |i| + node.children[2..].each do |i| pins.push Pin::Reference::Prepend.new( location: get_node_location(i), closure: cp, @@ -161,7 +161,7 @@ def process_prepend # @return [void] def process_extend # @sg-ignore Need to add nil check here - node.children[2..-1].each do |i| + node.children[2..].each do |i| loc = get_node_location(node) if i.type == :self pins.push Pin::Reference::Extend.new( @@ -202,7 +202,7 @@ def process_module_function region.instance_variable_set(:@visibility, :module_function) elsif %i[sym str].include?(node.children[2].type) # @sg-ignore Need to add nil check here - node.children[2..-1].each do |x| + node.children[2..].each do |x| cn = x.children[0].to_s # @type [Pin::Method, nil] ref = pins.find { |p| p.is_a?(Pin::Method) && p.namespace == region.closure.full_context.namespace && p.name == cn } @@ -265,7 +265,7 @@ def process_private_constant Solargraph::Pin::Constant].include?(p.class) && p.namespace == region.closure.full_context.namespace && p.name == cn end.first # HACK: Smelly instance variable access - ref.instance_variable_set(:@visibility, :private) unless ref.nil? + ref&.instance_variable_set(:@visibility, :private) end # @return [void] @@ -288,7 +288,7 @@ def process_private_class_method p.is_a?(Pin::Method) && p.namespace == region.closure.full_context.namespace && p.name == node.children[2].children[0].to_s end.first # HACK: Smelly instance variable access - ref.instance_variable_set(:@visibility, :private) unless ref.nil? + ref&.instance_variable_set(:@visibility, :private) false else process_children region.update(scope: :class, visibility: :private) diff --git a/lib/solargraph/parser/snippet.rb b/lib/solargraph/parser/snippet.rb index 1ea6bd6d9..7282a44d6 100644 --- a/lib/solargraph/parser/snippet.rb +++ b/lib/solargraph/parser/snippet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Parser class Snippet diff --git a/lib/solargraph/pin/base.rb b/lib/solargraph/pin/base.rb index 2b0ac5cbb..8893ec1d5 100644 --- a/lib/solargraph/pin/base.rb +++ b/lib/solargraph/pin/base.rb @@ -195,11 +195,6 @@ def needs_consistent_name? true end - # @sg-ignore def should infer as symbol - "Not enough arguments to Module#protected" - protected def equality_fields - [name, location, type_location, closure, source] - end - # @param other [self] # @return [ComplexType] def combine_return_type other @@ -217,7 +212,7 @@ def combine_return_type other return_type else all_items = return_type.items + other.return_type.items - if all_items.any? { |item| item.selfy? } && all_items.any? do |item| + if all_items.any?(&:selfy?) && all_items.any? do |item| item.rooted_tag == context.reduce_class_type.rooted_tag end # assume this was a declaration that should have said 'self' @@ -440,7 +435,7 @@ def transform_types &transform # @param context_type [ComplexType] The receiver type # @return [self] def resolve_generics definitions, context_type - transform_types { |t| t.resolve_generics(definitions, context_type) if t } + transform_types { |t| t&.resolve_generics(definitions, context_type) } end def all_rooted? @@ -492,7 +487,7 @@ def best_location # @param other [Solargraph::Pin::Base, Object] # @return [Boolean] def nearly? other - self.class == other.class && + instance_of?(other.class) && # @sg-ignore Translate to something flow sensitive typing understands name == other.name && # @sg-ignore flow sensitive typing needs to handle attrs @@ -655,7 +650,7 @@ def type_desc rbs = return_type.rooted_tags if return_type.name == 'Class' if path if rbs - path + ' ' + rbs + "#{path} #{rbs}" else path end @@ -696,6 +691,11 @@ def all_location_text protected + # @sg-ignore def should infer as symbol - "Not enough arguments to Module#protected" + def equality_fields + [name, location, type_location, closure, source] + end + # @return [Boolean] attr_writer :probed @@ -753,7 +753,7 @@ def compare_directives dir1, dir2 # @param tag2 [YARD::Tags::Tag] # @return [Boolean] def compare_tags tag1, tag2 - tag1.class == tag2.class && + tag1.instance_of?(tag2.class) && tag1.tag_name == tag2.tag_name && tag1.text == tag2.text && tag1.name == tag2.name && diff --git a/lib/solargraph/pin/breakable.rb b/lib/solargraph/pin/breakable.rb index 7cf6df9ab..f5e4bfd4a 100644 --- a/lib/solargraph/pin/breakable.rb +++ b/lib/solargraph/pin/breakable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Pin # Mix-in for pins which enclose code which the 'break' statement diff --git a/lib/solargraph/pin/callable.rb b/lib/solargraph/pin/callable.rb index 937237f7c..824bfa91c 100644 --- a/lib/solargraph/pin/callable.rb +++ b/lib/solargraph/pin/callable.rb @@ -264,13 +264,11 @@ def mandatory_positional_param_count # @return [String] def parameters_to_rbs # @sg-ignore Need to add nil check here - rbs_generics + '(' + parameters.map { |param| - param.to_rbs - }.join(', ') + ') ' + (block.nil? ? '' : '{ ' + block.to_rbs + ' } ') + "#{rbs_generics}(#{parameters.map(&:to_rbs).join(', ')}) #{"{ #{block.to_rbs} } " unless block.nil?}" end def to_rbs - parameters_to_rbs + '-> ' + (return_type&.to_rbs || 'untyped') + "#{parameters_to_rbs}-> #{return_type&.to_rbs || 'untyped'}" end def block? diff --git a/lib/solargraph/pin/closure.rb b/lib/solargraph/pin/closure.rb index b01d760df..4b5738cfc 100644 --- a/lib/solargraph/pin/closure.rb +++ b/lib/solargraph/pin/closure.rb @@ -62,7 +62,7 @@ def to_rbs def rbs_generics return '' if generics.empty? - '[' + generics.map { |gen| gen.to_s }.join(', ') + '] ' + "[#{generics.map(&:to_s).join(', ')}] " end end end diff --git a/lib/solargraph/pin/compound_statement.rb b/lib/solargraph/pin/compound_statement.rb index 4598d677a..fb98e3570 100644 --- a/lib/solargraph/pin/compound_statement.rb +++ b/lib/solargraph/pin/compound_statement.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Pin # A series of statements where if a given statement executes, /all diff --git a/lib/solargraph/pin/constant.rb b/lib/solargraph/pin/constant.rb index 8da79f682..a7696fd1f 100644 --- a/lib/solargraph/pin/constant.rb +++ b/lib/solargraph/pin/constant.rb @@ -33,8 +33,8 @@ def path # @return [ComplexType] def generate_complex_type - tags = docstring.tags(:return).map(&:types).flatten.reject(&:nil?) - tags = docstring.tags(:type).map(&:types).flatten.reject(&:nil?) if tags.empty? + tags = docstring.tags(:return).map(&:types).flatten.compact + tags = docstring.tags(:type).map(&:types).flatten.compact if tags.empty? return ComplexType::UNDEFINED if tags.empty? ComplexType.try_parse(*tags) end diff --git a/lib/solargraph/pin/conversions.rb b/lib/solargraph/pin/conversions.rb index 25d585ea0..0d45741d4 100644 --- a/lib/solargraph/pin/conversions.rb +++ b/lib/solargraph/pin/conversions.rb @@ -44,7 +44,7 @@ def completion_item path: path, return_type: return_type.tag, # @sg-ignore flow sensitive typing needs to handle attrs - location: (location ? location.to_hash : nil), + location: location&.to_hash, deprecated: deprecated? } } diff --git a/lib/solargraph/pin/delegated_method.rb b/lib/solargraph/pin/delegated_method.rb index 83273705f..7e17f5042 100644 --- a/lib/solargraph/pin/delegated_method.rb +++ b/lib/solargraph/pin/delegated_method.rb @@ -115,7 +115,7 @@ def resolve_method api_map def print_chain chain out = +'' chain.links.each_with_index do |link, index| - if index > 0 + if index.positive? if Source::Chain::Constant out << '::' unless link.word.start_with?('::') else diff --git a/lib/solargraph/pin/local_variable.rb b/lib/solargraph/pin/local_variable.rb index 580775bd7..077da21be 100644 --- a/lib/solargraph/pin/local_variable.rb +++ b/lib/solargraph/pin/local_variable.rb @@ -6,7 +6,7 @@ class LocalVariable < BaseVariable # @param api_map [ApiMap] # @return [ComplexType, ComplexType::UniqueType] def probe api_map - if presence_certain? && return_type && return_type&.defined? + if presence_certain? && return_type&.defined? # flow sensitive typing has already figured out this type # has been downcast - use the type it figured out # @sg-ignore flow sensitive typing should support ivars @@ -24,7 +24,7 @@ def combine_with other, attrs = {} end def to_rbs - (name || '(anon)') + ' ' + (return_type&.to_rbs || 'untyped') + "#{name || '(anon)'} #{return_type&.to_rbs || 'untyped'}" end end end diff --git a/lib/solargraph/pin/method.rb b/lib/solargraph/pin/method.rb index 9ceec972f..b49c47a08 100644 --- a/lib/solargraph/pin/method.rb +++ b/lib/solargraph/pin/method.rb @@ -54,7 +54,7 @@ def combine_with other, attrs = {} return priority_choice unless priority_choice.nil? sigs = combine_signatures(other) - parameters = if sigs.length > 0 + parameters = if sigs.length.positive? [].freeze else choose(other, :parameters).clone.freeze @@ -172,7 +172,7 @@ def generate_signature parameters, return_type name: name, decl: decl, # @sg-ignore flow sensitive typing needs to handle attrs - presence: location ? location.range : nil, + presence: location&.range, return_type: ComplexType.try_parse(*p.types), source: source ) @@ -238,7 +238,7 @@ def detail def signature_help @signature_help ||= signatures.map do |sig| { - label: name + '(' + sig.parameters.map(&:full).join(', ') + ')', + label: "#{name}(#{sig.parameters.map(&:full).join(', ')})", documentation: documentation } end @@ -302,26 +302,26 @@ def documentation if @documentation.nil? method_docs ||= super || '' param_tags = docstring.tags(:param) - unless param_tags.nil? or param_tags.empty? + unless param_tags.nil? || param_tags.empty? method_docs += "\n\n" unless method_docs.empty? method_docs += "Params:\n" lines = [] param_tags.each do |p| l = "* #{p.name}" - l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty? + l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? || p.types.empty? l += " #{p.text}" lines.push l end method_docs += lines.join("\n") end yieldparam_tags = docstring.tags(:yieldparam) - unless yieldparam_tags.nil? or yieldparam_tags.empty? + unless yieldparam_tags.nil? || yieldparam_tags.empty? method_docs += "\n\n" unless method_docs.empty? method_docs += "Block Params:\n" lines = [] yieldparam_tags.each do |p| l = "* #{p.name}" - l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty? + l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? || p.types.empty? l += " #{p.text}" lines.push l end @@ -334,7 +334,7 @@ def documentation lines = [] yieldreturn_tags.each do |r| l = '*' - l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty? + l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? || r.types.empty? l += " #{r.text}" lines.push l end @@ -347,7 +347,7 @@ def documentation lines = [] return_tags.each do |r| l = '*' - l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty? + l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? || r.types.empty? l += " #{r.text}" lines.push l end @@ -403,7 +403,7 @@ def overloads name: name, decl: decl, # @sg-ignore flow sensitive typing needs to handle attrs - presence: location ? location.range : nil, + presence: location&.range, return_type: param_type_from_name(tag, src.first), source: :overloads ) @@ -698,9 +698,9 @@ def infer_from_iv api_map def parse_overload_param name # @todo this needs to handle mandatory vs not args, kwargs, blocks, etc if name.start_with?('**') - [name[2..-1], :kwrestarg] + [name[2..], :kwrestarg] elsif name.start_with?('*') - [name[1..-1], :restarg] + [name[1..], :restarg] else [name, :arg] end diff --git a/lib/solargraph/pin/namespace.rb b/lib/solargraph/pin/namespace.rb index f090716c8..55bb52b0e 100644 --- a/lib/solargraph/pin/namespace.rb +++ b/lib/solargraph/pin/namespace.rb @@ -27,7 +27,7 @@ def initialize type: :class, visibility: :public, gates: [''], name: '', **splat @type = type @visibility = visibility if name.start_with?('::') - name = name[2..-1] || '' + name = name[2..] || '' @closure = Solargraph::Pin::ROOT_PIN end @open_gates = gates @@ -40,7 +40,7 @@ def initialize type: :class, visibility: :public, gates: [''], name: '', **splat '' else # @sg-ignore Need to add nil check here - closure.full_context.namespace + '::' + "#{closure.full_context.namespace}::" end closure_name += parts.join('::') @closure = Pin::Namespace.new(name: closure_name, gates: [parts.join('::')], source: :namespace) diff --git a/lib/solargraph/pin/parameter.rb b/lib/solargraph/pin/parameter.rb index 4aa8f0e19..e307b655b 100644 --- a/lib/solargraph/pin/parameter.rb +++ b/lib/solargraph/pin/parameter.rb @@ -178,14 +178,15 @@ def return_type if @return_type.nil? @return_type = ComplexType::UNDEFINED found = param_tag - @return_type = ComplexType.try_parse(*found.types) unless found.nil? or found.types.nil? + @return_type = ComplexType.try_parse(*found.types) unless found.nil? || found.types.nil? # @sg-ignore flow sensitive typing should be able to handle redefinition if @return_type.undefined? - if decl == :restarg + case decl + when :restarg @return_type = ComplexType.try_parse('::Array') - elsif decl == :kwrestarg + when :kwrestarg @return_type = ComplexType.try_parse('::Hash') - elsif decl == :blockarg + when :blockarg @return_type = ComplexType.try_parse('::Proc') end end @@ -278,7 +279,7 @@ def typify_method_param api_map found = p break end - if found.nil? and !index.nil? && params[index] && (params[index].name.nil? || params[index].name.empty?) + if found.nil? && !index.nil? && params[index] && (params[index].name.nil? || params[index].name.empty?) found = params[index] end # @sg-ignore Need to add nil check here @@ -328,8 +329,6 @@ def resolve_reference ref, api_map, skip pins.each do |pin| params = pin.docstring.tags(:param) return params unless params.empty? - end - pins.each do |pin| params = see_reference(pin.docstring, api_map, skip) return params unless params.empty? end diff --git a/lib/solargraph/pin/search.rb b/lib/solargraph/pin/search.rb index 532b9774e..54e4d5bc5 100644 --- a/lib/solargraph/pin/search.rb +++ b/lib/solargraph/pin/search.rb @@ -54,7 +54,7 @@ def do_query # # @return [Float] def fuzzy_string_match str1, str2 - return 1.0 + (str2.length.to_f / str1.length.to_f) if str1.downcase.include?(str2.downcase) + return 1.0 + (str2.length.to_f / str1.length) if str1.downcase.include?(str2.downcase) JaroWinkler.similarity(str1, str2, ignore_case: true) end end diff --git a/lib/solargraph/pin/signature.rb b/lib/solargraph/pin/signature.rb index 5e6d72c9e..bf5147d6b 100644 --- a/lib/solargraph/pin/signature.rb +++ b/lib/solargraph/pin/signature.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph module Pin class Signature < Callable @@ -5,10 +7,6 @@ class Signature < Callable # to the method pin attr_writer :closure - def initialize **splat - super(**splat) - end - def generics # @type [Array<::String, nil>] @generics ||= [].freeze diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 5263ccb8f..68c3a49b0 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fileutils' require 'rbs' require 'rubygems' @@ -448,7 +450,7 @@ def uncache *path_segments, out: nil path = File.join(*path_segments) if File.exist?(path) FileUtils.rm_rf path, secure: true - out.puts "Clearing pin cache in #{path}" unless out.nil? + out&.puts "Clearing pin cache in #{path}" else out&.puts "Pin cache file #{path} does not exist" end @@ -460,11 +462,11 @@ def uncache *path_segments, out: nil def uncache_by_prefix *path_segments, out: nil path = File.join(*path_segments) glob = "#{path}*" - out.puts "Clearing pin cache in #{glob}" unless out.nil? + out&.puts "Clearing pin cache in #{glob}" Dir.glob(glob).each do |file| next unless File.file?(file) FileUtils.rm_rf file, secure: true - out.puts "Clearing pin cache in #{file}" unless out.nil? + out&.puts "Clearing pin cache in #{file}" end end diff --git a/lib/solargraph/position.rb b/lib/solargraph/position.rb index 1f8aa2805..201e6de36 100644 --- a/lib/solargraph/position.rb +++ b/lib/solargraph/position.rb @@ -21,10 +21,6 @@ def initialize line, character @character = character end - protected def equality_fields - [line, character] - end - # @param other [Position] def <=> other return nil unless other.is_a?(Position) @@ -91,7 +87,7 @@ def self.from_offset text, offset cursor += line_length line += 1 end - character = 0 if character.nil? and (cursor - offset).between?(0, 1) + character = 0 if character.nil? && (cursor - offset).between?(0, 1) raise InvalidOffsetError if character.nil? # @sg-ignore flow sensitive typing needs to handle 'raise if' Position.new(line, character) @@ -114,5 +110,11 @@ def == other return false unless other.is_a?(Position) line == other.line and character == other.character end + + protected + + def equality_fields + [line, character] + end end end diff --git a/lib/solargraph/range.rb b/lib/solargraph/range.rb index 337e1db0d..e1ed89592 100644 --- a/lib/solargraph/range.rb +++ b/lib/solargraph/range.rb @@ -19,10 +19,6 @@ def initialize start, ending @ending = ending end - protected def equality_fields - [start, ending] - end - # @param other [BasicObject] def <=> other return nil unless other.is_a?(Range) @@ -86,7 +82,7 @@ def self.from_to l1, c1, l2, c2 # @param node [::Parser::AST::Node] # @return [Range, nil] def self.from_node node - return unless node&.loc && node.loc.expression + return unless node&.loc&.expression from_expr(node.loc.expression) end @@ -107,5 +103,11 @@ def == other def inspect "#<#{self.class} #{start.inspect} to #{ending.inspect}>" end + + protected + + def equality_fields + [start, ending] + end end end diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index cb2bede54..824895244 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -68,7 +68,7 @@ def loader def cache_key return CACHE_KEY_UNRESOLVED unless resolved? - @hextdigest ||= begin + @cache_key ||= begin # @type [String, nil] data = nil # @type gem_config [nil, Hash{String => Hash{String => String}}] @@ -141,7 +141,7 @@ def pins out: $stderr # @return [generic, nil] def path_pin path, klass = Pin::Base pin = pins.find { |p| p.path == path } - pin if pin&.is_a?(klass) + pin if pin.is_a?(klass) end # @param path [String] diff --git a/lib/solargraph/rbs_map/conversions.rb b/lib/solargraph/rbs_map/conversions.rb index 9383ac441..bba131bf4 100644 --- a/lib/solargraph/rbs_map/conversions.rb +++ b/lib/solargraph/rbs_map/conversions.rb @@ -359,7 +359,7 @@ def global_decl_to_pin decl ['RuboCop::Cop::RangeHelp', :instance, 'source_range'] => :private, ['AST::Node', :instance, 'original_dup'] => :private, ['Rainbow::Presenter', :instance, 'wrap_with_sgr'] => :private - } + }.freeze # @param decl [RBS::AST::Members::MethodDefinition, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrWriter, RBS::AST::Members::AttrAccessor] # @param closure [Pin::Closure] @@ -726,7 +726,7 @@ def alias_to_pin decl, closure 'int' => 'Integer', 'untyped' => '', 'NilClass' => 'nil' - } + }.freeze # @param type [RBS::MethodType, RBS::Types::Block] # @return [String] @@ -765,57 +765,58 @@ def type_tag type_name, type_args = [] # but not all. # @return [String] def other_type_to_tag type - if type.is_a?(RBS::Types::Optional) + case type + when RBS::Types::Optional "#{other_type_to_tag(type.type)}, nil" - elsif type.is_a?(RBS::Types::Bases::Any) + when RBS::Types::Bases::Any 'undefined' - elsif type.is_a?(RBS::Types::Bases::Bool) + when RBS::Types::Bases::Bool 'Boolean' - elsif type.is_a?(RBS::Types::Tuple) + when RBS::Types::Tuple "Array(#{type.types.map { |t| other_type_to_tag(t) }.join(', ')})" - elsif type.is_a?(RBS::Types::Literal) + when RBS::Types::Literal type.literal.inspect - elsif type.is_a?(RBS::Types::Union) + when RBS::Types::Union type.types.map { |t| other_type_to_tag(t) }.join(', ') - elsif type.is_a?(RBS::Types::Record) + when RBS::Types::Record # @todo Better record support 'Hash' - elsif type.is_a?(RBS::Types::Bases::Nil) + when RBS::Types::Bases::Nil 'nil' - elsif type.is_a?(RBS::Types::Bases::Self) + when RBS::Types::Bases::Self 'self' - elsif type.is_a?(RBS::Types::Bases::Void) + when RBS::Types::Bases::Void 'void' - elsif type.is_a?(RBS::Types::Variable) + when RBS::Types::Variable "#{Solargraph::ComplexType::GENERIC_TAG_NAME}<#{type.name}>" - elsif type.is_a?(RBS::Types::ClassInstance) # && !type.args.empty? + when RBS::Types::ClassInstance # && !type.args.empty? type_tag(type.name, type.args) - elsif type.is_a?(RBS::Types::Bases::Instance) + when RBS::Types::Bases::Instance 'self' - elsif type.is_a?(RBS::Types::Bases::Top) + when RBS::Types::Bases::Top # top is the most super superclass 'BasicObject' - elsif type.is_a?(RBS::Types::Bases::Bottom) + when RBS::Types::Bases::Bottom # bottom is used in contexts where nothing will ever return # - e.g., it could be the return type of 'exit()' or 'raise' # # @todo define a specific bottom type and use it to # determine dead code 'undefined' - elsif type.is_a?(RBS::Types::Intersection) + when RBS::Types::Intersection type.types.map { |member| other_type_to_tag(member) }.join(', ') - elsif type.is_a?(RBS::Types::Proc) + when RBS::Types::Proc 'Proc' - elsif type.is_a?(RBS::Types::Alias) + when RBS::Types::Alias # type-level alias use - e.g., 'bool' in "type bool = true | false" # @todo ensure these get resolved after processing all aliases # @todo handle recursive aliases type_tag(type.name, type.args) - elsif type.is_a?(RBS::Types::Interface) + when RBS::Types::Interface # represents a mix-in module which can be considered a # subtype of a consumer of it type_tag(type.name, type.args) - elsif type.is_a?(RBS::Types::ClassSingleton) + when RBS::Types::ClassSingleton # e.g., singleton(String) type_tag(type.name) else diff --git a/lib/solargraph/rbs_map/core_fills.rb b/lib/solargraph/rbs_map/core_fills.rb index 809e802a1..0116b15eb 100644 --- a/lib/solargraph/rbs_map/core_fills.rb +++ b/lib/solargraph/rbs_map/core_fills.rb @@ -19,7 +19,7 @@ module CoreFills Solargraph::Pin::Method.new(name: 'class', scope: :instance, closure: Solargraph::Pin::Namespace.new(name: 'Object', source: :core_fill), comments: '@return [::Class]', source: :core_fill) - ] + ].freeze OVERRIDES = [ Override.from_comment('BasicObject#instance_eval', '@yieldreceiver [self]', @@ -39,7 +39,7 @@ module CoreFills # RBS does not define Class with a generic, so all calls to # generic() return an 'untyped'. We can do better: Override.method_return('Class#allocate', 'self', source: :core_fill) - ] + ].freeze # @todo I don't see any direct link in RBS to build this from - # presumably RBS is using duck typing to match interfaces @@ -73,7 +73,7 @@ module CoreFills closure: Solargraph::Pin::Namespace.new(name: 'String', source: :core_fill), source: :core_fill) - ] + ].freeze # HACK: Add Errno exception classes errno = Solargraph::Pin::Namespace.new(name: 'Errno', source: :core_fill) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 14f57801f..956b50a3a 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -84,7 +84,7 @@ def config directory = '.' File.open(File.join(directory, '.solargraph.yml'), 'w') do |file| file.puts conf.to_yaml end - STDOUT.puts 'Configuration file initialized.' + $stdout.puts 'Configuration file initialized.' end desc 'clear', 'Delete all cached documentation' @@ -246,9 +246,13 @@ def typecheck *files end end puts "Typecheck finished in #{time.real} seconds." - puts "#{probcount} problem#{'s' if probcount != 1} found#{" in #{filecount} of #{files.length} files" if files.length != 1}." + puts "#{probcount} problem#{if probcount != 1 + 's' + end} found#{if files.length != 1 + " in #{filecount} of #{files.length} files" + end}." # " - exit 1 if probcount > 0 + exit 1 if probcount.positive? end desc 'scan', 'Test the workspace for problems' diff --git a/lib/solargraph/source.rb b/lib/solargraph/source.rb index 1451692a5..76aa293a4 100644 --- a/lib/solargraph/source.rb +++ b/lib/solargraph/source.rb @@ -66,7 +66,7 @@ def at range def from_to l1, c1, l2, c2 b = Solargraph::Position.line_char_to_offset(code, l1, c1) e = Solargraph::Position.line_char_to_offset(code, l2, c2) - code[b..e - 1] + code[b..(e - 1)] end # Get the nearest node that contains the specified index. @@ -196,7 +196,7 @@ def code_for node b = Position.line_char_to_offset(code, rng.start.line, rng.start.column) # @sg-ignore Need to add nil check here e = Position.line_char_to_offset(code, rng.ending.line, rng.ending.column) - frag = code[b..e - 1].to_s + frag = code[b..(e - 1)].to_s frag.strip.gsub(/,$/, '') end @@ -293,7 +293,7 @@ def inner_folding_ranges top, result = [], parent = nil # @sg-ignore Translate to something flow sensitive typing understands range = Range.from_node(top) # @sg-ignore Need to add nil check here - if (result.empty? || range.start.line > result.last.start.line) && !(range.ending.line - range.start.line < 2) + if (result.empty? || range.start.line > result.last.start.line) && range.ending.line - range.start.line >= 2 # @sg-ignore Need to add nil check here result.push range end @@ -322,7 +322,7 @@ def stringify_comment_array comments here = p.index(/[^ \t]/) # @sg-ignore flow sensitive typing should be able to handle redefinition skip = here if skip.nil? || here < skip - ctxt.concat p[skip..-1] + ctxt.concat p[skip..] end started = true end @@ -354,7 +354,7 @@ def foldable_comment_block_ranges return [] unless synchronized? result = [] grouped = [] - comments.keys.each do |l| + comments.each_key do |l| if grouped.empty? || l == grouped.last + 1 grouped.push l else diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb index b083edc27..205fa6ff4 100644 --- a/lib/solargraph/source/chain.rb +++ b/lib/solargraph/source/chain.rb @@ -48,11 +48,6 @@ class Chain attr_reader :node - # @sg-ignore Fix "Not enough arguments to Module#protected" - protected def equality_fields - [links, node] - end - # @param node [Parser::AST::Node, nil] # @param links [::Array] # @param splat [Boolean] @@ -300,6 +295,13 @@ def maybe_nil type return type unless nullable? ComplexType.new(type.items + [ComplexType::NIL]) end + + protected + + # @sg-ignore Fix "Not enough arguments to Module#protected" + def equality_fields + [links, node] + end end end end diff --git a/lib/solargraph/source/chain/array.rb b/lib/solargraph/source/chain/array.rb index 544934d8a..352ccf8f0 100644 --- a/lib/solargraph/source/chain/array.rb +++ b/lib/solargraph/source/chain/array.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph class Source class Chain @@ -20,11 +22,11 @@ def resolve api_map, name_pin, locals child_types = @children.map do |child| child.infer(api_map, name_pin, locals).simplify_literals end - type = if child_types.length == 0 || child_types.any?(&:undefined?) + type = if child_types.empty? || child_types.any?(&:undefined?) ComplexType::UniqueType.new('Array', rooted: true) elsif child_types.uniq.length == 1 && child_types.first.defined? ComplexType::UniqueType.new('Array', [], child_types.uniq, rooted: true, parameters_type: :list) - elsif child_types.length == 0 + elsif child_types.empty? ComplexType::UniqueType.new('Array', rooted: true, parameters_type: :list) else ComplexType::UniqueType.new('Array', [], child_types, rooted: true, parameters_type: :fixed) diff --git a/lib/solargraph/source/chain/call.rb b/lib/solargraph/source/chain/call.rb index d1e14e365..9876fbbbd 100644 --- a/lib/solargraph/source/chain/call.rb +++ b/lib/solargraph/source/chain/call.rb @@ -35,12 +35,6 @@ def initialize word, location = nil, arguments = [], block = nil fix_block_pass end - # @sg-ignore Fix "Not enough arguments to Module#protected" - protected def equality_fields - # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array - super + [arguments, block] - end - def with_block? !!@block end @@ -69,7 +63,7 @@ def resolve api_map, name_pin, locals [stack.first].compact end # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array - pin_groups = [] if !api_map.loose_unions && pin_groups.any? { |pins| pins.empty? } + pin_groups = [] if !api_map.loose_unions && pin_groups.any?(&:empty?) pins = pin_groups.flatten.uniq(&:path) return [] if pins.empty? inferred_pins(pins, api_map, name_pin, locals) @@ -370,6 +364,14 @@ def block_call_type api_map, name_pin, locals # @sg-ignore Need to add nil check here block.infer(api_map, block_pin, locals) end + + protected + + # @sg-ignore Fix "Not enough arguments to Module#protected" + def equality_fields + # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array + super + [arguments, block] + end end end end diff --git a/lib/solargraph/source/chain/constant.rb b/lib/solargraph/source/chain/constant.rb index b1c25fab9..c1698f619 100644 --- a/lib/solargraph/source/chain/constant.rb +++ b/lib/solargraph/source/chain/constant.rb @@ -11,7 +11,7 @@ def initialize word def resolve api_map, name_pin, locals return [Pin::ROOT_PIN] if word.empty? if word.start_with?('::') - base = word[2..-1] + base = word[2..] gates = [''] else base = word diff --git a/lib/solargraph/source/chain/hash.rb b/lib/solargraph/source/chain/hash.rb index bf2aa484c..a75963478 100644 --- a/lib/solargraph/source/chain/hash.rb +++ b/lib/solargraph/source/chain/hash.rb @@ -12,12 +12,6 @@ def initialize type, node, splatted = false @splatted = splatted end - # @sg-ignore Fix "Not enough arguments to Module#protected" - protected def equality_fields - # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array - super + [@splatted] - end - def word @word ||= "<#{@type}>" end @@ -29,6 +23,14 @@ def resolve api_map, name_pin, locals def splatted? @splatted end + + protected + + # @sg-ignore Fix "Not enough arguments to Module#protected" + def equality_fields + # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array + super + [@splatted] + end end end end diff --git a/lib/solargraph/source/chain/if.rb b/lib/solargraph/source/chain/if.rb index 9a18c7948..f71f49cc5 100644 --- a/lib/solargraph/source/chain/if.rb +++ b/lib/solargraph/source/chain/if.rb @@ -13,17 +13,19 @@ def initialize links @links = links end - # @sg-ignore Fix "Not enough arguments to Module#protected" - protected def equality_fields - # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array - super + [@links] - end - def resolve api_map, name_pin, locals types = @links.map { |link| link.infer(api_map, name_pin, locals) } [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')), source: :chain)] end + + protected + + # @sg-ignore Fix "Not enough arguments to Module#protected" + def equality_fields + # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array + super + [@links] + end end end end diff --git a/lib/solargraph/source/chain/link.rb b/lib/solargraph/source/chain/link.rb index e58030792..ae0d0d60d 100644 --- a/lib/solargraph/source/chain/link.rb +++ b/lib/solargraph/source/chain/link.rb @@ -17,17 +17,6 @@ def initialize word = '' @word = word end - # @sg-ignore two problems - Declared return type - # ::Solargraph::Source::Chain::Array does not match inferred - # type ::Array(::Class<::Solargraph::Source::Chain::Link>, - # ::String) for - # Solargraph::Source::Chain::Link#equality_fields - # and - # Not enough arguments to Module#protected - protected def equality_fields - [self.class, word] - end - def undefined? word == '' end @@ -95,6 +84,17 @@ def inspect protected + # @sg-ignore two problems - Declared return type + # ::Solargraph::Source::Chain::Array does not match inferred + # type ::Array(::Class<::Solargraph::Source::Chain::Link>, + # ::String) for + # Solargraph::Source::Chain::Link#equality_fields + # and + # Not enough arguments to Module#protected + def equality_fields + [self.class, word] + end + # Mark whether this link is the head of a chain # # @param bool [Boolean] diff --git a/lib/solargraph/source/chain/q_call.rb b/lib/solargraph/source/chain/q_call.rb index 811594f7d..7247cf4cc 100644 --- a/lib/solargraph/source/chain/q_call.rb +++ b/lib/solargraph/source/chain/q_call.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph class Source class Chain diff --git a/lib/solargraph/source/change.rb b/lib/solargraph/source/change.rb index 97df1c140..acea51b67 100644 --- a/lib/solargraph/source/change.rb +++ b/lib/solargraph/source/change.rb @@ -28,7 +28,7 @@ def initialize range, new_text # syntax errors will be repaired. # @return [String] The updated text. def write text, nullable = false - if nullable and !range.nil? and new_text.match(/[.\[{(@$:]$/) + if nullable && !range.nil? && new_text.match(/[.\[{(@$:]$/) [':', '@'].each do |dupable| next unless new_text == dupable # @sg-ignore flow sensitive typing needs to handle attrs @@ -66,7 +66,7 @@ def repair text match = result[0, off].match(/[.:]+\z/) if match # @sg-ignore flow sensitive typing should be able to handle redefinition - result = result[0, off].sub(/#{match[0]}\z/, ' ' * match[0].length) + result[off..-1] + result = result[0, off].sub(/#{match[0]}\z/, ' ' * match[0].length) + result[off..] end result end @@ -82,7 +82,7 @@ def commit text, insert start_offset = Position.to_offset(text, range.start) # @sg-ignore Need to add nil check here end_offset = Position.to_offset(text, range.ending) - (start_offset == 0 ? '' : text[0..start_offset - 1].to_s) + normalize(insert) + text[end_offset..-1].to_s + (start_offset.zero? ? '' : text[0..(start_offset - 1)].to_s) + normalize(insert) + text[end_offset..].to_s end end end diff --git a/lib/solargraph/source/cursor.rb b/lib/solargraph/source/cursor.rb index 0b3b40606..1afa1c76c 100644 --- a/lib/solargraph/source/cursor.rb +++ b/lib/solargraph/source/cursor.rb @@ -39,11 +39,11 @@ def word # @return [String] def start_of_word @start_of_word ||= begin - match = source.code[0..offset - 1].to_s.match(start_word_pattern) + match = source.code[0..(offset - 1)].to_s.match(start_word_pattern) result = (match ? match[0] : '') # Including the preceding colon if the word appears to be a symbol # @sg-ignore Need to add nil check here - if source.code[0..offset - result.length - 1].end_with?(':') and !source.code[0..offset - result.length - 1].end_with?('::') + if source.code[0..(offset - result.length - 1)].end_with?(':') && !source.code[0..(offset - result.length - 1)].end_with?('::') result = ":#{result}" end result @@ -57,7 +57,7 @@ def start_of_word # @sg-ignore Need to add nil check here def end_of_word @end_of_word ||= begin - match = source.code[offset..-1].to_s.match(end_word_pattern) + match = source.code[offset..].to_s.match(end_word_pattern) match ? match[0] : '' end end diff --git a/lib/solargraph/source/source_chainer.rb b/lib/solargraph/source/source_chainer.rb index 5c235058a..dda5d6e76 100644 --- a/lib/solargraph/source/source_chainer.rb +++ b/lib/solargraph/source/source_chainer.rb @@ -103,7 +103,7 @@ def chain # @sg-ignore Need to add nil check here # @return [String] def phrase - @phrase ||= source.code[signature_data..offset - 1] + @phrase ||= source.code[signature_data..(offset - 1)] end # @sg-ignore Need to add nil check here @@ -165,36 +165,37 @@ def get_signature_data_at index in_whitespace = false while index >= 0 pos = Position.from_offset(@source.code, index) - break if index > 0 and @source.comment_at?(pos) - break if brackets > 0 or parens > 0 or squares > 0 + break if index.positive? && @source.comment_at?(pos) + break if brackets.positive? || parens.positive? || squares.positive? char = @source.code[index, 1] break if char.nil? # @todo Is this the right way to handle this? - if brackets.zero? and parens.zero? and squares.zero? and [' ', "\r", "\n", "\t"].include?(char) + if brackets.zero? && parens.zero? && squares.zero? && [' ', "\r", "\n", "\t"].include?(char) in_whitespace = true else # @sg-ignore Need to add nil check here - if brackets.zero? and parens.zero? and squares.zero? and in_whitespace && !(char == '.' or @source.code[index + 1..-1].strip.start_with?('.')) - @source.code[index + 1..-1] + if brackets.zero? && parens.zero? && squares.zero? && in_whitespace && !((char == '.') || @source.code[(index + 1)..].strip.start_with?('.')) + @source.code[(index + 1)..] # @sg-ignore Need to add nil check here - @source.code[index + 1..-1].lstrip + @source.code[(index + 1)..].lstrip # @sg-ignore Need to add nil check here - index += (@source.code[index + 1..-1].length - @source.code[index + 1..-1].lstrip.length) + index += (@source.code[(index + 1)..].length - @source.code[(index + 1)..].lstrip.length) break end - if char == ')' + case char + when ')' parens -= 1 - elsif char == ']' + when ']' squares -= 1 - elsif char == '}' + when '}' brackets -= 1 - elsif char == '(' + when '(' parens += 1 - elsif char == '{' + when '{' brackets += 1 - elsif char == '[' + when '[' squares += 1 end - if brackets.zero? and parens.zero? and squares.zero? + if brackets.zero? && parens.zero? && squares.zero? break if ['"', "'", ',', ';', '%'].include?(char) break if ['!', '?'].include?(char) && index < offset - 1 break if char == '$' diff --git a/lib/solargraph/source/updater.rb b/lib/solargraph/source/updater.rb index 4fdb78330..9debc0283 100644 --- a/lib/solargraph/source/updater.rb +++ b/lib/solargraph/source/updater.rb @@ -33,7 +33,7 @@ def initialize filename, version, changes # @return [String] def write text, nullable = false can_nullify = (nullable and changes.length == 1) - return @output if @input == text and can_nullify == @did_nullify + return @output if (@input == text) && (can_nullify == @did_nullify) @input = text @output = text @did_nullify = can_nullify diff --git a/lib/solargraph/source_map/clip.rb b/lib/solargraph/source_map/clip.rb index 2e48b0c89..981792915 100644 --- a/lib/solargraph/source_map/clip.rb +++ b/lib/solargraph/source_map/clip.rb @@ -161,7 +161,7 @@ def package_completions result def tag_complete result = [] # @sg-ignore Need to add nil check here - match = source_map.code[0..cursor.offset - 1].match(/[\[<, ]([a-z0-9_:]*)\z/i) + match = source_map.code[0..(cursor.offset - 1)].match(/[\[<, ]([a-z0-9_:]*)\z/i) if match # @sg-ignore Need to add nil check here full = match[1] diff --git a/lib/solargraph/source_map/mapper.rb b/lib/solargraph/source_map/mapper.rb index 5d8d66a9c..84a91ebca 100644 --- a/lib/solargraph/source_map/mapper.rb +++ b/lib/solargraph/source_map/mapper.rb @@ -94,7 +94,7 @@ def find_directive_line_number comment, tag, start # Avoid overruning the index return start unless start < comment.lines.length # @sg-ignore Need to add nil check here - num = comment.lines[start..-1].find_index do |line| + num = comment.lines[start..].find_index do |line| # Legacy method directives might be `@method` instead of `@!method` # @todo Legacy syntax should probably emit a warning line.include?("@!#{tag}") || (tag == 'method' && line.include?("@#{tag}")) @@ -139,7 +139,7 @@ def process_directive source_position, comment_position, directive when 'attribute' return if directive.tag.name.nil? namespace = closure_at(source_position) - t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.flatten.join('') + t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.join if t.nil? || t.include?('r') pins.push Solargraph::Pin::Method.new( location: location, @@ -214,7 +214,7 @@ def process_directive source_position, comment_position, directive Parser.process_node(src.node, region, @pins, locals, ivars) @pins.concat ivars # @sg-ignore Need to add nil check here - @pins[index..-1].each do |p| + @pins[index..].each do |p| # @todo Smelly instance variable access p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff) p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff) @@ -260,7 +260,7 @@ def remove_inline_comment_hashes comment # @sg-ignore Need to add nil check here num = cur if cur < num end - ctxt += "#{p[num..-1]}" if started + ctxt += p[num..].to_s if started end ctxt end diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index ea914644f..bfb0f0159 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -445,17 +445,16 @@ def signature_argument_problems_for location, locals, closure_pin, params, argum return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing argchain = arguments[idx] if argchain.nil? + final_arg = arguments.last if par.decl == :arg - final_arg = arguments.last if final_arg && final_arg.node.type == :splat argchain = final_arg return errors else errors.push Problem.new(location, "Not enough arguments to #{pin.path}") end - else - final_arg = arguments.last - argchain = final_arg if final_arg && %i[kwsplat hash].include?(final_arg.node.type) + elsif final_arg && %i[kwsplat hash].include?(final_arg.node.type) + argchain = final_arg end end if argchain @@ -812,7 +811,7 @@ def optional_param_count parameters def abstract? pin pin.docstring.has_tag?('abstract') || # @sg-ignore of low sensitive typing needs to handle ivars - (pin.closure && pin.closure.docstring.has_tag?('abstract')) + pin.closure&.docstring&.has_tag?('abstract') end # @param pin [Pin::Method] diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index acedf6375..cb97da43e 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -223,7 +223,7 @@ def gemspec_files # @return [String, nil] def rbs_collection_path - @gem_rbs_collection ||= read_rbs_collection_path + @rbs_collection_path ||= read_rbs_collection_path end # @return [String, nil] @@ -323,7 +323,7 @@ def load_sources source_hash.clear return if directory.empty? || directory == '*' size = config.calculated.length - if config.max_files > 0 and size > config.max_files + if config.max_files.positive? && (size > config.max_files) raise WorkspaceTooLargeError, "The workspace is too large to index (#{size} files, #{config.max_files} max)" end diff --git a/lib/solargraph/yard_map/helpers.rb b/lib/solargraph/yard_map/helpers.rb index 7969ffce5..8c1747d9a 100644 --- a/lib/solargraph/yard_map/helpers.rb +++ b/lib/solargraph/yard_map/helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Solargraph class YardMap module Helpers diff --git a/lib/solargraph/yard_map/mapper.rb b/lib/solargraph/yard_map/mapper.rb index 67a9ff860..c08021364 100644 --- a/lib/solargraph/yard_map/mapper.rb +++ b/lib/solargraph/yard_map/mapper.rb @@ -35,12 +35,13 @@ def map # @return [Array] def generate_pins code_object result = [] - if code_object.is_a?(YARD::CodeObjects::NamespaceObject) + case code_object + when YARD::CodeObjects::NamespaceObject nspin = ToNamespace.make(code_object, @spec, @namespace_pins[code_object.namespace.to_s]) @namespace_pins[code_object.path] = nspin result.push nspin # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check - if code_object.is_a?(YARD::CodeObjects::ClassObject) and !code_object.superclass.nil? + if code_object.is_a?(YARD::CodeObjects::ClassObject) && !code_object.superclass.nil? # This method of superclass detection is a bit of a hack. If # the superclass is a Proxy, it is assumed to be undefined in its # yardoc and converted to a fully qualified namespace. @@ -64,7 +65,7 @@ def generate_pins code_object source: :yard_map ) end - elsif code_object.is_a?(YARD::CodeObjects::MethodObject) + when YARD::CodeObjects::MethodObject closure = @namespace_pins[code_object.namespace.to_s] if code_object.name == :initialize && code_object.scope == :instance # @todo Check the visibility of .new @@ -73,7 +74,7 @@ def generate_pins code_object else result.push ToMethod.make(code_object, nil, nil, nil, closure, @spec) end - elsif code_object.is_a?(YARD::CodeObjects::ConstantObject) + when YARD::CodeObjects::ConstantObject closure = @namespace_pins[code_object.namespace] result.push ToConstant.make(code_object, closure, @spec) end diff --git a/lib/solargraph/yard_map/mapper/to_method.rb b/lib/solargraph/yard_map/mapper/to_method.rb index f4d5b0dad..047f53957 100644 --- a/lib/solargraph/yard_map/mapper/to_method.rb +++ b/lib/solargraph/yard_map/mapper/to_method.rb @@ -10,7 +10,7 @@ module ToMethod VISIBILITY_OVERRIDE = { # YARD pays attention to 'private' statements prior to class methods but shouldn't ['Rails::Engine', :class, 'find_root_with_flag'] => :public - } + }.freeze # @param code_object [YARD::CodeObjects::MethodObject] # @param name [String, nil] diff --git a/solargraph.gemspec b/solargraph.gemspec index a02082985..8660c16a6 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # @sg-ignore Should better support meaning of '&' in RBS -$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/lib" require 'solargraph/version' require 'date' diff --git a/spec/api_map/cache_spec.rb b/spec/api_map/cache_spec.rb index 0a8d002de..392f04fe4 100644 --- a/spec/api_map/cache_spec.rb +++ b/spec/api_map/cache_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::ApiMap::Cache do it 'recognizes empty caches' do - cache = Solargraph::ApiMap::Cache.new + cache = described_class.new expect(cache).to be_empty cache.set_methods('', :class, [:public], true, []) expect(cache).not_to be_empty diff --git a/spec/api_map/config_spec.rb b/spec/api_map/config_spec.rb index 936d05e27..993cb9a97 100644 --- a/spec/api_map/config_spec.rb +++ b/spec/api_map/config_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' describe Solargraph::Workspace::Config do diff --git a/spec/api_map/source_to_yard_spec.rb b/spec/api_map/source_to_yard_spec.rb index e7dcf3d0d..c232ca41d 100644 --- a/spec/api_map/source_to_yard_spec.rb +++ b/spec/api_map/source_to_yard_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::ApiMap::SourceToYard do it 'rakes sources' do source = Solargraph::SourceMap.load_string(%( @@ -9,7 +11,7 @@ def baz end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) expect(object.code_object_paths.length).to eq(3) expect(object.code_object_paths).to include('Foo') @@ -30,7 +32,7 @@ def self.baz end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) class_object = object.code_object_at('Foo') expect(class_object.docstring).to eq('My foo class 描述') @@ -51,7 +53,7 @@ class Baz end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) module_object = object.code_object_at('Foo') class_object = object.code_object_at('Baz') @@ -68,7 +70,7 @@ class Baz end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) module_object = object.code_object_at('Foo') class_object = object.code_object_at('Baz') @@ -84,7 +86,7 @@ class Foo end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) expect(object.code_object_at('Foo#bar')).not_to be_nil expect(object.code_object_at('Foo#bar=')).to be_nil @@ -102,7 +104,7 @@ def bar baz, boo = 'boo' end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) method_object = object.code_object_at('Foo#bar') expect(method_object.parameters.length).to eq(2) @@ -118,7 +120,7 @@ def bar baz, boo: 'boo' end )) object = Object.new - object.extend Solargraph::ApiMap::SourceToYard + object.extend described_class object.rake_yard Solargraph::ApiMap::Store.new(source.pins) method_object = object.code_object_at('Foo#bar') expect(method_object.parameters.length).to eq(2) diff --git a/spec/api_map/store_spec.rb b/spec/api_map/store_spec.rb index dc8413de5..059c3eb1b 100644 --- a/spec/api_map/store_spec.rb +++ b/spec/api_map/store_spec.rb @@ -4,7 +4,7 @@ it 'indexes multiple pinsets' do foo_pin = Solargraph::Pin::Namespace.new(name: 'Foo') bar_pin = Solargraph::Pin::Namespace.new(name: 'Bar') - store = Solargraph::ApiMap::Store.new([foo_pin], [bar_pin]) + store = described_class.new([foo_pin], [bar_pin]) expect(store.get_path_pins('Foo')).to eq([foo_pin]) expect(store.get_path_pins('Bar')).to eq([bar_pin]) @@ -13,7 +13,7 @@ it 'indexes empty pinsets' do foo_pin = Solargraph::Pin::Namespace.new(name: 'Foo') - store = Solargraph::ApiMap::Store.new([], [foo_pin]) + store = described_class.new([], [foo_pin]) expect(store.get_path_pins('Foo')).to eq([foo_pin]) end @@ -21,7 +21,7 @@ foo_pin = Solargraph::Pin::Namespace.new(name: 'Foo') bar_pin = Solargraph::Pin::Namespace.new(name: 'Bar') baz_pin = Solargraph::Pin::Namespace.new(name: 'Baz') - store = Solargraph::ApiMap::Store.new([foo_pin], [bar_pin]) + store = described_class.new([foo_pin], [bar_pin]) store.update([foo_pin], [baz_pin]) expect(store.get_path_pins('Foo')).to eq([foo_pin]) @@ -32,7 +32,7 @@ it 'updates new pinsets' do foo_pin = Solargraph::Pin::Namespace.new(name: 'Foo') bar_pin = Solargraph::Pin::Namespace.new(name: 'Bar') - store = Solargraph::ApiMap::Store.new([foo_pin]) + store = described_class.new([foo_pin]) store.update([foo_pin], [bar_pin]) expect(store.get_path_pins('Foo')).to eq([foo_pin]) @@ -42,7 +42,7 @@ it 'updates empty stores' do foo_pin = Solargraph::Pin::Namespace.new(name: 'Foo') bar_pin = Solargraph::Pin::Namespace.new(name: 'Bar') - store = Solargraph::ApiMap::Store.new + store = described_class.new store.update([foo_pin, bar_pin]) expect(store.get_path_pins('Foo')).to eq([foo_pin]) @@ -56,20 +56,20 @@ class Foo; end class Bar < Foo; end ), 'test.rb') - store = Solargraph::ApiMap::Store.new(map.pins) + store = described_class.new(map.pins) ref = store.get_superclass('Bar') expect(ref.name).to eq('Foo') end it 'returns Boolean superclass' do - store = Solargraph::ApiMap::Store.new + store = described_class.new ref = store.get_superclass('TrueClass') expect(ref.name).to eq('Boolean') end it 'maps core Errno classes' do map = Solargraph::RbsMap::CoreMap.new - store = Solargraph::ApiMap::Store.new(map.pins) + store = described_class.new(map.pins) Errno.constants.each do |const| pin = store.get_path_pins("Errno::#{const}").first expect(pin).to be_a(Solargraph::Pin::Namespace) diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index c4303dab4..c394a594f 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' describe Solargraph::ApiMap do @@ -5,7 +7,7 @@ # would be needed regardless as we are changing the working # directory before :context do - @api_map = Solargraph::ApiMap.new + @api_map = described_class.new end it 'returns core methods' do @@ -198,7 +200,7 @@ def prot end it 'adds Object instance methods to duck types' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new type = Solargraph::ComplexType.parse('#foo') pins = api_map.get_complex_type_methods(type) expect(pins.any? { |p| p.namespace == 'BasicObject' }).to be(true) @@ -447,7 +449,7 @@ class Sup end it 'loads workspaces from directories' do - api_map = Solargraph::ApiMap.load('spec/fixtures/workspace') + api_map = described_class.load('spec/fixtures/workspace') expect(api_map.source_map(File.absolute_path('spec/fixtures/workspace/app.rb'))).to be_a(Solargraph::SourceMap) end @@ -764,17 +766,17 @@ def bar; end end it 'can qualify "Boolean"' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new expect(api_map.qualify('Boolean')).to eq('Boolean') end it 'knows that true is a "subtype" of Boolean' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new expect(api_map.super_and_sub?('Boolean', 'true')).to be(true) end it 'knows that false is a "subtype" of Boolean' do - api_map = Solargraph::ApiMap.new + api_map = described_class.new expect(api_map.super_and_sub?('Boolean', 'false')).to be(true) end @@ -802,7 +804,7 @@ class Foo mixin = Solargraph::Pin::Reference::Include.new( name: 'defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable)', closure: closure ) - api_map = Solargraph::ApiMap.new(pins: [closure, mixin]) + api_map = described_class.new(pins: [closure, mixin]) expect(api_map.get_method_stack('Foo', 'foo')).to be_empty end @@ -823,7 +825,7 @@ def baz end ), 'test.rb') - api_map = Solargraph::ApiMap.new.map(source) + api_map = described_class.new.map(source) clip = api_map.clip_at('test.rb', [11, 10]) expect(clip.infer.to_s).to eq('Symbol') diff --git a/spec/complex_type_spec.rb b/spec/complex_type_spec.rb index a11307edd..15e54c3e1 100644 --- a/spec/complex_type_spec.rb +++ b/spec/complex_type_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe 'YARD type specifier list parsing' do context 'in compliance with https://www.rubydoc.info/gems/yard/file/docs/Tags.md#type-list-conventions' do # Types Specifier List @@ -215,7 +217,7 @@ types = Solargraph::ComplexType.parse('Hash{String, Symbol => Integer, BigDecimal}') expect(types.length).to eq(1) type = types.first - expect(type.hash_parameters?).to eq(true) + expect(type.hash_parameters?).to be(true) expect(type.key_types.map(&:name)).to eq(%w[String Symbol]) expect(type.value_types.map(&:name)).to eq(%w[Integer BigDecimal]) expect(type.to_rbs).to eq('Hash[(String | Symbol), (Integer | BigDecimal)]') @@ -535,7 +537,7 @@ ['generic', 'Array>', { 'B' => 'Integer' }, 'Array', { 'B' => 'Integer', 'A' => 'Array' }], ['Array>', 'Array', {}, 'Array', { 'A' => 'String' }] - ] + ].freeze UNIQUE_METHOD_GENERIC_TESTS.each do |tag, context_type_tag, unfrozen_input_map, expected_tag, expected_output_map| context "resolves #{tag} with context #{context_type_tag} and existing resolved generics #{unfrozen_input_map}" do @@ -545,7 +547,7 @@ let(:context_type) { Solargraph::ComplexType.parse(context_type_tag) } let(:generic_value) { unfrozen_input_map.transform_values! { |tag| Solargraph::ComplexType.parse(tag) } } - it '#{tag} is a unique type' do + it "#{tag} is a unique type" do expect(complex_type.length).to eq(1) end diff --git a/spec/convention/struct_definition_spec.rb b/spec/convention/struct_definition_spec.rb index 51a71f673..02786cfe6 100644 --- a/spec/convention/struct_definition_spec.rb +++ b/spec/convention/struct_definition_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Convention::StructDefinition do describe 'parsing docs' do it 'supports keyword args' do diff --git a/spec/convention_spec.rb b/spec/convention_spec.rb index b6f4fc52e..7785aef68 100644 --- a/spec/convention_spec.rb +++ b/spec/convention_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Convention do it 'newly defined pins are resolved by ApiMap after file changes' do filename = 'test.rb' diff --git a/spec/diagnostics/base_spec.rb b/spec/diagnostics/base_spec.rb index 42fbb097e..417797172 100644 --- a/spec/diagnostics/base_spec.rb +++ b/spec/diagnostics/base_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics::Base do it 'returns empty diagnostics' do - reporter = Solargraph::Diagnostics::Base.new + reporter = described_class.new expect(reporter.diagnose(nil, nil)).to be_empty end end diff --git a/spec/diagnostics/require_not_found_spec.rb b/spec/diagnostics/require_not_found_spec.rb index 53b375d7e..1588a033b 100644 --- a/spec/diagnostics/require_not_found_spec.rb +++ b/spec/diagnostics/require_not_found_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics::RequireNotFound do before do @source = Solargraph::Source.new(%( @@ -12,7 +14,7 @@ end it 'reports unresolved requires' do - reporter = Solargraph::Diagnostics::RequireNotFound.new + reporter = described_class.new result = reporter.diagnose(@source, @api_map) expect(result.length).to eq(1) end diff --git a/spec/diagnostics/rubocop_helpers_spec.rb b/spec/diagnostics/rubocop_helpers_spec.rb index bccfeb9d6..838da0816 100644 --- a/spec/diagnostics/rubocop_helpers_spec.rb +++ b/spec/diagnostics/rubocop_helpers_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics::RubocopHelpers do context do around do |example| @@ -20,7 +22,7 @@ it 'requires the specified version of rubocop' do input = custom_version - Solargraph::Diagnostics::RubocopHelpers.require_rubocop(input) + described_class.require_rubocop(input) output = RuboCop::Version::STRING expect(output).to eq(custom_version) end @@ -31,7 +33,7 @@ it 'requires the default version of rubocop' do input = nil - Solargraph::Diagnostics::RubocopHelpers.require_rubocop(input) + described_class.require_rubocop(input) output = RuboCop::Version::STRING expect(output).to eq(default_version) end @@ -39,13 +41,13 @@ it 'converts lower-case drive letters to upper-case' do input = 'c:/one/two' - output = Solargraph::Diagnostics::RubocopHelpers.fix_drive_letter(input) + output = described_class.fix_drive_letter(input) expect(output).to eq('C:/one/two') end it 'ignores paths without drive letters' do input = 'one/two' - output = Solargraph::Diagnostics::RubocopHelpers.fix_drive_letter(input) + output = described_class.fix_drive_letter(input) expect(output).to eq('one/two') end end diff --git a/spec/diagnostics/rubocop_spec.rb b/spec/diagnostics/rubocop_spec.rb index 4926a458f..7f6a9221d 100644 --- a/spec/diagnostics/rubocop_spec.rb +++ b/spec/diagnostics/rubocop_spec.rb @@ -10,7 +10,7 @@ def bar foo = Foo.new ), 'file.rb') - rubocop = Solargraph::Diagnostics::Rubocop.new + rubocop = described_class.new result = rubocop.diagnose(source, nil) expect(result).to be_a(Array) end @@ -28,13 +28,13 @@ def bar YAML example.run ensure - File.delete(config_file) if File.exist?(config_file) + FileUtils.rm_f(config_file) end it 'handles validation errors' do file = File.realpath(File.join(fixture_path, 'app.rb')) source = Solargraph::Source.load(file) - rubocop = Solargraph::Diagnostics::Rubocop.new + rubocop = described_class.new expect do rubocop.diagnose(source, nil) end.to raise_error(Solargraph::DiagnosticsError) @@ -44,7 +44,7 @@ def bar it 'calculates ranges' do file = File.realpath(File.join('spec', 'fixtures', 'rubocop-unused-variable-error', 'app.rb')) source = Solargraph::Source.load(file) - rubocop = Solargraph::Diagnostics::Rubocop.new + rubocop = described_class.new results = rubocop.diagnose(source, nil) expect(results).to be_one diff --git a/spec/diagnostics/type_check_spec.rb b/spec/diagnostics/type_check_spec.rb index c140f9593..f28cc7b73 100644 --- a/spec/diagnostics/type_check_spec.rb +++ b/spec/diagnostics/type_check_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics::TypeCheck do let(:api_map) { Solargraph::ApiMap.new } @@ -8,7 +10,7 @@ def foo end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always').diagnose(source, api_map) + result = described_class.new('always').diagnose(source, api_map) expect(result).to be_empty end @@ -18,7 +20,7 @@ def foo end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always', 'strong').diagnose(source, api_map) + result = described_class.new('always', 'strong').diagnose(source, api_map) expect(result.length).to eq(1) expect(result[0][:message]).to include('foo') end @@ -31,7 +33,7 @@ def foo(bar) end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always').diagnose(source, api_map) + result = described_class.new('always').diagnose(source, api_map) expect(result).to be_empty end @@ -43,7 +45,7 @@ def foo(bar) end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always', 'strong').diagnose(source, api_map) + result = described_class.new('always', 'strong').diagnose(source, api_map) expect(result.length).to eq(1) expect(result[0][:message]).to include('bar') end @@ -61,7 +63,7 @@ def foo end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always').diagnose(source, api_map) + result = described_class.new('always').diagnose(source, api_map) expect(result).to be_empty end @@ -79,7 +81,7 @@ def foo bar end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always').diagnose(source, api_map) + result = described_class.new('always').diagnose(source, api_map) expect(result).to be_empty end @@ -92,7 +94,7 @@ def foo(bar = 'bar', baz: 'baz') end )) api_map.map source - result = Solargraph::Diagnostics::TypeCheck.new('always').diagnose(source, api_map) + result = described_class.new('always').diagnose(source, api_map) expect(result).to be_empty end end diff --git a/spec/diagnostics/update_errors_spec.rb b/spec/diagnostics/update_errors_spec.rb index 1a05f93d5..0bb013231 100644 --- a/spec/diagnostics/update_errors_spec.rb +++ b/spec/diagnostics/update_errors_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics::UpdateErrors do it 'detects repaired lines' do api_map = Solargraph::ApiMap.new orig = Solargraph::Source.load_string('foo', 'test.rb') - diagnoser = Solargraph::Diagnostics::UpdateErrors.new + diagnoser = described_class.new result = diagnoser.diagnose(orig, api_map) expect(result.length).to eq(0) updater = Solargraph::Source::Updater.new('test.rb', 2, [ @@ -12,7 +14,7 @@ ) ]) source = orig.synchronize(updater) - diagnoser = Solargraph::Diagnostics::UpdateErrors.new + diagnoser = described_class.new result = diagnoser.diagnose(source, api_map) expect(result.length).to eq(1) end diff --git a/spec/diagnostics_spec.rb b/spec/diagnostics_spec.rb index 628341f2a..83e222e12 100644 --- a/spec/diagnostics_spec.rb +++ b/spec/diagnostics_spec.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + describe Solargraph::Diagnostics do it 'registers reporters' do - Solargraph::Diagnostics.register 'base', Solargraph::Diagnostics::Base - expect(Solargraph::Diagnostics.reporters).to include('base') - expect(Solargraph::Diagnostics.reporter('base')).to be(Solargraph::Diagnostics::Base) + described_class.register 'base', Solargraph::Diagnostics::Base + expect(described_class.reporters).to include('base') + expect(described_class.reporter('base')).to be(Solargraph::Diagnostics::Base) end end diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index b99b14786..efe76cefc 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -79,7 +79,7 @@ # Requiring 'set' is unnecessary because it's already included in core. It # might make sense to log redundant requires, but a warning is overkill. allow(Solargraph.logger).to receive(:warn).and_call_original - Solargraph::DocMap.new(['set'], workspace) + described_class.new(['set'], workspace) expect(Solargraph.logger).not_to have_received(:warn).with(/path set/) end @@ -183,7 +183,7 @@ def global doc_map Solargraph::Convention.register dummy_convention - doc_map = Solargraph::DocMap.new(['original_gem'], workspace) + doc_map = described_class.new(['original_gem'], workspace) # @todo this should probably not be in requires, which is a # path, and instead be in a new gem_names property on the diff --git a/spec/language_server/host/diagnoser_spec.rb b/spec/language_server/host/diagnoser_spec.rb index 78c50a7a2..f5f070cf5 100644 --- a/spec/language_server/host/diagnoser_spec.rb +++ b/spec/language_server/host/diagnoser_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Host::Diagnoser do it 'diagnoses on ticks' do host = double(Solargraph::LanguageServer::Host, options: { 'diagnostics' => true }, synchronizing?: false) allow(host).to receive(:diagnose) - diagnoser = Solargraph::LanguageServer::Host::Diagnoser.new(host) + diagnoser = described_class.new(host) diagnoser.schedule 'file.rb' diagnoser.tick expect(host).to have_received(:diagnose).with('file.rb') diff --git a/spec/language_server/host/dispatch_spec.rb b/spec/language_server/host/dispatch_spec.rb index a11093b5e..394309ef4 100644 --- a/spec/language_server/host/dispatch_spec.rb +++ b/spec/language_server/host/dispatch_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Host::Dispatch do before :context do # @dispatch = Solargraph::LanguageServer::Host::Dispatch @dispatch = Object.new - @dispatch.extend Solargraph::LanguageServer::Host::Dispatch + @dispatch.extend described_class end after do diff --git a/spec/language_server/host/message_worker_spec.rb b/spec/language_server/host/message_worker_spec.rb index 110b41cc4..8e5bdbb32 100644 --- a/spec/language_server/host/message_worker_spec.rb +++ b/spec/language_server/host/message_worker_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Host::MessageWorker do it 'handle requests on queue' do host = double(Solargraph::LanguageServer::Host) message = { 'method' => '$/example' } allow(host).to receive(:receive).with(message).and_return(nil) - worker = Solargraph::LanguageServer::Host::MessageWorker.new(host) + worker = described_class.new(host) worker.queue(message) expect(worker.messages).to eq [message] worker.tick diff --git a/spec/language_server/host_spec.rb b/spec/language_server/host_spec.rb index 3eb4d52f3..2d0bda048 100644 --- a/spec/language_server/host_spec.rb +++ b/spec/language_server/host_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'tmpdir' describe Solargraph::LanguageServer::Host do it 'prepares a workspace' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new Dir.mktmpdir do |dir| host.prepare(dir) expect(host.libraries.first).not_to be_nil @@ -10,7 +12,7 @@ end it 'processes responses to message requests' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new done_somethings = 0 host.send_request 'window/showMessageRequest', { 'message' => 'Message', @@ -28,7 +30,7 @@ it 'creates files from disk' do Dir.mktmpdir do |dir| - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.prepare dir file = File.join(dir, 'test.rb') File.write(file, "foo = 'foo'") @@ -41,7 +43,7 @@ it 'deletes files' do Dir.mktmpdir do |dir| expect do - host = Solargraph::LanguageServer::Host.new + host = described_class.new file = File.join(dir, 'test.rb') File.write(file, "foo = 'foo'") host.prepare dir @@ -52,14 +54,14 @@ end it 'cancels requests' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.cancel 1 expect(host.cancel?(1)).to be(true) end it 'runs diagnostics on opened files' do Dir.mktmpdir do |dir| - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.configure({ 'diagnostics' => true }) file = File.join(dir, 'test.rb') File.write(file, "foo = 'foo'") @@ -82,12 +84,10 @@ end it 'handles DiagnosticsErrors' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new library = double(:Library) allow(library).to receive(:diagnose).and_raise(Solargraph::DiagnosticsError) - allow(library).to receive(:contain?).and_return(true) - allow(library).to receive(:synchronized?).and_return(true) - allow(library).to receive(:mapped?).and_return(true) + allow(library).to receive_messages(contain?: true, synchronized?: true, mapped?: true) allow(library).to receive(:attach) allow(library).to receive(:merge) allow(library).to receive(:catalog) @@ -102,7 +102,7 @@ end it 'opens multiple folders' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new app1_folder = File.absolute_path('spec/fixtures/workspace_folders/folder1').gsub('\\', '/') app2_folder = File.absolute_path('spec/fixtures/workspace_folders/folder2').gsub('\\', '/') host.prepare(app1_folder) @@ -120,7 +120,7 @@ end it 'stops' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.stop expect(host.stopped?).to be(true) end @@ -129,7 +129,7 @@ dir = File.absolute_path('spec/fixtures/workspace') file = File.join(dir, 'lib', 'thing.rb') file_uri = Solargraph::LanguageServer::UriHelpers.uri_to_file(file) - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.prepare(dir) host.open(file_uri, File.read(file), 1) host.remove(dir) @@ -139,7 +139,7 @@ end it 'responds with empty diagnostics for unopened files' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.diagnose 'file:///file.rb' response = host.flush json = JSON.parse(response.lines.last) @@ -148,7 +148,7 @@ end it 'rescues runtime errors from messages' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new message_class = Class.new(Solargraph::LanguageServer::Message::Base) do def process raise 'Always raise an error from this message' @@ -165,7 +165,7 @@ def process end it 'ignores invalid messages' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new expect do host.receive({ 'bad' => 'message' }) end.not_to raise_error @@ -174,7 +174,7 @@ def process it 'repairs simple breaking changes without incremental sync' do file = '/test.rb' uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.prepare '' host.open uri, 'Foo::Bar', 1 sleep 0.1 until host.libraries.all?(&:mapped?) @@ -206,7 +206,7 @@ def initialize(foo); end file = '/test.rb' uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.prepare '' host.open uri, code, 1 sleep 0.1 until host.libraries.all?(&:mapped?) @@ -234,26 +234,26 @@ def initialize(foo); end describe '#references_from' do it 'rescues FileNotFound errors' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new expect { host.references_from('file:///not_a_file.rb', 1, 1) }.not_to raise_error end it 'logs FileNotFound errors' do allow(Solargraph.logger).to receive(:warn) - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.references_from('file:///not_a_file.rb', 1, 1) expect(Solargraph.logger).to have_received(:warn).with(/FileNotFoundError/) end it 'rescues InvalidOffset errors' do - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.open('file:///file.rb', 'class Foo; end', 1) expect { host.references_from('file:///file.rb', 0, 100) }.not_to raise_error end it 'logs InvalidOffset errors' do allow(Solargraph.logger).to receive(:warn) - host = Solargraph::LanguageServer::Host.new + host = described_class.new host.open('file:///file.rb', 'class Foo; end', 1) host.references_from('file:///file.rb', 0, 100) expect(Solargraph.logger).to have_received(:warn).with(/InvalidOffsetError/) @@ -262,7 +262,7 @@ def initialize(foo); end describe 'Workspace variations' do before do - @host = Solargraph::LanguageServer::Host.new + @host = described_class.new end after do diff --git a/spec/language_server/message/completion_item/resolve_spec.rb b/spec/language_server/message/completion_item/resolve_spec.rb index 7cf980377..a88594b9a 100644 --- a/spec/language_server/message/completion_item/resolve_spec.rb +++ b/spec/language_server/message/completion_item/resolve_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::CompletionItem::Resolve do it 'returns MarkupContent for documentation' do pin = Solargraph::Pin::Method.new( @@ -11,9 +13,9 @@ ) host = double(Solargraph::LanguageServer::Host, locate_pins: [pin], probe: pin, detail: nil, options: { 'enablePages' => true }) - resolve = Solargraph::LanguageServer::Message::CompletionItem::Resolve.new(host, { - 'params' => pin.completion_item - }) + resolve = described_class.new(host, { + 'params' => pin.completion_item + }) resolve.process expect(resolve.result[:documentation][:kind]).to eq('markdown') expect(resolve.result[:documentation][:value]).to include('A method') @@ -27,9 +29,9 @@ comments: '' ) host = double(Solargraph::LanguageServer::Host, locate_pins: [pin], probe: pin, detail: nil) - resolve = Solargraph::LanguageServer::Message::CompletionItem::Resolve.new(host, { - 'params' => pin.completion_item - }) + resolve = described_class.new(host, { + 'params' => pin.completion_item + }) resolve.process expect(resolve.result[:documentation]).to be_nil end diff --git a/spec/language_server/message/extended/check_gem_version_spec.rb b/spec/language_server/message/extended/check_gem_version_spec.rb index 1987287c3..9c3e6cfff 100644 --- a/spec/language_server/message/extended/check_gem_version_spec.rb +++ b/spec/language_server/message/extended/check_gem_version_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::Extended::CheckGemVersion do before do version = double(:GemVersion, version: Gem::Version.new('1.0.0')) - Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, - search_for_dependency: [version]) + described_class.fetcher = double(:fetcher, + search_for_dependency: [version]) end after do - Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = nil + described_class.fetcher = nil end it 'checks the gem source' do @@ -39,7 +41,7 @@ allow(Open3).to receive(:capture2).with('gem update solargraph').and_return(['', status]) host = Solargraph::LanguageServer::Host.new - message = Solargraph::LanguageServer::Message::Extended::CheckGemVersion.new(host, {}, current: Gem::Version.new('0.0.1')) + message = described_class.new(host, {}, current: Gem::Version.new('0.0.1')) message.process response = nil reader = Solargraph::LanguageServer::Transport::DataReader.new @@ -58,7 +60,7 @@ it 'uses bundler' do host = Solargraph::LanguageServer::Host.new host.configure({ 'useBundler' => true }) - message = Solargraph::LanguageServer::Message::Extended::CheckGemVersion.new(host, {}, current: Gem::Version.new('0.0.1')) + message = described_class.new(host, {}, current: Gem::Version.new('0.0.1')) message.process response = nil reader = Solargraph::LanguageServer::Transport::DataReader.new diff --git a/spec/language_server/message/initialize_spec.rb b/spec/language_server/message/initialize_spec.rb index 877add68f..aae61cff7 100644 --- a/spec/language_server/message/initialize_spec.rb +++ b/spec/language_server/message/initialize_spec.rb @@ -1,22 +1,24 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::Initialize do it 'prepares workspace folders' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) - init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'workspaceFolders' => [ - { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir), - 'name' => 'workspace' - } - ] - } - }) + init = described_class.new(host, { + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'workspaceFolders' => [ + { + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir), + 'name' => 'workspace' + } + ] + } + }) init.process expect(host.folders.length).to eq(1) end @@ -24,16 +26,16 @@ it 'prepares rootUri as a workspace' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) - init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'rootUri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) - } - }) + init = described_class.new(host, { + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'rootUri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(dir) + } + }) init.process expect(host.folders.length).to eq(1) end @@ -41,23 +43,23 @@ it 'prepares rootPath as a workspace' do host = Solargraph::LanguageServer::Host.new dir = File.realpath(File.join('spec', 'fixtures', 'workspace')) - init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'capabilities' => { - 'workspace' => { - 'workspaceFolders' => true - } - }, - 'rootPath' => dir - } - }) + init = described_class.new(host, { + 'params' => { + 'capabilities' => { + 'workspace' => { + 'workspaceFolders' => true + } + }, + 'rootPath' => dir + } + }) init.process expect(host.folders.length).to eq(1) end it 'returns the default capabilities' do host = Solargraph::LanguageServer::Host.new - init = Solargraph::LanguageServer::Message::Initialize.new(host, {}) + init = described_class.new(host, {}) init.process result = init.result expect(result).to include(:capabilities) @@ -82,15 +84,15 @@ it 'returns all capabilities when all options are enabled' do host = Solargraph::LanguageServer::Host.new - init = Solargraph::LanguageServer::Message::Initialize.new(host, { - 'params' => { - 'initializationOptions' => { - 'completion' => true, - 'autoformat' => true, - 'formatting' => true - } - } - }) + init = described_class.new(host, { + 'params' => { + 'initializationOptions' => { + 'completion' => true, + 'autoformat' => true, + 'formatting' => true + } + } + }) init.process result = init.result diff --git a/spec/language_server/message/text_document/definition_spec.rb b/spec/language_server/message/text_document/definition_spec.rb index 1266e4aa0..d84d23cbe 100644 --- a/spec/language_server/message/text_document/definition_spec.rb +++ b/spec/language_server/message/text_document/definition_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::TextDocument::Definition do it 'prepares empty directory' do Dir.mktmpdir do |dir| @@ -11,7 +13,7 @@ host.catalog file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(test_rb_path) other_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(thing_rb_path) - message = Solargraph::LanguageServer::Message::TextDocument::Definition + message = described_class .new(host, { 'params' => { 'textDocument' => { @@ -35,17 +37,17 @@ host.catalog file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/other.rb')) other_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/thing.rb')) - message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => file_uri - }, - 'position' => { - 'line' => 4, - 'character' => 10 - } - } - }) + message = described_class.new(host, { + 'params' => { + 'textDocument' => { + 'uri' => file_uri + }, + 'position' => { + 'line' => 4, + 'character' => 10 + } + } + }) message.process expect(message.result.first[:uri]).to eq(other_uri) end @@ -56,19 +58,19 @@ host.prepare(path) sleep 0.1 until host.libraries.all?(&:mapped?) host.catalog - message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join( - path, 'lib', 'other.rb' - )) - }, - 'position' => { - 'line' => 0, - 'character' => 10 - } - } - }) + message = described_class.new(host, { + 'params' => { + 'textDocument' => { + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join( + path, 'lib', 'other.rb' + )) + }, + 'position' => { + 'line' => 0, + 'character' => 10 + } + } + }) message.process expect(message.result.first[:uri]).to eq(Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', 'thing.rb'))) diff --git a/spec/language_server/message/text_document/formatting_spec.rb b/spec/language_server/message/text_document/formatting_spec.rb index a174a2361..d3a4d4edb 100644 --- a/spec/language_server/message/text_document/formatting_spec.rb +++ b/spec/language_server/message/text_document/formatting_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::TextDocument::Formatting do it 'gracefully handles empty files' do host = double(:Host, read_text: '', formatter_config: {}) @@ -8,7 +10,7 @@ } } } - message = Solargraph::LanguageServer::Message::TextDocument::Formatting.new(host, request) + message = described_class.new(host, request) message.process expect(message.process.first[:newText]).to be_empty end diff --git a/spec/language_server/message/text_document/hover_spec.rb b/spec/language_server/message/text_document/hover_spec.rb index 93f05c127..76b3c9082 100644 --- a/spec/language_server/message/text_document/hover_spec.rb +++ b/spec/language_server/message/text_document/hover_spec.rb @@ -1,20 +1,22 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::TextDocument::Hover do it 'returns nil for empty documentation' do host = Solargraph::LanguageServer::Host.new host.prepare('spec/fixtures/workspace') sleep 0.1 until host.libraries.all?(&:mapped?) host.catalog - message = Solargraph::LanguageServer::Message::TextDocument::Hover.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => 'file://spec/fixtures/workspace/lib/other.rb' - }, - 'position' => { - 'line' => 5, - 'character' => 0 - } - } - }) + message = described_class.new(host, { + 'params' => { + 'textDocument' => { + 'uri' => 'file://spec/fixtures/workspace/lib/other.rb' + }, + 'position' => { + 'line' => 5, + 'character' => 0 + } + } + }) message.process expect(message.result).to be_nil end @@ -29,17 +31,17 @@ def foo host = Solargraph::LanguageServer::Host.new host.open('file:///test.rb', code, 1) host.catalog - message = Solargraph::LanguageServer::Message::TextDocument::Hover.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => 'file:///test.rb' - }, - 'position' => { - 'line' => 4, - 'character' => 6 - } - } - }) + message = described_class.new(host, { + 'params' => { + 'textDocument' => { + 'uri' => 'file:///test.rb' + }, + 'position' => { + 'line' => 4, + 'character' => 6 + } + } + }) message.process expect(message.result[:contents][:value]).to eq("x\n\n`=~ String`") end diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 2f9c1afa4..141242f02 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -14,20 +14,20 @@ class Foo foo = Foo.new ), 1) sleep 0.01 until host.libraries.all?(&:mapped?) - rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 1, - 'character' => 12 - }, - 'newName' => 'Bar' - } - }) + rename = described_class.new(host, { + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 1, + 'character' => 12 + }, + 'newName' => 'Bar' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(2) end @@ -44,20 +44,20 @@ def foo(bar) end ), 1) - rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 2, - 'character' => 14 - }, - 'newName' => 'baz' - } - }) + rename = described_class.new(host, { + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 2, + 'character' => 14 + }, + 'newName' => 'baz' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end @@ -73,20 +73,20 @@ def foo(bar) end end ), 1) - rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 3, - 'character' => 6 - }, - 'newName' => 'baz' - } - }) + rename = described_class.new(host, { + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 3, + 'character' => 6 + }, + 'newName' => 'baz' + } + }) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end @@ -100,20 +100,20 @@ class Namespace::ExampleClass end obj = Namespace::ExampleClass.new ), 1) - rename = Solargraph::LanguageServer::Message::TextDocument::Rename.new(host, { - 'id' => 1, - 'method' => 'textDocument/rename', - 'params' => { - 'textDocument' => { - 'uri' => temp_file_url - }, - 'position' => { - 'line' => 2, - 'character' => 12 - }, - 'newName' => 'Nameplace' - } - }) + rename = described_class.new(host, { + 'id' => 1, + 'method' => 'textDocument/rename', + 'params' => { + 'textDocument' => { + 'uri' => temp_file_url + }, + 'position' => { + 'line' => 2, + 'character' => 12 + }, + 'newName' => 'Nameplace' + } + }) rename.process changes = rename.result[:changes][temp_file_url] expect(changes.length).to eq(3) diff --git a/spec/language_server/message/text_document/type_definition_spec.rb b/spec/language_server/message/text_document/type_definition_spec.rb index 7fdd9ce6f..16f7f3006 100644 --- a/spec/language_server/message/text_document/type_definition_spec.rb +++ b/spec/language_server/message/text_document/type_definition_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message::TextDocument::TypeDefinition do it 'finds definitions of methods' do host = Solargraph::LanguageServer::Host.new @@ -6,17 +8,17 @@ host.catalog file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/other.rb')) something_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path('spec/fixtures/workspace/lib/something.rb')) - message = Solargraph::LanguageServer::Message::TextDocument::TypeDefinition.new(host, { - 'params' => { - 'textDocument' => { - 'uri' => file_uri - }, - 'position' => { - 'line' => 4, - 'character' => 10 - } - } - }) + message = described_class.new(host, { + 'params' => { + 'textDocument' => { + 'uri' => file_uri + }, + 'position' => { + 'line' => 4, + 'character' => 10 + } + } + }) message.process expect(message.result.first[:uri]).to eq(something_uri) end diff --git a/spec/language_server/message/workspace/did_change_configuration_spec.rb b/spec/language_server/message/workspace/did_change_configuration_spec.rb index 0a082875d..a352a5c85 100644 --- a/spec/language_server/message/workspace/did_change_configuration_spec.rb +++ b/spec/language_server/message/workspace/did_change_configuration_spec.rb @@ -71,8 +71,8 @@ described_class.new(host, message).process expect( - host.registered?('textDocument/completion') - ).to be_truthy, 'Expected textDocument/completion to be registered' + host + ).to be_registered('textDocument/completion'), 'Expected textDocument/completion to be registered' end end @@ -83,8 +83,8 @@ described_class.new(host, message).process expect( - host.registered?('textDocument/completion') - ).to be_falsy, 'Expected textDocument/completion to not be registered' + host + ).not_to be_registered('textDocument/completion'), 'Expected textDocument/completion to not be registered' end end end diff --git a/spec/language_server/message/workspace/did_change_watched_files_spec.rb b/spec/language_server/message/workspace/did_change_watched_files_spec.rb index 504947855..f5ad606fe 100644 --- a/spec/language_server/message/workspace/did_change_watched_files_spec.rb +++ b/spec/language_server/message/workspace/did_change_watched_files_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' describe Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles do @@ -8,17 +10,17 @@ file = File.join(dir, 'foo.rb') File.write file, 'class Foo; end' uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) - changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CREATED, - 'uri' => uri - } - ] - } - }) + changed = described_class.new(host, { + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CREATED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) expect(host.library_for(uri)).to be_a(Solargraph::Library) @@ -33,17 +35,17 @@ uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) host = Solargraph::LanguageServer::Host.new host.prepare dir - changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::DELETED, - 'uri' => uri - } - ] - } - }) + changed = described_class.new(host, { + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::DELETED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) expect do @@ -60,17 +62,17 @@ host = Solargraph::LanguageServer::Host.new host.prepare dir File.write file, 'class FooBar; end' - changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CHANGED, - 'uri' => uri - } - ] - } - }) + changed = described_class.new(host, { + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles::CHANGED, + 'uri' => uri + } + ] + } + }) changed.process expect(host.synchronizing?).to be(false) library = host.library_for(uri) @@ -84,17 +86,17 @@ host = double(Solargraph::LanguageServer::Host, catalog: nil) allow(host).to receive(:create) allow(host).to receive(:delete) - changed = Solargraph::LanguageServer::Message::Workspace::DidChangeWatchedFiles.new(host, { - 'method' => 'workspace/didChangeWatchedFiles', - 'params' => { - 'changes' => [ - { - 'type' => -1, - 'uri' => 'file:///foo.rb' - } - ] - } - }) + changed = described_class.new(host, { + 'method' => 'workspace/didChangeWatchedFiles', + 'params' => { + 'changes' => [ + { + 'type' => -1, + 'uri' => 'file:///foo.rb' + } + ] + } + }) changed.process expect(changed.error).not_to be_nil end diff --git a/spec/language_server/message_spec.rb b/spec/language_server/message_spec.rb index 4dca484d5..504e66d07 100644 --- a/spec/language_server/message_spec.rb +++ b/spec/language_server/message_spec.rb @@ -1,11 +1,13 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Message do it 'returns MethodNotFound for unregistered methods' do - msg = Solargraph::LanguageServer::Message.select 'notARealMethod' + msg = described_class.select 'notARealMethod' expect(msg).to be(Solargraph::LanguageServer::Message::MethodNotFound) end it 'returns MethodNotImplemented for unregistered $ methods' do - msg = Solargraph::LanguageServer::Message.select '$/notARealMethod' + msg = described_class.select '$/notARealMethod' expect(msg).to be(Solargraph::LanguageServer::Message::MethodNotImplemented) end end diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index d8a87e1c8..bc9f30602 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Protocol attr_reader :response @@ -34,7 +36,7 @@ def stop describe Protocol do before :context do - @protocol = Protocol.new(Solargraph::LanguageServer::Host.new) + @protocol = described_class.new(Solargraph::LanguageServer::Host.new) end after :all do @@ -163,7 +165,7 @@ def bar baz } response = @protocol.response expect(response['error']).to be_nil - expect(response['result']['items'].length > 0).to be(true) + expect(!response['result']['items'].empty?).to be(true) end it 'handles completionItem/resolve' do diff --git a/spec/language_server/transport/adapter_spec.rb b/spec/language_server/transport/adapter_spec.rb index 6ec360395..23d6ac123 100644 --- a/spec/language_server/transport/adapter_spec.rb +++ b/spec/language_server/transport/adapter_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AdapterTester include Solargraph::LanguageServer::Transport::Adapter diff --git a/spec/language_server/transport/data_reader_spec.rb b/spec/language_server/transport/data_reader_spec.rb index bf9bbcc15..179f14a24 100644 --- a/spec/language_server/transport/data_reader_spec.rb +++ b/spec/language_server/transport/data_reader_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::Transport::DataReader do it 'rescues exceptions for invalid JSON' do - reader = Solargraph::LanguageServer::Transport::DataReader.new + reader = described_class.new handled = 0 reader.set_message_handler do |_msg| handled += 1 diff --git a/spec/language_server/uri_helpers_spec.rb b/spec/language_server/uri_helpers_spec.rb index 002a10d1c..038dca0bc 100644 --- a/spec/language_server/uri_helpers_spec.rb +++ b/spec/language_server/uri_helpers_spec.rb @@ -1,37 +1,39 @@ +# frozen_string_literal: true + describe Solargraph::LanguageServer::UriHelpers do it "doesn't escapes colons in file paths" do file = 'c:/one/two' - uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) + uri = described_class.file_to_uri(file) expect(uri).to start_with('file:///c:') end it 'uses %20 for spaces' do file = '/path/to/a file' - uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) + uri = described_class.file_to_uri(file) expect(uri).to end_with('a%20file') end it 'removes file:// prefix' do uri = 'file:///dev_tools/' - file = Solargraph::LanguageServer::UriHelpers.uri_to_file(uri) + file = described_class.uri_to_file(uri) expect(file).to eq('/dev_tools/') end it 'removes file: prefix' do uri = 'file:/dev_tools/' - file = Solargraph::LanguageServer::UriHelpers.uri_to_file(uri) + file = described_class.uri_to_file(uri) expect(file).to eq('/dev_tools/') end it 'removes file:/// prefix when a drive is specified' do uri = 'file:///Z:/dev_tools/' - file = Solargraph::LanguageServer::UriHelpers.uri_to_file(uri) + file = described_class.uri_to_file(uri) expect(file).to eq('Z:/dev_tools/') end it 'removes file:/ prefix when a drive is specified' do uri = 'file:/Z:/dev_tools/' - file = Solargraph::LanguageServer::UriHelpers.uri_to_file(uri) + file = described_class.uri_to_file(uri) expect(file).to eq('Z:/dev_tools/') end end diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 4a4108b03..e753e6df9 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'tmpdir' require 'yard' @@ -8,7 +10,7 @@ workspace_path = File.realpath(temp_dir_path) file_path = File.join(workspace_path, 'file.rb') File.write(file_path, 'a = b') - library = Solargraph::Library.load(workspace_path) + library = described_class.load(workspace_path) result = library.create(file_path, File.read(file_path)) expect(result).to be(true) expect(library.open?(file_path)).to be(false) @@ -16,7 +18,7 @@ end it 'returns a Completion' do - library = Solargraph::Library.new + library = described_class.new library.attach Solargraph::Source.load_string(%( x = 1 x @@ -32,8 +34,8 @@ end it 'returns a Completion', time_limit_seconds: 50 do - library = Solargraph::Library.new(Solargraph::Workspace.new(Dir.pwd, - Solargraph::Workspace::Config.new)) + library = described_class.new(Solargraph::Workspace.new(Dir.pwd, + Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( require 'backport' @@ -57,8 +59,8 @@ def foo(adapter) end it 'returns a Completion' do - library = Solargraph::Library.new(Solargraph::Workspace.new('', - Solargraph::Workspace::Config.new)) + library = described_class.new(Solargraph::Workspace.new('', + Solargraph::Workspace::Config.new)) library.attach Solargraph::Source.load_string(%( require 'backport' @@ -74,7 +76,7 @@ def foo(adapter) end it 'gets definitions from a file' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string %( class Foo def bar @@ -87,7 +89,7 @@ def bar end it 'gets type definitions from a file' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string %( class Bar; end class Foo @@ -103,7 +105,7 @@ def self.bar end it 'signifies method arguments' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string %( class Foo def bar baz, key: '' @@ -118,7 +120,7 @@ def bar baz, key: '' end it 'ignores invalid filenames in create_from_disk' do - library = Solargraph::Library.new + library = described_class.new filename = 'not_a_real_file.rb' expect(library.create_from_disk(filename)).to be(false) expect(library.contain?(filename)).to be(false) @@ -128,7 +130,7 @@ def bar baz, key: '' Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path workspace_path = File.realpath(temp_dir_path) - library = Solargraph::Library.load(workspace_path) + library = described_class.load(workspace_path) file_path = File.join(workspace_path, 'created.rb') File.write(file_path, "puts 'hello'") expect(library.create_from_disk(file_path)).to be(true) @@ -138,7 +140,7 @@ def bar baz, key: '' it 'ignores non-mergeable files in create_from_disk' do Dir.mktmpdir do |dir| - library = Solargraph::Library.load(dir) + library = described_class.load(dir) filename = File.join(dir, 'created.txt') File.write(filename, "puts 'hello'") expect(library.create_from_disk(filename)).to be(false) @@ -147,7 +149,7 @@ def bar baz, key: '' end it 'diagnoses files' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string(%( puts 'hello' ), 'file.rb', 0) @@ -162,7 +164,7 @@ def bar baz, key: '' config = instance_double(Solargraph::Workspace::Config) allow(config).to receive_messages(plugins: [], required: [], reporters: ['all!']) workspace = Solargraph::Workspace.new directory, config - library = Solargraph::Library.new workspace + library = described_class.new workspace src = Solargraph::Source.load_string(%( puts 'hello' ), 'file.rb', 0) @@ -172,7 +174,7 @@ def bar baz, key: '' end it 'documents symbols' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string(%( class Foo def bar @@ -189,7 +191,7 @@ def bar describe '#references_from' do it 'collects references to a new method on a constant from assignment of Class.new' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) src1 = Solargraph::Source.load_string(%( Foo.new ), 'file1.rb', 0) @@ -206,7 +208,7 @@ def bar it 'collects references to a new method to a constant from assignment' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) src1 = Solargraph::Source.load_string(%( Foo.new ), 'file1.rb', 0) @@ -225,7 +227,7 @@ class Foo it 'collects references to an instance method symbol' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) src1 = Solargraph::Source.load_string(%( class Foo def bar @@ -252,7 +254,7 @@ def bar; end it 'collects references to a class method symbol' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) src1 = Solargraph::Source.load_string(%( class Foo def self.bar @@ -287,7 +289,7 @@ def bar; end it 'collects stripped references to constant symbols' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) src1 = Solargraph::Source.load_string(%( class Foo def bar @@ -310,13 +312,13 @@ class Other code = library.read_text(l.filename) o1 = Solargraph::Position.to_offset(code, l.range.start) o2 = Solargraph::Position.to_offset(code, l.range.ending) - expect(code[o1..o2 - 1]).to eq('Foo') + expect(code[o1..(o2 - 1)]).to eq('Foo') end end it 'rejects new references from different classes' do workspace = Solargraph::Workspace.new('*') - library = Solargraph::Library.new(workspace) + library = described_class.new(workspace) source = Solargraph::Source.load_string(%( class Foo def bar @@ -334,20 +336,20 @@ def bar end it 'searches the core for queries' do - library = Solargraph::Library.new + library = described_class.new result = library.search('String') expect(result).not_to be_empty end it 'returns YARD documentation from the core' do - library = Solargraph::Library.new + library = described_class.new _, result = library.document('String') expect(result).not_to be_empty expect(result.first).to be_a(Solargraph::Pin::Base) end it 'returns YARD documentation from sources' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string(%( class Foo # My bar method @@ -361,7 +363,7 @@ def bar; end end it 'synchronizes sources from updaters' do - library = Solargraph::Library.new + library = described_class.new src = Solargraph::Source.load_string(%( class Foo end @@ -382,7 +384,7 @@ def bar; end end it 'finds unique references' do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + library = described_class.new(Solargraph::Workspace.new('*')) src1 = Solargraph::Source.load_string(%( class Foo end @@ -398,7 +400,7 @@ class Foo end it 'includes method parameters in references' do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + library = described_class.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( class Foo def bar(baz) @@ -414,7 +416,7 @@ def bar(baz) end it "lies about names when client can't handle the truth" do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + library = described_class.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( class Foo def 🤦🏻foo♀️; 123; end @@ -426,7 +428,7 @@ def 🤦🏻foo♀️; 123; end end it 'tells the truth about names when client can handle the truth' do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + library = described_class.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( class Foo def 🤦🏻foo♀️; 123; end @@ -438,7 +440,7 @@ def 🤦🏻foo♀️; 123; end end it 'includes block parameters in references' do - library = Solargraph::Library.new(Solargraph::Workspace.new('*')) + library = described_class.new(Solargraph::Workspace.new('*')) source = Solargraph::Source.load_string(%( 100.times do |foo| puts foo @@ -453,7 +455,7 @@ def 🤦🏻foo♀️; 123; end end it 'defines YARD tags' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class TaggedExample end @@ -475,7 +477,7 @@ def foo; end end it 'defines YARD tags with nested namespaces' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class Tagged class Example; end @@ -495,7 +497,7 @@ def foo; end end it 'defines generic YARD tags' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class TaggedExample; end class CallerExample @@ -509,7 +511,7 @@ def foo; end end it 'defines multiple YARD tags' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class TaggedExample; end class CallerExample @@ -523,7 +525,7 @@ def foo; end end it 'skips comment text outside of tags' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( # String def foo; end @@ -534,7 +536,7 @@ def foo; end end it 'marks aliases as methods or attributes in completion items' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class Example attr_reader :foo @@ -557,7 +559,7 @@ def baz end it 'marks aliases as methods or attributes in definitions' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class Example attr_reader :foo @@ -576,7 +578,7 @@ def bar; end end it 'detaches current source with nil' do - library = Solargraph::Library.new + library = described_class.new source = Solargraph::Source.load_string(%( class Example attr_reader :foo @@ -594,7 +596,7 @@ def bar; end describe '#locate_ref' do it 'returns nil without a matching reference location' do workspace = File.absolute_path(File.join('spec', 'fixtures', 'workspace')) - library = Solargraph::Library.load(workspace) + library = described_class.load(workspace) library.map! location = Solargraph::Location.new(File.join(workspace, 'app.rb'), Solargraph::Range.from_to(0, 8, 0, 8)) found = library.locate_ref(location) @@ -605,7 +607,7 @@ def bar; end describe '#delete' do it 'removes files from Library#source_map_hash' do workspace = File.absolute_path(File.join('spec', 'fixtures', 'workspace')) - library = Solargraph::Library.load(workspace) + library = described_class.load(workspace) library.map! library.catalog other_file = File.absolute_path(File.join('spec', 'fixtures', 'workspace', 'lib', 'other.rb')) @@ -623,7 +625,7 @@ def bar; end end context 'unsynchronized' do - let(:library) { Solargraph::Library.load File.absolute_path(File.join('spec', 'fixtures', 'workspace')) } + let(:library) { described_class.load File.absolute_path(File.join('spec', 'fixtures', 'workspace')) } let(:good_file) { File.join(library.workspace.directory, 'lib', 'thing.rb') } let(:bad_file) { File.join(library.workspace.directory, 'lib', 'not_a_thing.rb') } diff --git a/spec/logging_spec.rb b/spec/logging_spec.rb index 0c200594b..7dcd52000 100644 --- a/spec/logging_spec.rb +++ b/spec/logging_spec.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + require 'tempfile' describe Solargraph::Logging do it 'logs messages with levels' do file = Tempfile.new('log') - Solargraph::Logging.logger.reopen file - Solargraph::Logging.logger.warn 'Test' + described_class.logger.reopen file + described_class.logger.warn 'Test' file.rewind msg = file.read file.close file.unlink - Solargraph::Logging.logger.reopen File::NULL + described_class.logger.reopen File::NULL expect(msg).to include('WARN') end end diff --git a/spec/parser/node_chainer_spec.rb b/spec/parser/node_chainer_spec.rb index 1c1213564..8f24eb335 100644 --- a/spec/parser/node_chainer_spec.rb +++ b/spec/parser/node_chainer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe 'NodeChainer' do def chain_string str Solargraph::Parser.chain_string(str, 'file.rb', 0) diff --git a/spec/parser/node_methods_spec.rb b/spec/parser/node_methods_spec.rb index 039059b0f..c1a9d19e9 100644 --- a/spec/parser/node_methods_spec.rb +++ b/spec/parser/node_methods_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These tests are deliberately generic because they apply to both the Legacy # and Rubyvm node methods. describe Solargraph::Parser::NodeMethods do @@ -7,66 +9,66 @@ def parse source it 'unpacks constant nodes into strings' do ast = parse('Foo::Bar') - expect(Solargraph::Parser::NodeMethods.unpack_name(ast)).to eq 'Foo::Bar' + expect(described_class.unpack_name(ast)).to eq 'Foo::Bar' end it 'infers literal strings' do ast = parse("x = 'string'") - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::String' + expect(described_class.infer_literal_node_type(ast.children[1])).to eq '::String' end it 'infers literal hashes' do ast = parse('x = {}') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Hash' + expect(described_class.infer_literal_node_type(ast.children[1])).to eq '::Hash' end it 'infers literal arrays' do ast = parse('x = []') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Array' + expect(described_class.infer_literal_node_type(ast.children[1])).to eq '::Array' end it 'infers literal integers' do ast = parse('x = 100') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Integer' + expect(described_class.infer_literal_node_type(ast.children[1])).to eq '::Integer' end it 'infers literal floats' do ast = parse('x = 10.1') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast.children[1])).to eq '::Float' + expect(described_class.infer_literal_node_type(ast.children[1])).to eq '::Float' end it 'infers literal symbols' do ast = parse(':symbol') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' + expect(described_class.infer_literal_node_type(ast)).to eq '::Symbol' end it 'infers double quoted symbols' do ast = parse(':"symbol"') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' + expect(described_class.infer_literal_node_type(ast)).to eq '::Symbol' end it 'infers interpolated double quoted symbols' do - ast = parse(':"#{Object}"') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' + ast = parse(%(:"#{Object}")) + expect(described_class.infer_literal_node_type(ast)).to eq '::Symbol' end it 'infers single quoted symbols' do ast = parse(":'symbol'") - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(ast)).to eq '::Symbol' + expect(described_class.infer_literal_node_type(ast)).to eq '::Symbol' end it 'infers literal booleans' do true_ast = parse('true') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(true_ast)).to eq '::Boolean' + expect(described_class.infer_literal_node_type(true_ast)).to eq '::Boolean' false_ast = parse('false') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(false_ast)).to eq '::Boolean' + expect(described_class.infer_literal_node_type(false_ast)).to eq '::Boolean' end it 'handles return nodes with implicit nil values' do node = parse(%( return if true )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) # @todo Should there be two returns, the second being nil? expect(rets.map(&:to_s)).to eq(['(nil)', '(nil)']) expect(rets.length).to eq(2) @@ -76,7 +78,7 @@ def parse source node = parse(%( return bla if true )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) # Two returns, the second being implicit nil expect(rets.length).to eq(2) end @@ -88,7 +90,7 @@ def parse source true end )) - returns = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + returns = described_class.returns_from_method_body(node) # Include an implicit `nil` for missing else expect(returns.map(&:to_s)).to eq(['(true)', '(nil)']) end @@ -112,7 +114,7 @@ def parse source end end )) - returns = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + returns = described_class.returns_from_method_body(node) expect(returns.length).to eq(6) expect(returns.map(&:to_s)).to eq(['(true)', '(int 73)', '(false)', '(nil)', '(false)', '(true)']) end @@ -126,7 +128,7 @@ def parse source false end )) - returns = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + returns = described_class.returns_from_method_body(node) expect(returns.length).to eq(2) end @@ -146,7 +148,7 @@ def parse source x = 1 return x )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end @@ -156,7 +158,7 @@ def parse source return x y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end @@ -166,7 +168,7 @@ def parse source return x if foo y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(2) end @@ -177,36 +179,36 @@ def parse source y end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end it "handles top 'and' nodes" do node = parse('1 && "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) expect(rets[0].type.to_s.downcase).to eq('and') end it "handles top 'or' nodes" do node = parse('1 || "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end it "handles nested 'and' nodes" do node = parse('return 1 && "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) expect(rets[0].type.to_s.downcase).to eq('and') end it "handles nested 'or' nodes" do node = parse('return 1 || "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(2) - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(rets[0])).to eq('::Integer') - expect(Solargraph::Parser::NodeMethods.infer_literal_node_type(rets[1])).to eq('::String') + expect(described_class.infer_literal_node_type(rets[0])).to eq('::Integer') + expect(described_class.infer_literal_node_type(rets[1])).to eq('::String') end it 'finds return nodes in blocks' do @@ -215,7 +217,7 @@ def parse source return item if foo end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[block lvar]) end @@ -226,7 +228,7 @@ def parse source '123' end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq([:str]) end @@ -239,7 +241,7 @@ def parse source end nil )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[lvar nil]) end @@ -247,7 +249,7 @@ def parse source node = parse(%( return if true )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) # The expectation is changing from previous versions. If conditions # have an implicit else branch, so this node should return [nil, nil]. expect(rets.length).to eq(2) @@ -257,7 +259,7 @@ def parse source node = parse(%( return bla if true )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[send nil]) end @@ -277,7 +279,7 @@ def parse source x = 1 return x )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq([:lvar]) end @@ -287,7 +289,7 @@ def parse source return x y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end @@ -298,7 +300,7 @@ def parse source raise "Error" y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(0) end @@ -308,7 +310,7 @@ def parse source raise "Error" if foo y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end @@ -318,7 +320,7 @@ def parse source return "Error" if foo y )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(2) end @@ -329,31 +331,31 @@ def parse source y end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.length).to eq(1) end it "handles top 'and' nodes" do node = parse('1 && "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq([:and]) end it "handles top 'or' nodes" do node = parse('1 || "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq([:or]) end it "handles nested 'and' nodes from return" do node = parse('return 1 && "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq([:and]) end it "handles nested 'or' nodes from return" do node = parse('return 1 || "2"') - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[int str]) end @@ -363,7 +365,7 @@ def parse source return item if foo end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[block lvar]) # expect(rets[1].type).to eq(:DVAR) end @@ -377,7 +379,7 @@ def parse source end nil )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[lvar nil]) # expect(rets[0].type).to eq(:DVAR) end @@ -390,7 +392,7 @@ def parse source "" end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[str str]) end @@ -401,7 +403,7 @@ def parse source "" end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[str nil]) end @@ -414,20 +416,20 @@ def parse source super end )) - rets = Solargraph::Parser::NodeMethods.returns_from_method_body(node) + rets = described_class.returns_from_method_body(node) expect(rets.map(&:type)).to eq(%i[send zsuper]) end describe 'convert_hash' do it 'converts literal hash arguments' do node = parse('{foo: :bar}') - hash = Solargraph::Parser::NodeMethods.convert_hash(node) + hash = described_class.convert_hash(node) expect(hash.keys).to eq([:foo]) end it 'ignores call arguments' do node = parse('some_call') - hash = Solargraph::Parser::NodeMethods.convert_hash(node) + hash = described_class.convert_hash(node) expect(hash).to eq({}) end end @@ -441,7 +443,7 @@ def super_with_block end end )) - calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + calls = described_class.call_nodes_from(source.node) expect(calls).to be_one end @@ -449,7 +451,7 @@ def super_with_block source = Solargraph::Source.load_string(%( Foo.new.bar('string') )) - calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + calls = described_class.call_nodes_from(source.node) expect(calls.length).to eq(2) end @@ -457,7 +459,7 @@ def super_with_block source = Solargraph::Source.load_string(%( [ Foo.new.bar('string') ] )) - calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + calls = described_class.call_nodes_from(source.node) expect(calls.length).to eq(2) end @@ -465,7 +467,7 @@ def super_with_block source = Solargraph::Source.load_string(%( [ Foo.new.bar('string') ].compact )) - calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + calls = described_class.call_nodes_from(source.node) expect(calls.length).to eq(3) end @@ -481,7 +483,7 @@ def something end end )) - calls = Solargraph::Parser::NodeMethods.call_nodes_from(source.node) + calls = described_class.call_nodes_from(source.node) expect(calls.length).to eq(2) end end diff --git a/spec/parser/node_processor_spec.rb b/spec/parser/node_processor_spec.rb index 6785b9a09..da7031779 100644 --- a/spec/parser/node_processor_spec.rb +++ b/spec/parser/node_processor_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Parser::NodeProcessor do def parse source Solargraph::Parser.parse(source, 'file.rb', 0) @@ -10,7 +12,7 @@ class Foo end )) expect do - Solargraph::Parser::NodeProcessor.process(node) + described_class.process(node) end.not_to raise_error end @@ -18,7 +20,7 @@ class Foo node = parse(%( def foo(bar = nil, baz = nil); end )) - pins, = Solargraph::Parser::NodeProcessor.process(node) + pins, = described_class.process(node) # Method pin is first pin after default namespace pin = pins[1] expect(pin.parameters.map(&:name)).to eq(%w[bar baz]) @@ -30,7 +32,7 @@ def foo(bar = nil, baz = nil); end detail += "foo" detail.strip! )) - _, vars = Solargraph::Parser::NodeProcessor.process(node) + _, vars = described_class.process(node) # ensure we parsed the += correctly and won't report an unexpected # nil assignment @@ -55,17 +57,17 @@ def process end end - Solargraph::Parser::NodeProcessor.register(:def, dummy_processor1) - Solargraph::Parser::NodeProcessor.register(:def, dummy_processor2) + described_class.register(:def, dummy_processor1) + described_class.register(:def, dummy_processor2) node = parse(%( def some_method; end )) - pins, = Solargraph::Parser::NodeProcessor.process(node) + pins, = described_class.process(node) # empty namespace pin is root namespace expect(pins.map(&:name)).to contain_exactly('', 'foo', 'bar', 'some_method') # Clean up the registered processors - Solargraph::Parser::NodeProcessor.deregister(:def, dummy_processor1) - Solargraph::Parser::NodeProcessor.deregister(:def, dummy_processor2) + described_class.deregister(:def, dummy_processor1) + described_class.deregister(:def, dummy_processor2) end end diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 1757bb90d..62fe7d955 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Parser do def parse source Solargraph::Parser.parse(source, 'file.rb', 0) @@ -5,7 +7,7 @@ def parse source it 'parses nodes' do node = parse('class Foo; end') - expect(Solargraph::Parser.is_ast_node?(node)).to be(true) + expect(described_class.is_ast_node?(node)).to be(true) end it 'raises repairable SyntaxError for unknown encoding errors' do diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index 6e38e3628..90d9a8850 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Base do let(:zero_location) { Solargraph::Location.new('test.rb', Solargraph::Range.from_to(0, 0, 0, 0)) } let(:one_location) { Solargraph::Location.new('test.rb', Solargraph::Range.from_to(0, 0, 1, 0)) } it 'does not combine pins with directive changes' do - pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A Foo class', - source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) - pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', - source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) + pin1 = described_class.new(location: zero_location, name: 'Foo', comments: 'A Foo class', + source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) + pin2 = described_class.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', + source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) expect(pin1.nearly?(pin2)).to be(false) # enable asserts with_env_var('SOLARGRAPH_ASSERTS', 'on') do @@ -15,10 +17,10 @@ end it 'does not combine pins with different directives' do - pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', - source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) - pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@!macro other', - source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) + pin1 = described_class.new(location: zero_location, name: 'Foo', comments: '@!macro my_macro', + source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) + pin2 = described_class.new(location: zero_location, name: 'Foo', comments: '@!macro other', + source: :yardoc, closure: Solargraph::Pin::ROOT_PIN) expect(pin1.nearly?(pin2)).to be(false) with_env_var('SOLARGRAPH_ASSERTS', 'on') do expect { pin1.combine_with(pin2) }.to raise_error(RuntimeError, /Inconsistent :macros values/) @@ -26,26 +28,26 @@ end it 'sees tag differences as not near or equal' do - pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@return [Foo]') - pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@return [Bar]') + pin1 = described_class.new(location: zero_location, name: 'Foo', comments: '@return [Foo]') + pin2 = described_class.new(location: zero_location, name: 'Foo', comments: '@return [Bar]') expect(pin1.nearly?(pin2)).to be(false) expect(pin1 == pin2).to be(false) end it 'sees comment differences as nearly but not equal' do - pin1 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A Foo class') - pin2 = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: 'A different Foo') + pin1 = described_class.new(location: zero_location, name: 'Foo', comments: 'A Foo class') + pin2 = described_class.new(location: zero_location, name: 'Foo', comments: 'A different Foo') expect(pin1.nearly?(pin2)).to be(true) expect(pin1 == pin2).to be(false) end it 'recognizes deprecated tags' do - pin = Solargraph::Pin::Base.new(location: zero_location, name: 'Foo', comments: '@deprecated Use Bar instead.') + pin = described_class.new(location: zero_location, name: 'Foo', comments: '@deprecated Use Bar instead.') expect(pin).to be_deprecated end it 'does not link documentation for undefined return types' do - pin = Solargraph::Pin::Base.new(name: 'Foo', comments: '@return [undefined]') + pin = described_class.new(name: 'Foo', comments: '@return [undefined]') expect(pin.link_documentation).to eq('Foo') end diff --git a/spec/pin/base_variable_spec.rb b/spec/pin/base_variable_spec.rb index 06d3fb1f8..a6849629d 100644 --- a/spec/pin/base_variable_spec.rb +++ b/spec/pin/base_variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::BaseVariable do it 'checks assignments for equality' do smap = Solargraph::SourceMap.load_string('foo = "foo"') diff --git a/spec/pin/block_spec.rb b/spec/pin/block_spec.rb index 0ca36e950..2738d03e3 100644 --- a/spec/pin/block_spec.rb +++ b/spec/pin/block_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Block do it 'strips prefixes from parameter names' do # @todo Method parameters are pins now diff --git a/spec/pin/class_variable_spec.rb b/spec/pin/class_variable_spec.rb index c327f01a1..686b13177 100644 --- a/spec/pin/class_variable_spec.rb +++ b/spec/pin/class_variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # describe Solargraph::Pin::ClassVariable do # it "always has class scope" do # source = Solargraph::Source.load_string(%( diff --git a/spec/pin/constant_spec.rb b/spec/pin/constant_spec.rb index 124c36174..116c72b2a 100644 --- a/spec/pin/constant_spec.rb +++ b/spec/pin/constant_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Constant do it 'resolves constant paths' do source = Solargraph::Source.new(%( diff --git a/spec/pin/delegated_method_spec.rb b/spec/pin/delegated_method_spec.rb index a20fc26d4..7063d42c0 100644 --- a/spec/pin/delegated_method_spec.rb +++ b/spec/pin/delegated_method_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require 'pry' describe Solargraph::Pin::DelegatedMethod do it 'can be constructed from a Method pin' do method_pin = Solargraph::Pin::Method.new(comments: '@return [Hash{String => String}]') - delegation_pin = Solargraph::Pin::DelegatedMethod.new(method: method_pin, scope: :instance) + delegation_pin = described_class.new(method: method_pin, scope: :instance) expect(delegation_pin.return_type.to_s).to eq('Hash{String => String}') end @@ -26,7 +28,7 @@ def collaborator; end class2 = api_map.get_path_pins('Class2').first chain = Solargraph::Source::Chain.new([Solargraph::Source::Chain::Call.new('collaborator', nil)]) - pin = Solargraph::Pin::DelegatedMethod.new( + pin = described_class.new( closure: class2, scope: :instance, name: 'name', diff --git a/spec/pin/documenting_spec.rb b/spec/pin/documenting_spec.rb index 42da36dd1..cfecb0468 100644 --- a/spec/pin/documenting_spec.rb +++ b/spec/pin/documenting_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Documenting do let(:object) do Class.new do diff --git a/spec/pin/instance_variable_spec.rb b/spec/pin/instance_variable_spec.rb index c9fd036d7..e1b08e028 100644 --- a/spec/pin/instance_variable_spec.rb +++ b/spec/pin/instance_variable_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + describe Solargraph::Pin::InstanceVariable do it 'is a kind of variable' do source = Solargraph::Source.load_string("@foo = 'foo'", 'file.rb') map = Solargraph::SourceMap.map(source) - pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::InstanceVariable) }.first + pin = map.pins.select { |p| p.is_a?(described_class) }.first expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::VARIABLE) expect(pin.symbol_kind).to eq(Solargraph::LanguageServer::SymbolKinds::VARIABLE) end it 'does not link documentation for undefined return types' do - pin = Solargraph::Pin::InstanceVariable.new(name: '@bar') + pin = described_class.new(name: '@bar') expect(pin.link_documentation).to eq('@bar') end end diff --git a/spec/pin/keyword_spec.rb b/spec/pin/keyword_spec.rb index 99e51f287..4ffb9d34c 100644 --- a/spec/pin/keyword_spec.rb +++ b/spec/pin/keyword_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Keyword do it 'is a kind of keyword' do - pin = Solargraph::Pin::Keyword.new('foo') + pin = described_class.new('foo') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end end diff --git a/spec/pin/local_variable_spec.rb b/spec/pin/local_variable_spec.rb index 9bf722280..fecc2c8e2 100644 --- a/spec/pin/local_variable_spec.rb +++ b/spec/pin/local_variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::LocalVariable do xit 'merges presence changes so that [not currently used]' do map1 = Solargraph::SourceMap.load_string(%( diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index feb55698a..b996709ce 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Method do it 'tracks code parameters' do source = Solargraph::Source.new(%( @@ -33,7 +35,7 @@ def foo bar:, baz: MyClass.new )) map = Solargraph::SourceMap.map(source) pin = map.pins.select { |pin| pin.path == '#foo' }.first - expect(pin.class).to eq(Solargraph::Pin::Method) + expect(pin.class).to eq(described_class) method_pin = pin expect(method_pin.signatures.length).to eq(1) method_signature = method_pin.signatures.first @@ -53,7 +55,7 @@ def foo bar:, baz: MyClass.new @param two [Second] description2 COMMENTS # pin = source.pins.select{|pin| pin.path == 'Foo#bar'}.first - pin = Solargraph::Pin::Method.new(comments: comments) + pin = described_class.new(comments: comments) expect(pin.documentation).to include('one') expect(pin.documentation).to include('[First]') expect(pin.documentation).to include('description1') @@ -77,9 +79,9 @@ def bazzle; end COMMENTS map = Solargraph::SourceMap.map(source) bazzle = map.pins.select { |pin| pin.path == 'Bar::Baz#bazzle' }.first - expect(bazzle.return_type.rooted?).to eq(false) + expect(bazzle.return_type.rooted?).to be(false) bing = map.pins.select { |pin| pin.path == 'Bar::Baz#bing' }.first - expect(bing.return_type.rooted?).to eq(true) + expect(bing.return_type.rooted?).to be(true) end it 'includes yieldparam tags in documentation' do @@ -87,7 +89,7 @@ def bazzle; end @yieldparam one [First] description1 @yieldparam two [Second] description2 COMMENTS - pin = Solargraph::Pin::Method.new(comments: comments) + pin = described_class.new(comments: comments) expect(pin.documentation).to include('one') expect(pin.documentation).to include('[First]') expect(pin.documentation).to include('description1') @@ -101,37 +103,37 @@ def bazzle; end @yieldreturn [YRet] yretdescription @return [String] COMMENTS - pin = Solargraph::Pin::Method.new(comments: comments) + pin = described_class.new(comments: comments) expect(pin.documentation).to include('YRet') expect(pin.documentation).to include('yretdescription') end it 'detects return types from tags' do - pin = Solargraph::Pin::Method.new(comments: '@return [Hash]') + pin = described_class.new(comments: '@return [Hash]') expect(pin.return_type.tag).to eq('Hash') end it 'ignores malformed return tags' do - pin = Solargraph::Pin::Method.new(name: 'bar', comments: '@return [Array) @@ -425,7 +427,7 @@ def bar?; end end it 'supports multiple return tags' do - pin = Solargraph::Pin::Method.new( + pin = described_class.new( name: 'foo', comments: %( @return [String] @@ -436,7 +438,7 @@ def bar?; end end it 'includes @return text in documentation' do - pin = Solargraph::Pin::Method.new( + pin = described_class.new( name: 'foo', comments: %( @return [String] the foo text string @@ -446,7 +448,7 @@ def bar?; end end it 'includes @example text in documentation' do - pin = Solargraph::Pin::Method.new( + pin = described_class.new( name: 'foo', comments: %( @example @@ -458,7 +460,7 @@ def bar?; end end it 'includes @example names' do - pin = Solargraph::Pin::Method.new( + pin = described_class.new( name: 'foo', comments: %( @example Call foo @@ -520,32 +522,32 @@ class Foo end )) map = Solargraph::SourceMap.map(source) - pin = map.pins.select { |p| p.is_a?(Solargraph::Pin::Method) }.first + pin = map.pins.select { |p| p.is_a?(described_class) }.first expect(pin).to be_attribute expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::PROPERTY) expect(pin.symbol_kind).to eq(Solargraph::LanguageServer::SymbolKinds::PROPERTY) end it 'uses return type tags' do - pin = Solargraph::Pin::Method.new(name: 'bar', comments: '@return [File]', attribute: true) + pin = described_class.new(name: 'bar', comments: '@return [File]', attribute: true) expect(pin.return_type.tag).to eq('File') end it 'detects undefined types' do - pin = Solargraph::Pin::Method.new(name: 'bar', attribute: true) + pin = described_class.new(name: 'bar', attribute: true) expect(pin.return_type).to be_undefined end it 'generates paths' do npin = Solargraph::Pin::Namespace.new(name: 'Foo', type: :class) - ipin = Solargraph::Pin::Method.new(closure: npin, name: 'bar', attribute: true, scope: :instance) + ipin = described_class.new(closure: npin, name: 'bar', attribute: true, scope: :instance) expect(ipin.path).to eq('Foo#bar') - cpin = Solargraph::Pin::Method.new(closure: npin, name: 'bar', attribute: true, scope: :class) + cpin = described_class.new(closure: npin, name: 'bar', attribute: true, scope: :class) expect(cpin.path).to eq('Foo.bar') end it 'handles invalid return type tags' do - pin = Solargraph::Pin::Method.new(name: 'bar', comments: '@return [Array<]', attribute: true) + pin = described_class.new(name: 'bar', comments: '@return [Array<]', attribute: true) expect(pin.return_type).to be_undefined end @@ -634,7 +636,7 @@ def bar end it 'ignores malformed overload tags' do - pin = Solargraph::Pin::Method.new(name: 'example', comments: "@overload\n @param") + pin = described_class.new(name: 'example', comments: "@overload\n @param") expect(pin.overloads).to be_empty end end diff --git a/spec/pin/namespace_spec.rb b/spec/pin/namespace_spec.rb index 0421def05..cafadcc58 100644 --- a/spec/pin/namespace_spec.rb +++ b/spec/pin/namespace_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Namespace do it 'handles long namespaces' do - pin = Solargraph::Pin::Namespace.new(closure: Solargraph::Pin::Namespace.new(name: 'Foo'), name: 'Bar') + pin = described_class.new(closure: described_class.new(name: 'Foo'), name: 'Bar') expect(pin.path).to eq('Foo::Bar') end @@ -9,26 +11,26 @@ class Foo end )) - pin = Solargraph::Pin::Namespace.new(name: 'Foo') + pin = described_class.new(name: 'Foo') expect(pin.context.scope).to eq(:class) end it 'is a kind of namespace/class/module' do - pin1 = Solargraph::Pin::Namespace.new(name: 'Foo') + pin1 = described_class.new(name: 'Foo') expect(pin1.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::CLASS) - pin2 = Solargraph::Pin::Namespace.new(name: 'Foo', type: :module) + pin2 = described_class.new(name: 'Foo', type: :module) expect(pin2.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::MODULE) end it 'handles nested namespaces inside closures' do - pin = Solargraph::Pin::Namespace.new(closure: Solargraph::Pin::Namespace.new(name: 'Foo'), name: 'Bar::Baz') + pin = described_class.new(closure: described_class.new(name: 'Foo'), name: 'Bar::Baz') expect(pin.namespace).to eq('Foo::Bar') expect(pin.name).to eq('Baz') expect(pin.path).to eq('Foo::Bar::Baz') end it 'uses @param tags as generic type parameters' do - pin = Solargraph::Pin::Namespace.new(name: 'Foo', comments: '@generic GenericType') + pin = described_class.new(name: 'Foo', comments: '@generic GenericType') expect(pin.generics).to eq(['GenericType']) expect(pin.to_rbs).to eq('class ::Foo[GenericType]') end diff --git a/spec/pin/parameter_spec.rb b/spec/pin/parameter_spec.rb index 14c39f3fe..07f232848 100644 --- a/spec/pin/parameter_spec.rb +++ b/spec/pin/parameter_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Parameter do it 'detects block parameter return types from @yieldparam tags' do api_map = Solargraph::ApiMap.new @@ -195,8 +197,8 @@ def baz; end it 'uses longer comment while combining compatible parameters' do loc = Solargraph::Location.new('test.rb', Solargraph::Range.from_to(0, 0, 0, 0)) block = Solargraph::Pin::Block.new(location: loc, name: 'Foo') - pin1 = Solargraph::Pin::Parameter.new(closure: block, name: 'bar') - pin2 = Solargraph::Pin::Parameter.new(closure: block, name: 'bar', comments: 'a comment') + pin1 = described_class.new(closure: block, name: 'bar') + pin2 = described_class.new(closure: block, name: 'bar', comments: 'a comment') expect(pin1.combine_with(pin2).comments).to eq('a comment') end @@ -207,7 +209,7 @@ def baz; end ), 'test.rb') api_map = Solargraph::ApiMap.new api_map.map source - pin = api_map.source_map('test.rb').locals.select { |p| p.is_a?(Solargraph::Pin::Parameter) }.first + pin = api_map.source_map('test.rb').locals.select { |p| p.is_a?(described_class) }.first # expect(pin.infer(api_map)).to be_undefined expect(pin.typify(api_map)).to be_undefined expect(pin.probe(api_map)).to be_undefined diff --git a/spec/pin/search_spec.rb b/spec/pin/search_spec.rb index 28c302839..f505f45e9 100644 --- a/spec/pin/search_spec.rb +++ b/spec/pin/search_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Search do it 'returns ordered matches on paths' do example_class = Solargraph::Pin::Namespace.new(name: 'Example') @@ -6,7 +8,7 @@ Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), Solargraph::Pin::Method.new(name: 'foo_bar', closure: example_class) ] - search = Solargraph::Pin::Search.new(pins, 'example') + search = described_class.new(pins, 'example') expect(search.results).to eq(pins) end @@ -17,7 +19,7 @@ Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), Solargraph::Pin::Method.new(name: 'foo_bar', closure: example_class) ] - search = Solargraph::Pin::Search.new(pins, 'foobar') + search = described_class.new(pins, 'foobar') expect(search.results.map(&:path)).to eq(['Example.foobar', 'Example.foo_bar']) end @@ -28,7 +30,7 @@ Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), Solargraph::Pin::Method.new(name: 'bazquz', closure: example_class) ] - search = Solargraph::Pin::Search.new(pins, 'foobar') + search = described_class.new(pins, 'foobar') expect(search.results.map(&:path)).to eq(['Example.foobar']) end end diff --git a/spec/pin/symbol_spec.rb b/spec/pin/symbol_spec.rb index 141dbf463..e3226e300 100644 --- a/spec/pin/symbol_spec.rb +++ b/spec/pin/symbol_spec.rb @@ -1,53 +1,55 @@ +# frozen_string_literal: true + describe Solargraph::Pin::Symbol do context 'as an unquoted literal' do it 'is a kind of keyword to the LSP' do - pin = Solargraph::Pin::Symbol.new(nil, ':symbol') + pin = described_class.new(nil, ':symbol') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end it 'has global closure' do - pin = Solargraph::Pin::Symbol.new(nil, ':symbol') + pin = described_class.new(nil, ':symbol') expect(pin.closure).to eq(Solargraph::Pin::ROOT_PIN) end it 'has a Symbol return type' do - pin = Solargraph::Pin::Symbol.new(nil, ':symbol') + pin = described_class.new(nil, ':symbol') expect(pin.return_type.tag).to eq('Symbol') end end context 'as a double quoted literal' do it 'is a kind of keyword' do - pin = Solargraph::Pin::Symbol.new(nil, ':"symbol"') + pin = described_class.new(nil, ':"symbol"') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end it 'has a Symbol return type' do - pin = Solargraph::Pin::Symbol.new(nil, ':"symbol"') + pin = described_class.new(nil, ':"symbol"') expect(pin.return_type.tag).to eq('Symbol') end end context 'as a double quoted interpolatd literal' do it 'is a kind of keyword' do - pin = Solargraph::Pin::Symbol.new(nil, ':"symbol #{variable}"') + pin = described_class.new(nil, ':"symbol #{variable}"') expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end it 'has a Symbol return type' do - pin = Solargraph::Pin::Symbol.new(nil, ':"symbol #{variable}"') + pin = described_class.new(nil, ':"symbol #{variable}"') expect(pin.return_type.tag).to eq('Symbol') end end context 'as a single quoted literal' do it 'is a kind of keyword' do - pin = Solargraph::Pin::Symbol.new(nil, ":'symbol'") + pin = described_class.new(nil, ":'symbol'") expect(pin.completion_item_kind).to eq(Solargraph::LanguageServer::CompletionItemKinds::KEYWORD) end it 'has a Symbol return type' do - pin = Solargraph::Pin::Symbol.new(nil, ":'symbol'") + pin = described_class.new(nil, ":'symbol'") expect(pin.return_type.tag).to eq('Symbol') end end diff --git a/spec/position_spec.rb b/spec/position_spec.rb index 5612e8a5e..d61b05ce5 100644 --- a/spec/position_spec.rb +++ b/spec/position_spec.rb @@ -1,49 +1,51 @@ +# frozen_string_literal: true + describe Solargraph::Position do it 'normalizes arrays into positions' do - pos = Solargraph::Position.normalize([0, 1]) - expect(pos).to be_a(Solargraph::Position) + pos = described_class.normalize([0, 1]) + expect(pos).to be_a(described_class) expect(pos.line).to eq(0) expect(pos.column).to eq(1) end it 'returns original positions when normalizing' do - orig = Solargraph::Position.new(0, 1) - norm = Solargraph::Position.normalize(orig) + orig = described_class.new(0, 1) + norm = described_class.normalize(orig) expect(orig).to be(norm) end it 'finds offset from position' do text = "\n class Foo\n def bar baz, boo = 'boo'\n end\n end\n " - expect(Solargraph::Position.to_offset(text, Solargraph::Position.new(0, 0))).to eq(0) - expect(Solargraph::Position.to_offset(text, Solargraph::Position.new(0, 4))).to eq(4) - expect(Solargraph::Position.to_offset(text, Solargraph::Position.new(2, 12))).to eq(29) - expect(Solargraph::Position.to_offset(text, Solargraph::Position.new(2, 27))).to eq(44) - expect(Solargraph::Position.to_offset(text, Solargraph::Position.new(3, 8))).to eq(58) + expect(described_class.to_offset(text, described_class.new(0, 0))).to eq(0) + expect(described_class.to_offset(text, described_class.new(0, 4))).to eq(4) + expect(described_class.to_offset(text, described_class.new(2, 12))).to eq(29) + expect(described_class.to_offset(text, described_class.new(2, 27))).to eq(44) + expect(described_class.to_offset(text, described_class.new(3, 8))).to eq(58) end it 'constructs position from offset' do text = "\n class Foo\n def bar baz, boo = 'boo'\n end\n end\n " - expect(Solargraph::Position.from_offset(text, 0)).to eq(Solargraph::Position.new(0, 0)) - expect(Solargraph::Position.from_offset(text, 4)).to eq(Solargraph::Position.new(1, 3)) - expect(Solargraph::Position.from_offset(text, 29)).to eq(Solargraph::Position.new(2, 12)) - expect(Solargraph::Position.from_offset(text, 44)).to eq(Solargraph::Position.new(2, 27)) + expect(described_class.from_offset(text, 0)).to eq(described_class.new(0, 0)) + expect(described_class.from_offset(text, 4)).to eq(described_class.new(1, 3)) + expect(described_class.from_offset(text, 29)).to eq(described_class.new(2, 12)) + expect(described_class.from_offset(text, 44)).to eq(described_class.new(2, 27)) end it 'raises an error for objects that cannot be normalized' do expect do - Solargraph::Position.normalize('0, 1') + described_class.normalize('0, 1') end.to raise_error(ArgumentError) end it 'avoids fencepost errors' do text = " class Foo\n def bar baz, boo = 'boo'\n end\n end\n " - offset = Solargraph::Position.to_offset(text, Solargraph::Position.new(3, 6)) + offset = described_class.to_offset(text, described_class.new(3, 6)) expect(offset).to eq(67) end it 'avoids fencepost errors with multiple blank lines' do text = " class Foo\n def bar baz, boo = 'boo'\n\n end\n end\n " - offset = Solargraph::Position.to_offset(text, Solargraph::Position.new(4, 6)) + offset = described_class.to_offset(text, described_class.new(4, 6)) expect(offset).to eq(68) end end diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 0afd34e91..a633f104f 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::RbsMap::Conversions do context 'with RBS to digest' do # create a temporary directory with the scope of the spec @@ -12,7 +14,7 @@ let(:conversions) do loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false)) loader.add(path: Pathname(temp_dir)) - Solargraph::RbsMap::Conversions.new(loader: loader) + described_class.new(loader: loader) end let(:api_map) { Solargraph::ApiMap.new } diff --git a/spec/rbs_map/core_map_spec.rb b/spec/rbs_map/core_map_spec.rb index 229db4cc2..eefa16108 100644 --- a/spec/rbs_map/core_map_spec.rb +++ b/spec/rbs_map/core_map_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::RbsMap::CoreMap do it 'maps core Errno classes' do - map = Solargraph::RbsMap::CoreMap.new + map = described_class.new store = Solargraph::ApiMap::Store.new(map.pins) Errno.constants.each do |const| pin = store.get_path_pins("Errno::#{const}").first @@ -12,7 +14,7 @@ end it 'understands RBS class aliases' do - map = Solargraph::RbsMap::CoreMap.new + map = described_class.new store = Solargraph::ApiMap::Store.new(map.pins) # The core RBS contains: # class Mutex = Thread::Mutex @@ -25,7 +27,7 @@ end it 'understands RBS global variables' do - map = Solargraph::RbsMap::CoreMap.new + map = described_class.new store = Solargraph::ApiMap::Store.new(map.pins) global_variable_pins = store.pins_by_class(Solargraph::Pin::GlobalVariable) stderr_pins = global_variable_pins.select do |pin| @@ -79,7 +81,7 @@ # @todo This is a simple smoke test to ensure that mixins are applied # correctly. It would be better to test RbsMap or RbsMap::Conversions # with an RBS fixture. - core_map = Solargraph::RbsMap::CoreMap.new + core_map = described_class.new pins = core_map.pins.select { |pin| pin.is_a?(Solargraph::Pin::Reference::Include) && pin.name == 'Enumerable' } expect(pins.map(&:closure).map(&:namespace)).to include('Enumerator') end @@ -98,7 +100,7 @@ class Foo end it 'generates rooted pins from RBS for core' do - map = Solargraph::RbsMap::CoreMap.new + map = described_class.new map.pins.each do |pin| expect(pin).to be_all_rooted unless pin.is_a?(Solargraph::Pin::Keyword) diff --git a/spec/rbs_map/stdlib_map_spec.rb b/spec/rbs_map/stdlib_map_spec.rb index fe8c81b85..7af9365f8 100644 --- a/spec/rbs_map/stdlib_map_spec.rb +++ b/spec/rbs_map/stdlib_map_spec.rb @@ -1,18 +1,20 @@ +# frozen_string_literal: true + describe Solargraph::RbsMap::StdlibMap do it 'finds stdlib require paths' do - rbs_map = Solargraph::RbsMap::StdlibMap.load('fileutils') + rbs_map = described_class.load('fileutils') pin = rbs_map.path_pin('FileUtils#chdir') expect(pin).to be end it 'maps YAML' do - rbs_map = Solargraph::RbsMap::StdlibMap.load('yaml') + rbs_map = described_class.load('yaml') pin = rbs_map.path_pin('YAML') expect(pin).to be_a(Solargraph::Pin::Base) end it 'processes RBS module aliases' do - map = Solargraph::RbsMap::StdlibMap.load('yaml') + map = described_class.load('yaml') store = Solargraph::ApiMap::Store.new(map.pins) constant_pins = store.get_constants('') yaml_pins = constant_pins.select do |pin| @@ -25,7 +27,7 @@ end it 'pins are marked as coming from RBS parsing' do - map = Solargraph::RbsMap::StdlibMap.load('yaml') + map = described_class.load('yaml') store = Solargraph::ApiMap::Store.new(map.pins) constant_pins = store.get_constants('') pin = constant_pins.first diff --git a/spec/rbs_map_spec.rb b/spec/rbs_map_spec.rb index 4631c9ca5..09e7a1a80 100644 --- a/spec/rbs_map_spec.rb +++ b/spec/rbs_map_spec.rb @@ -1,25 +1,27 @@ +# frozen_string_literal: true + describe Solargraph::RbsMap do it 'loads from a gemspec' do spec = Gem::Specification.find_by_name('rbs') - rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + rbs_map = described_class.from_gemspec(spec, nil, nil) pin = rbs_map.path_pin('RBS::EnvironmentLoader#add_collection') expect(pin).not_to be_nil end it 'fails if it does not find data from gemspec' do spec = Gem::Specification.find_by_name('backport') - rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + rbs_map = described_class.from_gemspec(spec, nil, nil) expect(rbs_map).not_to be_resolved end it 'fails if it does not find data from name' do - rbs_map = Solargraph::RbsMap.new('lskdflaksdfjl') + rbs_map = described_class.new('lskdflaksdfjl') expect(rbs_map.pins).to be_empty end it 'converts constants and aliases to correct types' do spec = Gem::Specification.find_by_name('rbs') - rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + rbs_map = described_class.from_gemspec(spec, nil, nil) pin = rbs_map.path_pin('RBS::EnvironmentLoader::DEFAULT_CORE_ROOT') expect(pin.return_type.tag).to eq('Pathname') pin = rbs_map.path_pin('RBS::EnvironmentWalker::InstanceNode') @@ -28,7 +30,7 @@ it 'processes RBS class variables' do spec = Gem::Specification.find_by_name('rbs') - rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + rbs_map = described_class.from_gemspec(spec, nil, nil) store = Solargraph::ApiMap::Store.new(rbs_map.pins) class_variable_pins = store.pins_by_class(Solargraph::Pin::ClassVariable) count_pins = class_variable_pins.select do |pin| @@ -41,7 +43,7 @@ it 'processes RBS class instance variables' do spec = Gem::Specification.find_by_name('rbs') - rbs_map = Solargraph::RbsMap.from_gemspec(spec, nil, nil) + rbs_map = described_class.from_gemspec(spec, nil, nil) store = Solargraph::ApiMap::Store.new(rbs_map.pins) instance_variable_pins = store.pins_by_class(Solargraph::Pin::InstanceVariable) root_pins = instance_variable_pins.select do |pin| diff --git a/spec/source/chain/array_spec.rb b/spec/source/chain/array_spec.rb index 34eeafe01..bb93820cd 100644 --- a/spec/source/chain/array_spec.rb +++ b/spec/source/chain/array_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Array do it 'resolves an instance of an array' do literal = described_class.new([], nil) diff --git a/spec/source/chain/call_spec.rb b/spec/source/chain/call_spec.rb index d342e7b18..26b58ffca 100644 --- a/spec/source/chain/call_spec.rb +++ b/spec/source/chain/call_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Call do it 'recognizes core methods that return subtypes' do api_map = Solargraph::ApiMap.new diff --git a/spec/source/chain/class_variable_spec.rb b/spec/source/chain/class_variable_spec.rb index 65fcd0e5d..1a4179fce 100644 --- a/spec/source/chain/class_variable_spec.rb +++ b/spec/source/chain/class_variable_spec.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::ClassVariable do it 'resolves class variable pins' do foo_pin = Solargraph::Pin::ClassVariable.new(name: '@@foo') bar_pin = Solargraph::Pin::ClassVariable.new(name: '@@bar') api_map = double(Solargraph::ApiMap, get_class_variable_pins: [foo_pin, bar_pin]) - link = Solargraph::Source::Chain::ClassVariable.new('@@bar') + link = described_class.new('@@bar') pins = link.resolve(api_map, Solargraph::Pin::ROOT_PIN, []) expect(pins.length).to eq(1) expect(pins.first.name).to eq('@@bar') diff --git a/spec/source/chain/constant_spec.rb b/spec/source/chain/constant_spec.rb index f809783ef..6fbf54bff 100644 --- a/spec/source/chain/constant_spec.rb +++ b/spec/source/chain/constant_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Constant do it 'resolves constants in the current context' do foo_pin = Solargraph::Pin::Constant.new(name: 'Foo', closure: Solargraph::Pin::ROOT_PIN) diff --git a/spec/source/chain/global_variable_spec.rb b/spec/source/chain/global_variable_spec.rb index 4d0ed4056..ec89fd599 100644 --- a/spec/source/chain/global_variable_spec.rb +++ b/spec/source/chain/global_variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::GlobalVariable do it 'resolves instance variable pins' do closure = Solargraph::Pin::Namespace.new(name: 'Foo') @@ -5,7 +7,7 @@ not_pin = Solargraph::Pin::InstanceVariable.new(closure: closure, name: '@bar') api_map = Solargraph::ApiMap.new api_map.index [foo_pin, not_pin] - link = Solargraph::Source::Chain::GlobalVariable.new('$foo') + link = described_class.new('$foo') pins = link.resolve(api_map, Solargraph::ComplexType.parse('Foo'), []) expect(pins.length).to eq(1) expect(pins.first.name).to eq('$foo') diff --git a/spec/source/chain/head_spec.rb b/spec/source/chain/head_spec.rb index 7d4e5d9ea..6526d4747 100644 --- a/spec/source/chain/head_spec.rb +++ b/spec/source/chain/head_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Head do it 'returns self pins' do - head = Solargraph::Source::Chain::Head.new('self') + head = described_class.new('self') npin = Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.parse('Foo')) ipin = head.resolve(nil, npin, []).first expect(ipin.return_type.namespace).to eq('Foo') diff --git a/spec/source/chain/instance_variable_spec.rb b/spec/source/chain/instance_variable_spec.rb index fd956f770..29ef714ee 100644 --- a/spec/source/chain/instance_variable_spec.rb +++ b/spec/source/chain/instance_variable_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::InstanceVariable do it 'resolves instance variable pins' do closure = Solargraph::Pin::Namespace.new(name: 'Foo', @@ -18,8 +20,8 @@ api_map = Solargraph::ApiMap.new api_map.index [closure, methpin, foo_pin, bar_pin] - link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, - Solargraph::Location.new('test.rb', Solargraph::Range.from_to(2, 2, 2, 3))) + link = described_class.new('@foo', nil, + Solargraph::Location.new('test.rb', Solargraph::Range.from_to(2, 2, 2, 3))) pins = link.resolve(api_map, methpin, []) expect(pins.length).to eq(1) @@ -29,8 +31,8 @@ name_pin = Solargraph::Pin::ProxyType.anonymous(closure.binder, # Closure is the class closure: closure) - link = Solargraph::Source::Chain::InstanceVariable.new('@foo', nil, - Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 1, 5, 2))) + link = described_class.new('@foo', nil, + Solargraph::Location.new('test.rb', Solargraph::Range.from_to(5, 1, 5, 2))) pins = link.resolve(api_map, name_pin, []) expect(pins.length).to eq(1) expect(pins.first.name).to eq('@foo') diff --git a/spec/source/chain/link_spec.rb b/spec/source/chain/link_spec.rb index c492d0ba3..b303ee5b8 100644 --- a/spec/source/chain/link_spec.rb +++ b/spec/source/chain/link_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Link do it 'is undefined by default' do link = described_class.new diff --git a/spec/source/chain/literal_spec.rb b/spec/source/chain/literal_spec.rb index d3f759ea0..ce75772c5 100644 --- a/spec/source/chain/literal_spec.rb +++ b/spec/source/chain/literal_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Literal do it 'resolves an instance of a literal' do literal = described_class.new('String', nil) diff --git a/spec/source/chain/or_spec.rb b/spec/source/chain/or_spec.rb index 084738fe3..4a36dfe7c 100644 --- a/spec/source/chain/or_spec.rb +++ b/spec/source/chain/or_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::Or do it 'handles simple nil-removal' do source = Solargraph::Source.load_string(%( diff --git a/spec/source/chain/q_call_spec.rb b/spec/source/chain/q_call_spec.rb index a63568358..21994fb2d 100644 --- a/spec/source/chain/q_call_spec.rb +++ b/spec/source/chain/q_call_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::QCall do it 'understands &. in chains' do source = Solargraph::Source.load_string(%( diff --git a/spec/source/chain/z_super_spec.rb b/spec/source/chain/z_super_spec.rb index 4739ee195..fc6f5a554 100644 --- a/spec/source/chain/z_super_spec.rb +++ b/spec/source/chain/z_super_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::Source::Chain::ZSuper do it 'resolves super' do - head = Solargraph::Source::Chain::ZSuper.new('super') + head = described_class.new('super') npin = Solargraph::Pin::Namespace.new(name: 'Substring') scpin = Solargraph::Pin::Reference::Superclass.new(closure: npin, name: 'String') mpin = Solargraph::Pin::Method.new(closure: npin, name: 'upcase', scope: :instance, visibility: :public) diff --git a/spec/source/change_spec.rb b/spec/source/change_spec.rb index 7ef02984a..b9e019cfd 100644 --- a/spec/source/change_spec.rb +++ b/spec/source/change_spec.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + describe Solargraph::Source::Change do it 'inserts a character' do text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '.' - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.write(text) expect(updated).to eq('var.') end @@ -12,7 +14,7 @@ text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '.' - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.write(text, true) expect(updated).to eq('var ') end @@ -21,14 +23,14 @@ text = 'var' range = Solargraph::Range.from_to(0, 3, 0, 3) new_text = '._(!' - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.repair(text) expect(updated).to eq('var ') end it 'repairs nil ranges' do text = 'original' - change = Solargraph::Source::Change.new(nil, '...') + change = described_class.new(nil, '...') updated = change.repair(text) expect(updated).to eq(' ') end @@ -36,7 +38,7 @@ it 'overwrites nil ranges' do text = 'foo' new_text = 'bar' - change = Solargraph::Source::Change.new(nil, new_text) + change = described_class.new(nil, new_text) updated = change.write(text) expect(updated).to eq('bar') end @@ -45,7 +47,7 @@ text = 'bar' new_text = ':' range = Solargraph::Range.from_to(0, 3, 0, 3) - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.write(text, true) expect(updated).to eq('bar ') end @@ -54,7 +56,7 @@ text = 'bar:' new_text = ':' range = Solargraph::Range.from_to(0, 4, 0, 4) - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.write(text, true) expect(updated).to eq('bar ') end @@ -63,7 +65,7 @@ text = 'bar.' new_text = ' ' range = Solargraph::Range.from_to(0, 4, 0, 4) - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.repair(text) expect(updated).to eq('bar ') end @@ -72,7 +74,7 @@ text = 'bar:' new_text = 'x' range = Solargraph::Range.from_to(0, 4, 0, 4) - change = Solargraph::Source::Change.new(range, new_text) + change = described_class.new(range, new_text) updated = change.repair(text) expect(updated).to eq('bar ') end diff --git a/spec/source/source_chainer_spec.rb b/spec/source/source_chainer_spec.rb index ae672b095..3f9b4718d 100644 --- a/spec/source/source_chainer_spec.rb +++ b/spec/source/source_chainer_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::SourceChainer do it 'handles trailing colons that are not namespace separators' do source = Solargraph::Source.load_string('Foo:') @@ -111,14 +113,14 @@ ] ) source = orig.synchronize(updater) - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 3)) + chain = described_class.chain(source, Solargraph::Position.new(0, 3)) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Literal) expect(chain.links.length).to eq(2) end it 'chains incomplete constants' do source = Solargraph::Source.load_string('Foo::') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 5)) + chain = described_class.chain(source, Solargraph::Position.new(0, 5)) expect(chain.links.length).to eq(2) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Constant) expect(chain.links.last).to be_a(Solargraph::Source::Chain::Constant) @@ -132,7 +134,7 @@ ]) source = orig.synchronize(updater) expect do - Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 4)) + described_class.chain(source, Solargraph::Position.new(1, 4)) end.not_to raise_error end @@ -142,11 +144,11 @@ [bb2, 2, 3] {cc3, 2, 3} )) - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 10)) + chain = described_class.chain(source, Solargraph::Position.new(1, 10)) expect(chain.links.first.word).to eq('aa1') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(2, 10)) + chain = described_class.chain(source, Solargraph::Position.new(2, 10)) expect(chain.links.first.word).to eq('bb2') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(3, 10)) + chain = described_class.chain(source, Solargraph::Position.new(3, 10)) expect(chain.links.first.word).to eq('cc3') end @@ -162,7 +164,7 @@ error_ranges: [], node_at: nil, tree_at: []) - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 5)) + chain = described_class.chain(source, Solargraph::Position.new(0, 5)) expect(chain.links.first.word).to eq('@foo') expect(chain.links.last.word).to eq('') end @@ -179,7 +181,7 @@ error_ranges: [], node_at: nil, tree_at: []) - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 6)) + chain = described_class.chain(source, Solargraph::Position.new(0, 6)) expect(chain.links.first.word).to eq('@@foo') expect(chain.links.last.word).to eq('') end @@ -198,7 +200,7 @@ ) ] )) - chain = Solargraph::Source::SourceChainer.chain(source2, Solargraph::Position.new(1, 9)) + chain = described_class.chain(source2, Solargraph::Position.new(1, 9)) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Literal) expect(chain.links.first.word).to eq('<::String>') expect(chain.links.last.word).to eq('') @@ -208,7 +210,7 @@ source = Solargraph::Source.load_string(%( if !t ), 'test.rb') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 11)) + chain = described_class.chain(source, Solargraph::Position.new(1, 11)) expect(chain.links.length).to eq(1) expect(chain.links.first.word).to eq('t') end @@ -228,7 +230,7 @@ it 'handles integers with dots at end of file' do source = Solargraph::Source.load_string('100.') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(0, 4)) + chain = described_class.chain(source, Solargraph::Position.new(0, 4)) expect(chain.links.length).to eq(2) expect(chain.links.first).to be_a(Solargraph::Source::Chain::Literal) expect(chain.links.last).to be_undefined @@ -242,7 +244,7 @@ class Inner end Outer::Inner.new ), 'test.rb') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(5, 13)) + chain = described_class.chain(source, Solargraph::Position.new(5, 13)) expect(chain.links.last.word).to eq('Outer::Inner') end @@ -256,7 +258,7 @@ class Inner2 end Outer::Inner1::Inner2.new ), 'test.rb') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(7, 21)) + chain = described_class.chain(source, Solargraph::Position.new(7, 21)) expect(chain.links.last.word).to eq('Outer::Inner1::Inner2') end @@ -264,7 +266,7 @@ class Inner2 source = Solargraph::Source.load_string(%( foo(*optargs, **kwargs) ), 'test.rb') - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 7)) + chain = described_class.chain(source, Solargraph::Position.new(1, 7)) expect(chain.links.last.arguments.length).to eq(2) end @@ -276,7 +278,7 @@ class Inner2 api_map = Solargraph::ApiMap.new api_map.map source - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(2, 9)) + chain = described_class.chain(source, Solargraph::Position.new(2, 9)) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals) expect(type.tag).to eq('Array') end @@ -288,7 +290,7 @@ class Inner2 api_map = Solargraph::ApiMap.new api_map.map source - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 16)) + chain = described_class.chain(source, Solargraph::Position.new(1, 16)) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals) expect(type.tag).to eq('Array(String, Integer)') end @@ -302,7 +304,7 @@ class Inner2 api_map = Solargraph::ApiMap.new api_map.map source - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(3, 6)) + chain = described_class.chain(source, Solargraph::Position.new(3, 6)) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals) expect(type.rooted_tag).to eq('::Boolean') end @@ -314,7 +316,7 @@ class Inner2 api_map = Solargraph::ApiMap.new api_map.map source - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 16)) + chain = described_class.chain(source, Solargraph::Position.new(1, 16)) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals) expect(type.tag).to eq('Array') end @@ -334,7 +336,7 @@ def strings; end updated = source.synchronize(updater) api_map = Solargraph::ApiMap.new api_map.map updated - chain = Solargraph::Source::SourceChainer.chain(updated, Solargraph::Position.new(5, 14)) + chain = described_class.chain(updated, Solargraph::Position.new(5, 14)) expect(chain.node.type).to be(:send) expect(chain.node.children[1]).to be(:s) end @@ -345,7 +347,7 @@ def strings; end z end )) - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 9)) + chain = described_class.chain(source, Solargraph::Position.new(1, 9)) expect(chain.links.map(&:class)).to be end @@ -356,7 +358,7 @@ def strings; end api_map = Solargraph::ApiMap.new api_map.map source - chain = Solargraph::Source::SourceChainer.chain(source, Solargraph::Position.new(1, 20)) + chain = described_class.chain(source, Solargraph::Position.new(1, 20)) type = chain.infer(api_map, Solargraph::Pin::ROOT_PIN, api_map.source_map('test.rb').locals) expect(type.tag).to eq('Array') end diff --git a/spec/source/updater_spec.rb b/spec/source/updater_spec.rb index 580d41521..373072e0d 100644 --- a/spec/source/updater_spec.rb +++ b/spec/source/updater_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::Source::Updater do it 'applies changes' do text = 'foo' @@ -8,7 +10,7 @@ range = Solargraph::Range.from_to(0, 4, 0, 4) new_text = 'bar' changes.push Solargraph::Source::Change.new(range, new_text) - updater = Solargraph::Source::Updater.new('file.rb', 0, changes) + updater = described_class.new('file.rb', 0, changes) updated = updater.write(text) expect(updated).to eq('foo.bar') end @@ -22,7 +24,7 @@ range = Solargraph::Range.from_to(0, 4, 0, 4) new_text = 'bar' changes.push Solargraph::Source::Change.new(range, new_text) - updater = Solargraph::Source::Updater.new('file.rb', 0, changes) + updater = described_class.new('file.rb', 0, changes) updated = updater.repair(text) expect(updated).to eq('foo ') end @@ -33,7 +35,7 @@ range = nil new_text = 'bar' changes.push Solargraph::Source::Change.new(range, new_text) - updater = Solargraph::Source::Updater.new('file.rb', 0, changes) + updater = described_class.new('file.rb', 0, changes) updated = updater.write(text) expect(updated).to eq('bar') end diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 71f5369ff..a21579575 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::SourceMap::Clip do let(:api_map) { Solargraph::ApiMap.new } @@ -356,7 +358,7 @@ def spec_for_require path map.map source clip = map.clip_at('test.rb', Solargraph::Position.new(10, 10)) type = clip.infer - expect(type.to_s.split(',').map(&:strip).to_set).to eq(Set.new(['Gem::Specification'])) + expect(type.to_s.split(',').to_set(&:strip)).to eq(Set.new(['Gem::Specification'])) end it 'infers return types from method calls' do diff --git a/spec/source_map/mapper_spec.rb b/spec/source_map/mapper_spec.rb index f6cc7279e..1a79e5683 100644 --- a/spec/source_map/mapper_spec.rb +++ b/spec/source_map/mapper_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::SourceMap::Mapper do it 'ignores include calls that are not attached to the current namespace' do source = Solargraph::Source.new(%( @@ -1230,7 +1232,7 @@ def bar; end # @!override Foo#bar # return [String] ), 'test.rb') - pins, _locals = Solargraph::SourceMap::Mapper.map(source) + pins, _locals = described_class.map(source) over = pins.select { |pin| pin.is_a?(Solargraph::Pin::Reference::Override) }.first expect(over.name).to eq('Foo#bar') end @@ -1241,7 +1243,7 @@ class Foo def bar(**baz); end end )) - _pins, locals = Solargraph::SourceMap::Mapper.map(source) + _pins, locals = described_class.map(source) param = locals.select { |pin| pin.is_a?(Solargraph::Pin::Parameter) }.first expect(param).to be_kwrestarg end @@ -1252,7 +1254,7 @@ class Foo def bar(baz = {}); end end )) - _pins, locals = Solargraph::SourceMap::Mapper.map(source) + _pins, locals = described_class.map(source) param = locals.select { |pin| pin.is_a?(Solargraph::Pin::Parameter) }.first expect(param).to be_kwrestarg end @@ -1263,7 +1265,7 @@ def bar(baz = {}); end var = 'var' end )) - _pins, locals = Solargraph::SourceMap::Mapper.map(source) + _pins, locals = described_class.map(source) expect(locals).to be_one end @@ -1283,7 +1285,7 @@ class Foo alias_method end )) - pins, locals = Solargraph::SourceMap::Mapper.map(source) + pins, locals = described_class.map(source) expect(pins).to be_one expect(locals).to be_empty end diff --git a/spec/source_map/node_processor_spec.rb b/spec/source_map/node_processor_spec.rb index a0ce0bc91..7d3fca237 100644 --- a/spec/source_map/node_processor_spec.rb +++ b/spec/source_map/node_processor_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe 'Node processor (generic)' do it 'maps arg parameters' do map = Solargraph::SourceMap.load_string(%( diff --git a/spec/source_map_spec.rb b/spec/source_map_spec.rb index 8dba8c0b4..a5fcea154 100644 --- a/spec/source_map_spec.rb +++ b/spec/source_map_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::SourceMap do it 'locates named path pins' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class Foo def bar; end end @@ -10,7 +12,7 @@ def bar; end end it 'queries symbols using fuzzy matching' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class FooBar def baz_qux; end end @@ -23,7 +25,7 @@ def baz_qux; end end it 'returns all pins, except for references as document symbols' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class FooBar require 'foo' include SomeModule @@ -56,7 +58,7 @@ def local source_map Solargraph::Convention.register dummy_convention - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class FooBar def baz_qux; end end @@ -68,7 +70,7 @@ def baz_qux; end end it 'locates block pins' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class Foo 100.times do end @@ -79,7 +81,7 @@ class Foo end it 'scopes local variables correctly from root def methods' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( x = 'string' def foo x @@ -91,7 +93,7 @@ def foo end it 'scopes local variables correctly from class methods' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class Foo x = 'string' def foo @@ -110,7 +112,7 @@ def foo ENV['SOLARGRAPH_ASSERTS'] = 'on' expect do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( Foo.bar += baz ), 'test.rb') loc = Solargraph::Location.new('test.rb', Solargraph::Range.from_to(3, 9, 3, 9)) @@ -127,7 +129,7 @@ def foo ENV['SOLARGRAPH_ASSERTS'] = 'on' expect do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( Foo.bar ||= baz ), 'test.rb') loc = Solargraph::Location.new('test.rb', Solargraph::Range.from_to(3, 9, 3, 9)) @@ -144,7 +146,7 @@ def foo ENV['SOLARGRAPH_ASSERTS'] = 'on' expect do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( Foo.bar = baz ), 'test.rb') loc = Solargraph::Location.new('test.rb', Solargraph::Range.from_to(3, 9, 3, 9)) @@ -156,7 +158,7 @@ def foo end it 'scopes local variables correctly in class_eval blocks' do - map = Solargraph::SourceMap.load_string(%( + map = described_class.load_string(%( class Foo; end x = 'y' Foo.class_eval do @@ -169,12 +171,12 @@ class Foo; end end it 'updates cached inference when the ApiMap changes' do - file1 = Solargraph::SourceMap.load_string(%( + file1 = described_class.load_string(%( def foo '' end ), 'file1.rb') - file2 = Solargraph::SourceMap.load_string(%( + file2 = described_class.load_string(%( foo ), 'file2.rb') @@ -186,7 +188,7 @@ def foo original_api_map_hash = api_map.hash original_source_map_hash = file1.hash - file1 = Solargraph::SourceMap.load_string(%( + file1 = described_class.load_string(%( def foo [] end diff --git a/spec/source_spec.rb b/spec/source_spec.rb index 53c7ba4d3..6357924f2 100644 --- a/spec/source_spec.rb +++ b/spec/source_spec.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + describe Solargraph::Source do it 'parses code' do code = 'class Foo;def bar;end;end' source = described_class.new(code) expect(source.code).to eq(code) - expect(Solargraph::Parser.is_ast_node?(source.node)).to be_truthy + expect(Solargraph::Parser).to be_is_ast_node(source.node) expect(source).to be_parsed end @@ -101,7 +103,7 @@ def bar end it 'finds references' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( class Foo def bar end @@ -124,7 +126,7 @@ def bar= end it 'allows escape sequences incompatible with UTF-8' do - source = Solargraph::Source.new(' + source = described_class.new(' x = " Un bUen café \x92" puts x ') @@ -133,18 +135,18 @@ def bar= it 'fixes invalid byte sequences in UTF-8 encoding' do expect do - Solargraph::Source.load('spec/fixtures/invalid_byte.rb') + described_class.load('spec/fixtures/invalid_byte.rb') end.not_to raise_error end it 'loads files with Unicode characters' do expect do - Solargraph::Source.load('spec/fixtures/unicode.rb') + described_class.load('spec/fixtures/unicode.rb') end.not_to raise_error end it 'updates itself when code does not change' do - original = Solargraph::Source.load_string('x = y', 'test.rb') + original = described_class.load_string('x = y', 'test.rb') updater = Solargraph::Source::Updater.new('test.rb', 1, []) updated = original.synchronize(updater) expect(original).to be(updated) @@ -152,7 +154,7 @@ def bar= end it 'handles unparseable code' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( 100.times do |num| )) # @todo Unparseable code results in a nil node for now, but that could @@ -163,7 +165,7 @@ def bar= it 'finds foldable ranges' do # Of the 7 possible ranges, 2 are too short to be foldable - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( =begin Range 1 =end @@ -192,7 +194,7 @@ def range_2 end it 'folds multiline strings' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( a = 1 b = 2 c = 3 @@ -207,7 +209,7 @@ def range_2 end it 'folds multiline arrays' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( a = 1 b = 2 c = 3 @@ -222,7 +224,7 @@ def range_2 end it 'folds multiline hashes' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( a = 1 b = 2 c = 3 @@ -237,7 +239,7 @@ def range_2 end it 'finishes synchronizations for unbalanced lines' do - source1 = Solargraph::Source.load_string('x = 1', 'test.rb') + source1 = described_class.load_string('x = 1', 'test.rb') source2 = source1.synchronize Solargraph::Source::Updater.new( 'test.rb', 2, @@ -254,7 +256,7 @@ def range_2 it 'handles comment arrays that overlap lines' do # Fixes negative argument error (castwide/solargraph#141) - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( =begin =end y = 1 #foo @@ -266,7 +268,7 @@ def range_2 end it 'formats comments with multiple hash prefixes' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( ## # one # two @@ -278,7 +280,7 @@ class Foo; end end it 'does not include inner comments' do - source = Solargraph::Source.load_string(%( + source = described_class.load_string(%( # included class Foo # ignored @@ -291,12 +293,12 @@ class Foo end it 'handles long squiggly heredocs' do - source = Solargraph::Source.load('spec/fixtures/long_squiggly_heredoc.rb') + source = described_class.load('spec/fixtures/long_squiggly_heredoc.rb') expect(source.string_ranges).not_to be_empty end it 'handles string array substitutions' do - source = Solargraph::Source.load_string( + source = described_class.load_string( '%W[array of words #{\'with a substitution\'}]' ) expect(source.string_ranges.length).to eq(4) @@ -305,6 +307,6 @@ class Foo it 'handles errors in docstrings' do # YARD has a known problem with empty @overload tags comments = "@overload\n@return [String]" - expect { Solargraph::Source.parse_docstring(comments) }.not_to raise_error + expect { described_class.parse_docstring(comments) }.not_to raise_error end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62280f335..9f320e688 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'bundler/setup' require 'webmock/rspec' require 'rspec_time_guard' diff --git a/spec/type_checker/levels/normal_spec.rb b/spec/type_checker/levels/normal_spec.rb index 1a9e589da..57702f3cb 100644 --- a/spec/type_checker/levels/normal_spec.rb +++ b/spec/type_checker/levels/normal_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker do context 'when checking at normal level' do def type_checker code @@ -609,7 +611,7 @@ def bar end it 'verifies block passes in arguments' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( class Foo def map(&block) block.call(100) @@ -624,7 +626,7 @@ def to_s end it 'verifies args and block passes' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( class Foo def map(x, &block) block.call(x) @@ -639,14 +641,14 @@ def to_s end it 'verifies extra block passes in chained calls' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( ''.to_s(&:nil?) ), 'test.rb') expect(checker.problems).to be_empty end it 'verifies extra block variables in calls with args' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( def foo(bar); end foo(1, &block) ), 'test.rb') @@ -654,7 +656,7 @@ def foo(bar); end end it 'verifies splats passed to arguments' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( def foo(bar, baz); end foo(*splat) ), 'test.rb') diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 100f91ddb..9c40684b8 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker do context 'strict level' do # @return [Solargraph::TypeChecker] @@ -105,7 +107,7 @@ def bar(a); end ), 'test.rb') api_map = Solargraph::ApiMap.load '.' api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) - checker = Solargraph::TypeChecker.new('test.rb', api_map: api_map, level: :strict) + checker = described_class.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty end diff --git a/spec/type_checker/levels/strong_spec.rb b/spec/type_checker/levels/strong_spec.rb index e2c871407..50cabbe4e 100644 --- a/spec/type_checker/levels/strong_spec.rb +++ b/spec/type_checker/levels/strong_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker do context 'strong level' do def type_checker code diff --git a/spec/type_checker/levels/typed_spec.rb b/spec/type_checker/levels/typed_spec.rb index 0cbdb83d9..86030735c 100644 --- a/spec/type_checker/levels/typed_spec.rb +++ b/spec/type_checker/levels/typed_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker do context 'typed level' do def type_checker code diff --git a/spec/type_checker/rules_spec.rb b/spec/type_checker/rules_spec.rb index ded5302fa..1ecc30714 100644 --- a/spec/type_checker/rules_spec.rb +++ b/spec/type_checker/rules_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + describe Solargraph::TypeChecker::Rules do it 'sets normal rules' do - rules = Solargraph::TypeChecker::Rules.new(:normal, {}) + rules = described_class.new(:normal, {}) expect(rules.ignore_all_undefined?).to be(true) expect(rules.must_tag_or_infer?).to be(false) expect(rules.require_type_tags?).to be(false) @@ -9,7 +11,7 @@ end it 'sets typed rules' do - rules = Solargraph::TypeChecker::Rules.new(:typed, {}) + rules = described_class.new(:typed, {}) expect(rules.ignore_all_undefined?).to be(true) expect(rules.must_tag_or_infer?).to be(false) expect(rules.require_type_tags?).to be(false) @@ -18,7 +20,7 @@ end it 'sets strict rules' do - rules = Solargraph::TypeChecker::Rules.new(:strict, {}) + rules = described_class.new(:strict, {}) expect(rules.ignore_all_undefined?).to be(false) expect(rules.must_tag_or_infer?).to be(true) expect(rules.require_type_tags?).to be(false) @@ -27,7 +29,7 @@ end it 'sets strong rules' do - rules = Solargraph::TypeChecker::Rules.new(:strong, {}) + rules = described_class.new(:strong, {}) expect(rules.ignore_all_undefined?).to be(false) expect(rules.must_tag_or_infer?).to be(true) expect(rules.require_type_tags?).to be(true) diff --git a/spec/type_checker_spec.rb b/spec/type_checker_spec.rb index 4597c6d75..3113896d2 100644 --- a/spec/type_checker_spec.rb +++ b/spec/type_checker_spec.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'timeout' describe Solargraph::TypeChecker do it 'does not raise errors checking unparsed sources' do expect do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( foo{ )) checker.problems @@ -11,7 +13,7 @@ end it 'ignores tagged problems' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( NotAClass # @sg-ignore @@ -21,7 +23,7 @@ end it 'uses caching in Solargraph::Chain to handle a degenerate case' do - checker = Solargraph::TypeChecker.load_string(%( + checker = described_class.load_string(%( def documentation @documentation = "a" @documentation += "b" diff --git a/spec/workspace/config_spec.rb b/spec/workspace/config_spec.rb index 4d8245d1b..7bb04ba7a 100644 --- a/spec/workspace/config_spec.rb +++ b/spec/workspace/config_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fileutils' require 'tmpdir' @@ -9,7 +11,7 @@ it 'includes .rb files by default' do file = File.join(dir_path, 'file.rb') File.write(file, 'exit') - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.calculated).to include(file) end @@ -17,7 +19,7 @@ Dir.mkdir(File.join(dir_path, 'lib')) file = File.join(dir_path, 'lib', 'file.rb') File.write(file, 'exit') - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.calculated).to include(file) end @@ -25,7 +27,7 @@ Dir.mkdir(File.join(dir_path, 'test')) file = File.join(dir_path, 'test', 'file.rb') File.write(file, 'exit') - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.calculated).not_to include(file) end @@ -33,7 +35,7 @@ Dir.mkdir(File.join(dir_path, 'spec')) file = File.join(dir_path, 'spec', 'file.rb') File.write(file, 'exit') - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.calculated).not_to include(file) end @@ -41,12 +43,12 @@ Dir.mkdir(File.join(dir_path, 'vendor')) file = File.join(dir_path, 'vendor', 'file.rb') File.write(file, 'exit') - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.calculated).not_to include(file) end it 'includes base reporters by default' do - config = Solargraph::Workspace::Config.new(dir_path) + config = described_class.new(dir_path) expect(config.reporters).to include('rubocop') expect(config.reporters).to include('require_not_found') end diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 0489b06a4..e638a42e1 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fileutils' require 'tmpdir' @@ -53,7 +55,7 @@ config = double(:config, calculated: Array.new(Solargraph::Workspace::Config::MAX_FILES + 1), max_files: Solargraph::Workspace::Config::MAX_FILES) expect do - Solargraph::Workspace.new('.', config) + described_class.new('.', config) end.to raise_error(Solargraph::WorkspaceTooLargeError) end @@ -64,7 +66,7 @@ # @todo Mock reveals tight coupling config = double(:config, calculated: calculated, max_files: 0, allow?: true, require_paths: [], plugins: []) expect do - Solargraph::Workspace.new('.', config) + described_class.new('.', config) end.not_to raise_error end @@ -123,14 +125,14 @@ end it 'uses configured require paths' do - workspace = Solargraph::Workspace.new('spec/fixtures/workspace') + workspace = described_class.new('spec/fixtures/workspace') expect(workspace.require_paths).to eq([File.absolute_path('spec/fixtures/workspace/lib'), File.absolute_path('spec/fixtures/workspace/ext')]) end it 'ignores gemspecs in excluded directories' do # vendor/**/* is excluded by default - workspace = Solargraph::Workspace.new('spec/fixtures/vendored') + workspace = described_class.new('spec/fixtures/vendored') expect(workspace.require_paths).to eq([File.absolute_path('spec/fixtures/vendored/lib')]) end @@ -138,7 +140,7 @@ config = double(:Config, directory: './path', calculated: ['./path/does_not_exist.rb'], max_files: 5000, require_paths: [], plugins: []) expect do - Solargraph::Workspace.new('./path', config) + described_class.new('./path', config) end.not_to raise_error end diff --git a/spec/yard_map/mapper/to_method_spec.rb b/spec/yard_map/mapper/to_method_spec.rb index e406f00a4..c3a46b914 100644 --- a/spec/yard_map/mapper/to_method_spec.rb +++ b/spec/yard_map/mapper/to_method_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::YardMap::Mapper::ToMethod do let(:code_object) do namespace = YARD::CodeObjects::ModuleObject.new(nil, 'Example') @@ -6,7 +8,7 @@ it 'parses args' do code_object.parameters = [['bar', nil]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:arg) expect(param.name).to eq('bar') @@ -15,7 +17,7 @@ it 'parses optargs' do code_object.parameters = [['bar', "'baz'"]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:optarg) expect(param.name).to eq('bar') @@ -24,7 +26,7 @@ it 'parses kwargs' do code_object.parameters = [['bar:', nil]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.name).to eq('bar') expect(param.decl).to be(:kwarg) @@ -33,7 +35,7 @@ it 'parses kwoptargs' do code_object.parameters = [['bar:', "'baz'"]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:kwoptarg) expect(param.name).to eq('bar') @@ -42,7 +44,7 @@ it 'parses restargs' do code_object.parameters = [['*bar', nil]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:restarg) expect(param.name).to eq('bar') @@ -51,7 +53,7 @@ it 'parses kwrestargs' do code_object.parameters = [['**bar', nil]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:kwrestarg) expect(param.name).to eq('bar') @@ -60,7 +62,7 @@ it 'parses blockargs' do code_object.parameters = [['&bar', nil]] - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:blockarg) expect(param.name).to eq('bar') @@ -74,7 +76,7 @@ code_object.docstring = <<~EOF @yieldparam foo [Integer] EOF - pin = Solargraph::YardMap::Mapper::ToMethod.make(code_object) + pin = described_class.make(code_object) param = pin.parameters.first expect(param.decl).to be(:blockarg) expect(param.name).to eq('') diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index a246f73e3..43b5da7cf 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + describe Solargraph::YardMap::Mapper do # before :context here disables parallel tests in prspec, which # would be needed regardless as we are changing the working @@ -16,7 +18,7 @@ def pins_with require dir = File.absolute_path(File.join('spec', 'fixtures', 'yard_map')) Dir.chdir dir do YARD::Registry.load([File.join(dir, 'attr.rb')], true) - mapper = Solargraph::YardMap::Mapper.new(YARD::Registry.all) + mapper = described_class.new(YARD::Registry.all) pins = mapper.map pin = pins.select { |pin| pin.path == 'Foo#bar' }.first expect(pin.comments).to be_a(String) From d3cc5361cbd13b08615ceeb108c8aee97be49c93 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 17:20:56 -0500 Subject: [PATCH 060/206] Fix rubocop todo issues --- .rubocop_todo.yml | 15 --------------- spec/language_server/protocol_spec.rb | 18 +++++------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index de103f500..38fb2334f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -218,25 +218,10 @@ RSpec/MultipleExpectations: RSpec/NestedGroups: Max: 4 -# Configuration parameters: AllowedPatterns. -# AllowedPatterns: ^expect_, ^assert_ -RSpec/NoExpectationExample: - Exclude: - - 'spec/language_server/protocol_spec.rb' - RSpec/RemoveConst: Exclude: - 'spec/diagnostics/rubocop_helpers_spec.rb' -RSpec/RepeatedDescription: - Exclude: - - 'spec/language_server/protocol_spec.rb' - -# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. -RSpec/VerifiedDoubles: - Exclude: - - 'spec/language_server/protocol_spec.rb' - Security/MarshalLoad: Exclude: - 'lib/solargraph/pin_cache.rb' diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index bc9f30602..4e150c0c0 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -44,9 +44,9 @@ def stop end before do - version = double(:GemVersion, version: Gem::Version.new('1.0.0')) - Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = double(:fetcher, - search_for_dependency: [version]) + version = instance_double(Gem::Version, version: Gem::Version.new('1.0.0')) + Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = + instance_double(Gem::SpecFetcher, search_for_dependency: [version]) end after do @@ -149,8 +149,8 @@ def bar baz } ] } - @protocol.response - # @todo What to expect? + response = @protocol.response + expect(response).not_to be_nil end it 'handles textDocument/completion' do @@ -208,14 +208,6 @@ def bar baz expect(response['result']['documentation']).not_to be_empty end - it 'handles workspace/symbol' do - @protocol.request 'workspace/symbol', { - 'query' => 'test' - } - response = @protocol.response - expect(response['error']).to be_nil - end - it 'handles textDocument/definition' do sleep 0.5 # HACK: Give the Host::Sources thread time to work @protocol.request 'textDocument/definition', { From 6dc4f9409ce5fe5a8906be722e28f04fbea5da5c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 17:35:40 -0500 Subject: [PATCH 061/206] Debug --- spec/rbs_map/conversions_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index a633f104f..80a63521d 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -106,9 +106,13 @@ def bar: () -> untyped @api_map.catalog(bench) end - let(:api_map) { @api_map } + attr_reader :api_map context 'with superclass pin for Parser::AST::Node' do + before :context do + raise 'Invalid api_map' unless @api_map + end + let(:superclass_pin) do api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' From 99f55d39102695546c4e9bc06438301d4b026522 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 17:43:18 -0500 Subject: [PATCH 062/206] Revert some changes --- spec/rbs_map/conversions_spec.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 80a63521d..a633f104f 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -106,13 +106,9 @@ def bar: () -> untyped @api_map.catalog(bench) end - attr_reader :api_map + let(:api_map) { @api_map } context 'with superclass pin for Parser::AST::Node' do - before :context do - raise 'Invalid api_map' unless @api_map - end - let(:superclass_pin) do api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' From ee07001eaf0fe614f0773a1965c620d007f52029 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 17:50:23 -0500 Subject: [PATCH 063/206] Overwrite api_map in context --- spec/rbs_map/conversions_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index a633f104f..acd1ab1d2 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -123,6 +123,8 @@ def bar: () -> untyped # https://github.com/castwide/solargraph/issues/1042 context 'with Hash superclass with untyped value and alias' do + let(:api_map) { Solargraph::ApiMap.new } + let(:rbs) do <<~RBS class Sub < Hash[Symbol, untyped] From 99c82414728b8540d0fa5e64599db8bfbc57f648 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 17:55:51 -0500 Subject: [PATCH 064/206] Try another caching approach --- spec/rbs_map/conversions_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index acd1ab1d2..ff2f3e9d4 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -106,11 +106,9 @@ def bar: () -> untyped @api_map.catalog(bench) end - let(:api_map) { @api_map } - context 'with superclass pin for Parser::AST::Node' do let(:superclass_pin) do - api_map.pins.find do |pin| + @api_map.pins.find do |pin| pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' end end From 5a869472115d0bfee9dff0db718e3d33ac4174c4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 18:03:36 -0500 Subject: [PATCH 065/206] Try another caching approach --- spec/rbs_map/conversions_spec.rb | 68 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index ff2f3e9d4..9f3e34d75 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -95,7 +95,7 @@ def bar: () -> untyped end end - context 'with standard loads for solargraph project' do + context 'with superclass pin for Parser::AST::Node' do # Use :context here instead of :all so that parallel_rspec runs these on the same worker and we only have to cache these gems on one worker before :context do @api_map = Solargraph::ApiMap.load('.') @@ -106,51 +106,49 @@ def bar: () -> untyped @api_map.catalog(bench) end - context 'with superclass pin for Parser::AST::Node' do - let(:superclass_pin) do - @api_map.pins.find do |pin| - pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' - end + let(:superclass_pin) do + @api_map.pins.find do |pin| + pin.is_a?(Solargraph::Pin::Reference::Superclass) && pin.context.namespace == 'Parser::AST::Node' end + end - it 'generates a rooted pin' do - # rooted! - expect(superclass_pin&.name).to eq('::AST::Node') - end + it 'generates a rooted pin' do + # rooted! + expect(superclass_pin&.name).to eq('::AST::Node') end + end - # https://github.com/castwide/solargraph/issues/1042 - context 'with Hash superclass with untyped value and alias' do - let(:api_map) { Solargraph::ApiMap.new } + # https://github.com/castwide/solargraph/issues/1042 + context 'with Hash superclass with untyped value and alias' do + let(:api_map) { Solargraph::ApiMap.new } - let(:rbs) do - <<~RBS - class Sub < Hash[Symbol, untyped] - alias meth_alias [] - end - RBS - end + let(:rbs) do + <<~RBS + class Sub < Hash[Symbol, untyped] + alias meth_alias [] + end + RBS + end - let(:sup_method_stack) { api_map.get_method_stack('Hash{Symbol => undefined}', '[]', scope: :instance) } + let(:sup_method_stack) { api_map.get_method_stack('Hash{Symbol => undefined}', '[]', scope: :instance) } - let(:sub_alias_stack) { api_map.get_method_stack('Sub', 'meth_alias', scope: :instance) } + let(:sub_alias_stack) { api_map.get_method_stack('Sub', 'meth_alias', scope: :instance) } - it 'does not crash looking at superclass method' do - expect { sup_method_stack }.not_to raise_error - end + it 'does not crash looking at superclass method' do + expect { sup_method_stack }.not_to raise_error + end - it 'does not crash looking at alias' do - expect { sub_alias_stack }.not_to raise_error - end + it 'does not crash looking at alias' do + expect { sub_alias_stack }.not_to raise_error + end - it 'finds superclass method pin return type' do - expect(sup_method_stack.map(&:return_type).map(&:rooted_tags).uniq).to eq(['undefined']) - end + it 'finds superclass method pin return type' do + expect(sup_method_stack.map(&:return_type).map(&:rooted_tags).uniq).to eq(['undefined']) + end - it 'finds superclass method pin parameter type' do - expect(sup_method_stack.flat_map(&:signatures).flat_map(&:parameters).map(&:return_type).map(&:rooted_tags) - .uniq).to eq(['Symbol']) - end + it 'finds superclass method pin parameter type' do + expect(sup_method_stack.flat_map(&:signatures).flat_map(&:parameters).map(&:return_type).map(&:rooted_tags) + .uniq).to eq(['Symbol']) end end From 9eb3a6ab252c0c7e10faf8799c87613856b98370 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 18:17:18 -0500 Subject: [PATCH 066/206] Fix merge issue --- spec/api_map_method_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 83b789774..2f1dc7c8d 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -119,9 +119,6 @@ class B end describe '#get_method_stack' do - let(:out) { StringIO.new } - let(:api_map) { described_class.load_with_cache(Dir.pwd, out) } - context 'with stdlib that has vital dependencies' do let(:external_requires) { ['yaml'] } let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } From 74c79704f836486259bd5397cbf3279967429f13 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 18:49:39 -0500 Subject: [PATCH 067/206] Add before :context --- .rubocop_todo.yml | 15 ++++++--------- spec/library_spec.rb | 5 +++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 38fb2334f..8ad02cde5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -176,15 +176,7 @@ Naming/PredicatePrefix: - 'lib/solargraph/workspace.rb' RSpec/BeforeAfterAll: - Exclude: - - '**/spec/spec_helper.rb' - - '**/spec/rails_helper.rb' - - '**/spec/support/**/*.rb' - - 'spec/api_map_spec.rb' - - 'spec/language_server/host/dispatch_spec.rb' - - 'spec/language_server/protocol_spec.rb' - - 'spec/rbs_map/conversions_spec.rb' - - 'spec/yard_map/mapper_spec.rb' + Enabled: false # Configuration parameters: IgnoredMetadata. RSpec/DescribeClass: @@ -196,6 +188,11 @@ RSpec/DescribeClass: - '**/spec/views/**/*' - 'spec/complex_type_spec.rb' +# This cop supports safe autocorrection (--autocorrect). +RSpec/EmptyHook: + Exclude: + - 'spec/library_spec.rb' + # This cop supports safe autocorrection (--autocorrect). RSpec/ExpectActual: Exclude: diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 9f9ab87dc..b49c5ac8d 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -4,6 +4,11 @@ require 'yard' describe Solargraph::Library do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'does not open created files in the workspace' do Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path From c6702c92394a91b29444e16857b701afa510d874 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 18:58:34 -0500 Subject: [PATCH 068/206] More after :context --- spec/language_server/protocol_spec.rb | 2 +- spec/library_spec.rb | 40 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 4e150c0c0..4b55fbf3c 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -39,7 +39,7 @@ def stop @protocol = described_class.new(Solargraph::LanguageServer::Host.new) end - after :all do + after :context do @protocol.stop end diff --git a/spec/library_spec.rb b/spec/library_spec.rb index b49c5ac8d..b8f152eaa 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -34,6 +34,11 @@ end context 'with a require from a not-yet-cached external gem' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + before do Solargraph::Shell.new.uncache('backport') end @@ -59,6 +64,11 @@ def foo(adapter) end context 'with a require from an already-cached external gem' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + before do Solargraph::Shell.new.gems('backport') end @@ -194,6 +204,11 @@ def bar end describe '#references_from' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'collects references to a new method on a constant from assignment of Class.new' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) @@ -599,6 +614,11 @@ def bar; end end describe '#locate_ref' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'returns nil without a matching reference location' do workspace = File.absolute_path(File.join('spec', 'fixtures', 'workspace')) library = described_class.load(workspace) @@ -610,6 +630,11 @@ def bar; end end describe '#delete' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'removes files from Library#source_map_hash' do workspace = File.absolute_path(File.join('spec', 'fixtures', 'workspace')) library = described_class.load(workspace) @@ -630,11 +655,21 @@ def bar; end end context 'when unsynchronized' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + let(:library) { described_class.load File.absolute_path(File.join('spec', 'fixtures', 'workspace')) } let(:good_file) { File.join(library.workspace.directory, 'lib', 'thing.rb') } let(:bad_file) { File.join(library.workspace.directory, 'lib', 'not_a_thing.rb') } describe 'Library#completions_at' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'gracefully handles unmapped sources' do expect do library.completions_at(good_file, 0, 0) @@ -649,6 +684,11 @@ def bar; end end describe 'Library#definitions_at' do + before :context do + # run these in order so we don't uncache backport right when we + # need it before + end + it 'gracefully handles unmapped sources' do expect do library.definitions_at(good_file, 0, 0) From 6bc40d653d62fb5e05d4a478f865b2966a3db9ab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:08:56 -0500 Subject: [PATCH 069/206] Add more before :contexts --- .rubocop_todo.yml | 1 + spec/gem_pins_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8ad02cde5..fede54aed 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -191,6 +191,7 @@ RSpec/DescribeClass: # This cop supports safe autocorrection (--autocorrect). RSpec/EmptyHook: Exclude: + - 'spec/gem_pins_spec.rb' - 'spec/library_spec.rb' # This cop supports safe autocorrection (--autocorrect). diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 3a9745d70..4b21dbabb 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -10,6 +10,11 @@ end context 'with a combined method pin' do + before :context do + # run these on same runner so we don't cache rbs in parallel; + # seems like we still have a race condition in pin caching + end + let(:path) { 'RBS::EnvironmentLoader#core_root' } let(:requires) { ['rbs'] } @@ -28,6 +33,11 @@ end context 'with a YARD-only pin' do + before :context do + # run these on same runner so we don't cache rake in parallel; + # seems like we still have a race condition in pin caching + end + let(:requires) { ['rake'] } let(:path) { 'Rake::Task#prerequisites' } From 96daa5d39b5f910c8ecaabc9808ab35b1d6da457 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:15:32 -0500 Subject: [PATCH 070/206] Fix merge issue --- spec/shell_spec.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index e37b3d9b6..f69e53007 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -175,17 +175,6 @@ def bundle_exec(*cmd) end end - # @type cmd [Array] - # @return [String] - def bundle_exec(*cmd) - # run the command in the temporary directory with bundle exec - Bundler.with_unbundled_env do - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}") - expect(status.success?).to be(true), "Command failed: #{output}" - output - end - end - describe 'pin on a class' do let(:api_map) { instance_double(Solargraph::ApiMap) } let(:string_pin) { instance_double(Solargraph::Pin::Namespace, name: 'String') } From e8a33a5eb3eb7f08ecb10bee0e7f77644537a696 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:22:48 -0500 Subject: [PATCH 071/206] Fix merge --- spec/doc_map_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 8ff1e70b1..07ccf78c0 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -116,7 +116,9 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: nil) - doc_map_with_bundler_require.cache_doc_map_gems!(nil) + if doc_map_with_bundler_require.pins.length <= plain_doc_map.pins.length + doc_map_with_bundler_require.cache_doc_map_gems!(nil) + end expect(doc_map_with_bundler_require.pins.length - plain_doc_map.pins.length).to be_positive end end From 6ba49d0b36e4617d31368a0f6f8b47764a5480f0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:24:49 -0500 Subject: [PATCH 072/206] Fix merge --- spec/api_map_spec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/api_map_spec.rb b/spec/api_map_spec.rb index facf9489c..28f700af2 100755 --- a/spec/api_map_spec.rb +++ b/spec/api_map_spec.rb @@ -3,7 +3,10 @@ require 'tmpdir' describe Solargraph::ApiMap do - before :all do + # before :context here disables parallel tests in prspec, which + # would be needed regardless as we are changing the working + # directory + before :context do @api_map = described_class.new end From 4568e8df9a3a25a49f9bb5dc336620b92b8b7ae4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:31:53 -0500 Subject: [PATCH 073/206] Log gem caching to $stderr to help see where test time is going --- spec/doc_map_spec.rb | 2 +- spec/pin_cache_spec.rb | 14 +++++++------- spec/workspace_spec.rb | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 07ccf78c0..ec6f16c99 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -115,7 +115,7 @@ context 'with require as bundle/require' do it 'imports all gems when bundler/require used' do - doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: nil) + doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: $stderr) if doc_map_with_bundler_require.pins.length <= plain_doc_map.pins.length doc_map_with_bundler_require.cache_doc_map_gems!(nil) end diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 83cf7a3b7..fe26efbb4 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -66,13 +66,13 @@ let(:backport_gemspec) { Gem::Specification.find_by_name('backport') } before do - pin_cache.cache_gem(gemspec: backport_gemspec, out: nil) + pin_cache.cache_gem(gemspec: backport_gemspec, out: $stderr) end it 'does not load the gem again' do allow(Marshal).to receive(:load).and_call_original - pin_cache.cache_gem(gemspec: backport_gemspec, out: nil) + pin_cache.cache_gem(gemspec: backport_gemspec, out: $stderr) expect(Marshal).not_to have_received(:load).with(anything) end @@ -86,7 +86,7 @@ it 'chooses not to use YARD' do parser_gemspec = Gem::Specification.find_by_name('parser') - pin_cache.cache_gem(gemspec: parser_gemspec, out: nil) + pin_cache.cache_gem(gemspec: parser_gemspec, out: $stderr) # if this fails, you may not have run `bundle exec rbs collection update` expect(Solargraph::Yardoc).not_to have_received(:build_docs).with(any_args) end @@ -100,7 +100,7 @@ it 'uncaches when asked' do gemspec = Gem::Specification.find_by_name('kramdown') expect do - pin_cache.uncache_gem(gemspec, out: nil) + pin_cache.uncache_gem(gemspec, out: $stderr) end.not_to raise_error end end @@ -112,7 +112,7 @@ it 'chooses not to use YARD' do parser_gemspec = Gem::Specification.find_by_name('parser') - pin_cache.cache_gem(gemspec: parser_gemspec, rebuild: true, out: nil) + pin_cache.cache_gem(gemspec: parser_gemspec, rebuild: true, out: $stderr) # if this fails, you may not have run `bundle exec rbs collection update` expect(Solargraph::Yardoc).not_to have_received(:build_docs).with(any_args) end @@ -129,7 +129,7 @@ yaml_gemspec = Gem::Specification.find_by_name(gem_name) allow(File).to receive(:write).and_call_original - pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) + pin_cache.cache_gem(gemspec: yaml_gemspec, out: $stderr) # match arguments with regexp using rspec-matchers syntax expect(File).to have_received(:write).with(%r{combined/.*/logger-.*-stdlib.ser$}, any_args).once @@ -147,7 +147,7 @@ yaml_gemspec = Gem::Specification.find_by_name(gem_name) allow(File).to receive(:write).and_call_original - pin_cache.cache_gem(gemspec: yaml_gemspec, out: nil) + pin_cache.cache_gem(gemspec: yaml_gemspec, out: $stderr) # match arguments with regexp using rspec-matchers syntax expect(File).to have_received(:write).with(%r{combined/.*/rubocop-yard-.*-export.ser$}, any_args, diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index 0924cd707..cca1f3fe4 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -164,7 +164,7 @@ workspace.cache_all_for_workspace!(nil, rebuild: false) - expect(Solargraph::PinCache).to have_received(:cache_core).with(out: nil) + expect(Solargraph::PinCache).to have_received(:cache_core).with(out: $stderr) end end end From 00c19e3ddb35e0720b123ec4d1d68891d1191426 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 19:54:24 -0500 Subject: [PATCH 074/206] Add another before :context --- .rubocop_todo.yml | 1 + spec/logging_spec.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fede54aed..f8fbe4c88 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -193,6 +193,7 @@ RSpec/EmptyHook: Exclude: - 'spec/gem_pins_spec.rb' - 'spec/library_spec.rb' + - 'spec/logging_spec.rb' # This cop supports safe autocorrection (--autocorrect). RSpec/ExpectActual: diff --git a/spec/logging_spec.rb b/spec/logging_spec.rb index 7dcd52000..584b07c76 100644 --- a/spec/logging_spec.rb +++ b/spec/logging_spec.rb @@ -3,6 +3,11 @@ require 'tempfile' describe Solargraph::Logging do + before :context do + # We're messing with global state, so let's try to keep it to main + # runner + end + it 'logs messages with levels' do file = Tempfile.new('log') described_class.logger.reopen file From cbc9d09bf27ec8d51053710ffcd4c28a39b6399d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 20:14:24 -0500 Subject: [PATCH 075/206] Fix expectation --- spec/workspace_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace_spec.rb b/spec/workspace_spec.rb index cca1f3fe4..0924cd707 100644 --- a/spec/workspace_spec.rb +++ b/spec/workspace_spec.rb @@ -164,7 +164,7 @@ workspace.cache_all_for_workspace!(nil, rebuild: false) - expect(Solargraph::PinCache).to have_received(:cache_core).with(out: $stderr) + expect(Solargraph::PinCache).to have_received(:cache_core).with(out: nil) end end end From b5ae43ff9b7d9b7c6d0a77afc1d10b2d71c3537f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 20:31:27 -0500 Subject: [PATCH 076/206] Stop bundle installing with every example in shell_spec --- spec/shell_spec.rb | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index f69e53007..7f6c13421 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -6,33 +6,32 @@ describe Solargraph::Shell do let(:shell) { described_class.new } - let(:temp_dir) { Dir.mktmpdir } + describe '--version' do + let(:temp_dir) { Dir.mktmpdir } + let(:output) { bundle_exec('solargraph', '--version') } - before do - File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| - file.puts "source 'https://rubygems.org'" - file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" + before do + File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| + file.puts "source 'https://rubygems.org'" + file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" + end + output, status = Open3.capture2e('bundle install', chdir: temp_dir) + raise "Failure installing bundle: #{output}" unless status.success? end - output, status = Open3.capture2e('bundle install', chdir: temp_dir) - raise "Failure installing bundle: #{output}" unless status.success? - end - - # @type cmd [Array] - # @return [String] - def bundle_exec(*cmd) - # run the command in the temporary directory with bundle exec - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) - expect(status.success?).to be(true), "Command failed: #{output}" - output - end - after do - # remove the temporary directory after the tests - FileUtils.rm_rf(temp_dir) - end + # @type cmd [Array] + # @return [String] + def bundle_exec(*cmd) + # run the command in the temporary directory with bundle exec + output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) + expect(status.success?).to be(true), "Command failed: #{output}" + output + end - describe '--version' do - let(:output) { bundle_exec('solargraph', '--version') } + after do + # remove the temporary directory after the tests + FileUtils.rm_rf(temp_dir) + end it 'returns output' do expect(output).not_to be_empty From 94f1229ac015acca82667823d7d3fd28605bddc4 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 20:52:51 -0500 Subject: [PATCH 077/206] Move function --- .../gemspecs_resolve_require_spec.rb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 8deba9ff8..ad207c7e2 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -15,22 +15,6 @@ def find_or_install gem_name, version install_gem(gem_name, version) end - def add_bundle - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem 'backport' - GEMFILE - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) - end - raise "Failure installing bundle: #{output}" unless status.success? - # ensure Gemfile.lock exists - return if File.exist?(File.join(dir_path, 'Gemfile.lock')) - raise "Gemfile.lock not found after bundle install in #{dir_path}" - end - def install_gem gem_name, version Bundler.with_unbundled_env do cmd = Gem::Commands::InstallCommand.new @@ -185,6 +169,22 @@ def configure_bundler_spec stub_value context 'with external bundle' do let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } + def add_bundle + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? + # ensure Gemfile.lock exists + return if File.exist?(File.join(dir_path, 'Gemfile.lock')) + raise "Gemfile.lock not found after bundle install in #{dir_path}" + end + context 'with no actual bundle' do let(:require) { 'bundler/require' } From 9138a73b244aecb95caa7121fcb774c1eb3363b5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 21:06:42 -0500 Subject: [PATCH 078/206] Debug --- .../gemspecs_resolve_require_spec.rb | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index ad207c7e2..fc09a091d 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'benchmark' require 'fileutils' require 'tmpdir' require 'rubygems/commands/install_command' @@ -9,22 +10,6 @@ let(:gemspecs) { described_class.new(dir_path) } - def find_or_install gem_name, version - Gem::Specification.find_by_name(gem_name, version) - rescue Gem::LoadError - install_gem(gem_name, version) - end - - def install_gem gem_name, version - Bundler.with_unbundled_env do - cmd = Gem::Commands::InstallCommand.new - cmd.handle_options [gem_name, '-v', version] - cmd.execute - rescue Gem::SystemExitException => e - raise unless e.exit_code == 0 - end - end - context 'with local bundle' do let(:dir_path) { File.realpath(Dir.pwd) } @@ -170,16 +155,20 @@ def configure_bundler_spec stub_value let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } def add_bundle - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem 'backport' - GEMFILE - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) + time = Benchmark.measure do + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? end - raise "Failure installing bundle: #{output}" unless status.success? + STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds") # ensure Gemfile.lock exists return if File.exist?(File.join(dir_path, 'Gemfile.lock')) raise "Gemfile.lock not found after bundle install in #{dir_path}" @@ -240,6 +229,22 @@ def add_bundle context 'with a Gemfile and a gem preference' do # find_or_install helper doesn't seem to work on older versions if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') + def find_or_install gem_name, version + Gem::Specification.find_by_name(gem_name, version) + rescue Gem::LoadError + install_gem(gem_name, version) + end + + def install_gem gem_name, version + Bundler.with_unbundled_env do + cmd = Gem::Commands::InstallCommand.new + cmd.handle_options [gem_name, '-v', version] + cmd.execute + rescue Gem::SystemExitException => e + raise unless e.exit_code == 0 + end + end + before do add_bundle find_or_install('backport', '1.0.0') From 75fd25287d2df1a82b85afa1f011396aba391acd Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 21:42:52 -0500 Subject: [PATCH 079/206] Simplify version spec --- spec/shell_spec.rb | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 7f6c13421..2afeb3f22 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -7,37 +7,11 @@ let(:shell) { described_class.new } describe '--version' do - let(:temp_dir) { Dir.mktmpdir } - let(:output) { bundle_exec('solargraph', '--version') } - - before do - File.open(File.join(temp_dir, 'Gemfile'), 'w') do |file| - file.puts "source 'https://rubygems.org'" - file.puts "gem 'solargraph', path: '#{File.expand_path('..', __dir__)}'" + it 'returns a version when run' do + output = capture_stdout do + shell.version end - output, status = Open3.capture2e('bundle install', chdir: temp_dir) - raise "Failure installing bundle: #{output}" unless status.success? - end - - # @type cmd [Array] - # @return [String] - def bundle_exec(*cmd) - # run the command in the temporary directory with bundle exec - output, status = Open3.capture2e("bundle exec #{cmd.join(' ')}", chdir: temp_dir) - expect(status.success?).to be(true), "Command failed: #{output}" - output - end - - after do - # remove the temporary directory after the tests - FileUtils.rm_rf(temp_dir) - end - it 'returns output' do - expect(output).not_to be_empty - end - - it 'returns a version when run' do expect(output).to eq("#{Solargraph::VERSION}\n") end end From 056c3ca9117b4d1bf12bff4f0a570061104b81e2 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 21:51:32 -0500 Subject: [PATCH 080/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index fc09a091d..379f94e98 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -168,7 +168,7 @@ def add_bundle end raise "Failure installing bundle: #{output}" unless status.success? end - STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds") + STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") # ensure Gemfile.lock exists return if File.exist?(File.join(dir_path, 'Gemfile.lock')) raise "Gemfile.lock not found after bundle install in #{dir_path}" From d866dc069561141df1f3212515f0f436ee8c6a75 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 21:59:29 -0500 Subject: [PATCH 081/206] Debug --- .../gemspecs_fetch_dependencies_spec.rb | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 56504e7dd..9c5d67bb5 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'benchmark' require 'fileutils' require 'tmpdir' require 'rubygems/commands/install_command' @@ -56,17 +57,20 @@ end before do - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem '#{gem_name}' - GEMFILE - - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) + time = Benchmark.measure do + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem '#{gem_name}' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose', chdir: dir_path) + end + raise "Failure installing bundle: #{output}" unless status.success? end - raise "Failure installing bundle: #{output}" unless status.success? + STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") # ensure Gemfile.lock exists unless File.exist?(File.join(dir_path, 'Gemfile.lock')) From 203a4f83b368aeae118bab1745385e5b7b2fcc02 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 31 Jan 2026 22:06:55 -0500 Subject: [PATCH 082/206] Debug --- spec/workspace/require_paths_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index eb95d0c5b..aa027c6c1 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -2,9 +2,17 @@ require 'fileutils' require 'tmpdir' +require 'benchmark' describe Solargraph::Workspace::RequirePaths do - subject(:paths) { described_class.new(dir_path, config).generate } + subject(:paths) do + out = nil + time = Benchmark.measure do + out = described_class.new(dir_path, config).generate + end + STDERR.puts("Generated require paths in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") + out + end let(:config) { Solargraph::Workspace::Config.new(dir_path) } From 854e9163555cc58bfdaf0f8d5688916c66bedde1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 10:48:55 -0500 Subject: [PATCH 083/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 379f94e98..438e2e4e6 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -168,7 +168,7 @@ def add_bundle end raise "Failure installing bundle: #{output}" unless status.success? end - STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") + STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") # ensure Gemfile.lock exists return if File.exist?(File.join(dir_path, 'Gemfile.lock')) raise "Gemfile.lock not found after bundle install in #{dir_path}" From 4ae10a58e18e3f23c224290b909db29a265892ec Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 10:58:23 -0500 Subject: [PATCH 084/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 438e2e4e6..401d9c15f 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -155,6 +155,7 @@ def configure_bundler_spec stub_value let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } def add_bundle + output = nil time = Benchmark.measure do # write out Gemfile File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) From 3014abe8a117ab3f5dd9e4312247f08b7e7ab950 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:09:35 -0500 Subject: [PATCH 085/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 401d9c15f..58e314ec9 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -165,7 +165,7 @@ def add_bundle # run bundle install output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) + Open3.capture2e('bundle install --verbose --local', chdir: dir_path) end raise "Failure installing bundle: #{output}" unless status.success? end From c0c340e24f41d35e84795f807777f2bc2cafed83 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:16:15 -0500 Subject: [PATCH 086/206] Speed up bundle install --- .github/workflows/rspec.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 8c033f59c..676d2f6ba 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -86,6 +86,10 @@ jobs: run: bundle exec rbs collection update - name: Run tests run: | + # use this for some of the bundle installs we run inside the + # tests as well when we're testing different solargraph usage + # scenarios + bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL echo "Running tests with PARALLEL=$PARALLEL" From c210441f3b1a3444bf9509da6f0af75e0e77f852 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:17:50 -0500 Subject: [PATCH 087/206] Debug --- .github/workflows/rspec.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 676d2f6ba..4822f4a4e 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -86,9 +86,13 @@ jobs: run: bundle exec rbs collection update - name: Run tests run: | - # use this for some of the bundle installs we run inside the - # tests as well when we're testing different solargraph usage - # scenarios + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL From 83d8da9eeaeeb9b40aad211abe7ba48450133a0a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:31:30 -0500 Subject: [PATCH 088/206] Debug --- .github/workflows/plugins.yml | 40 +++++++++++++++++++++++++++++++++++ .github/workflows/rspec.yml | 8 +++++++ 2 files changed, 48 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a6cbfedfd..7029a39c7 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -73,6 +73,14 @@ jobs: run: bundle exec rbs collection update - name: Ensure specs still run run: | + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL bundle exec rake spec @@ -134,6 +142,14 @@ jobs: run: bundle exec rbs collection update - name: Ensure specs still run run: | + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL bundle exec rake spec @@ -191,6 +207,14 @@ jobs: run: bundle exec rbs collection update - name: Ensure specs still run run: | + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL bundle exec rake spec @@ -253,6 +277,14 @@ jobs: bundle exec appraisal solargraph gems $rspec_gems - name: Run specs run: | + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle cd ../solargraph-rspec PARALLEL=$(nproc --all) export PARALLEL @@ -305,6 +337,14 @@ jobs: BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" export BUNDLE_PATH cd ../solargraph-rails + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle bundle exec solargraph --version bundle info solargraph bundle info rbs diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 4822f4a4e..ada914382 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -121,6 +121,14 @@ jobs: run: bundle exec rake spec - name: Check PR coverage run: | + # Speed up some of the bundle installs we run inside the tests + # as well when we're testing different solargraph usage + # scenarios. This is already set in the local bundle config by + # the setup-ruby action. + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL bundle exec rake undercover From 819bf4398671172282bbb148e90c44ae99e39da1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:34:10 -0500 Subject: [PATCH 089/206] Debug --- .github/workflows/plugins.yml | 43 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 7029a39c7..b0f1142c3 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -313,38 +313,35 @@ jobs: MATRIX_RAILS_VERSION: "7.0" - name: Install gems run: | - set -x - BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" - export BUNDLE_PATH - cd ../solargraph-rails - echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile - bundle install - bundle update --pre rbs - RAILS_DIR="$(pwd)/spec/rails7" - export RAILS_DIR - cd ${RAILS_DIR} - bundle install - bundle exec --gemfile ../../Gemfile rbs --version - bundle exec --gemfile ../../Gemfile rbs collection install - cd ../../ - # bundle exec rbs collection init - # bundle exec rbs collection install + set -x + # Share caches to speed up bundle install + # + # See + # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically + cd ../solargraph-rails + echo "gem 'solargraph', path: '${GITHUB_WORKSPACE:?}'" >> Gemfile + bundle install + bundle update --pre rbs + RAILS_DIR="$(pwd)/spec/rails7" + export RAILS_DIR + cd ${RAILS_DIR} + bundle install + bundle exec --gemfile ../../Gemfile rbs --version + bundle exec --gemfile ../../Gemfile rbs collection install + cd ../../ + # bundle exec rbs collection init + # bundle exec rbs collection install env: MATRIX_RAILS_VERSION: "7.0" MATRIX_RAILS_MAJOR_VERSION: '7' - name: Run specs run: | - BUNDLE_PATH="${GITHUB_WORKSPACE:?}/vendor/bundle" - export BUNDLE_PATH - cd ../solargraph-rails - # Speed up some of the bundle installs we run inside the tests - # as well when we're testing different solargraph usage - # scenarios. This is already set in the local bundle config by - # the setup-ruby action. + # Share caches to speed up bundle install # # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle + cd ../solargraph-rails bundle exec solargraph --version bundle info solargraph bundle info rbs From 8a220c4ddf4db47e16be5c94dc60a0c64bfe6b31 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:38:31 -0500 Subject: [PATCH 090/206] Debug Gemfile.lock --- .github/workflows/rspec.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index ada914382..aae2fda07 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -97,6 +97,7 @@ jobs: PARALLEL=$(nproc --all) export PARALLEL echo "Running tests with PARALLEL=$PARALLEL" + cat Gemfile.lock bundle exec rake spec undercover: runs-on: ubuntu-latest From 547276b7da4bc20eeda51551719b60d41b79ba16 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 11:49:23 -0500 Subject: [PATCH 091/206] Debug slowness --- spec/workspace/gemspecs_resolve_require_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 58e314ec9..b1cba0a0b 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -165,7 +165,7 @@ def add_bundle # run bundle install output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose --local', chdir: dir_path) + Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) end raise "Failure installing bundle: #{output}" unless status.success? end From 27a171c120575d71a08207a5183ac63afecd1365 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 12:01:53 -0500 Subject: [PATCH 092/206] Bump time limit --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9f320e688..3b7bd5aea 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -32,7 +32,7 @@ end RspecTimeGuard.setup RspecTimeGuard.configure do |config| - config.global_time_limit_seconds = 120 + config.global_time_limit_seconds = 300 config.continue_on_timeout = false end require 'solargraph' From 3dd98b03c8cf04ea18104b95b027440398dcab17 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 12:49:42 -0500 Subject: [PATCH 093/206] Debug --- spec/workspace/gemspecs_fetch_dependencies_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 9c5d67bb5..06b57a952 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -57,6 +57,7 @@ end before do + output = nil time = Benchmark.measure do # write out Gemfile File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) @@ -66,11 +67,11 @@ # run bundle install output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose', chdir: dir_path) + Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) end raise "Failure installing bundle: #{output}" unless status.success? end - STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") + STDERR.puts("Added bundle in #{dir_path} gin #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") # ensure Gemfile.lock exists unless File.exist?(File.join(dir_path, 'Gemfile.lock')) From be64b85c9183b3be2c05ca50897a12f1131690d0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:00:05 -0500 Subject: [PATCH 094/206] Debug --- spec/workspace/gemspecs_fetch_dependencies_spec.rb | 6 +++--- spec/workspace/gemspecs_resolve_require_spec.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 06b57a952..556105b9a 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -71,7 +71,7 @@ end raise "Failure installing bundle: #{output}" unless status.success? end - STDERR.puts("Added bundle in #{dir_path} gin #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") + STDERR.puts("Added #{gem_name} bundle in #{dir_path} gin #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") # ensure Gemfile.lock exists unless File.exist?(File.join(dir_path, 'Gemfile.lock')) @@ -88,12 +88,12 @@ end context 'with gem does not exist in our bundle' do - let(:gem_name) { 'activerecord' } + let(:gem_name) { 'activemodel' } it 'gives a useful message' do dep_names = nil output = capture_both { dep_names = deps.map(&:name) } - expect(output).to include('Please install the gem activerecord') + expect(output).to include("Please install the gem #{gem_name}") end end end diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index b1cba0a0b..17aa9edca 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -169,7 +169,7 @@ def add_bundle end raise "Failure installing bundle: #{output}" unless status.success? end - STDERR.puts("Added bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") + STDERR.puts("Added backport bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") # ensure Gemfile.lock exists return if File.exist?(File.join(dir_path, 'Gemfile.lock')) raise "Gemfile.lock not found after bundle install in #{dir_path}" From 83ce3d39f671deb62b6aa425b81120700337cd00 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:15:14 -0500 Subject: [PATCH 095/206] Debug --- spec/workspace/gemspecs_fetch_dependencies_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index 556105b9a..d08613b98 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -80,15 +80,15 @@ end context 'with gem that exists in our bundle' do - let(:gem_name) { 'undercover' } + let(:gem_name) { 'simplecov' } it 'finds dependencies' do - expect(deps.map(&:name)).to include('ast') + expect(deps.map(&:name)).to include('simplecov-html') end end context 'with gem does not exist in our bundle' do - let(:gem_name) { 'activemodel' } + let(:gem_name) { 'functional-ruby' } it 'gives a useful message' do dep_names = nil From 6c80bae6ce4e66048bc2c06e7e2675a46c37d663 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:24:25 -0500 Subject: [PATCH 096/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 17aa9edca..a610a9c02 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -184,7 +184,13 @@ def add_bundle end context 'with Gemfile and Bundler.require' do - before { add_bundle } + before :context do + # this tells parallel rspec to run this serially in the same + # worker, so we don't end up doing the bundle installs in + # parallel + + add_bundle + end let(:require) { 'bundler/require' } From 07db6a3d25ae058779204f2be16eac1c4e6ae059 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:28:51 -0500 Subject: [PATCH 097/206] Debug --- .../gemspecs_resolve_require_spec.rb | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index a610a9c02..a247b0e32 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -204,7 +204,13 @@ def add_bundle end context 'with Gemfile and deep require into a possibly-core gem' do - before { add_bundle } + before :context do + # this tells parallel rspec to run this serially in the same + # worker, so we don't end up doing the bundle installs in + # parallel + + add_bundle + end let(:require) { 'bundler/gem_tasks' } @@ -214,7 +220,13 @@ def add_bundle end context 'with Gemfile and deep require into a gem' do - before { add_bundle } + before :context do + # this tells parallel rspec to run this serially in the same + # worker, so we don't end up doing the bundle installs in + # parallel + + add_bundle + end let(:require) { 'rspec/mocks' } @@ -224,7 +236,13 @@ def add_bundle end context 'with Gemfile but an unknown gem' do - before { add_bundle } + before :context do + # this tells parallel rspec to run this serially in the same + # worker, so we don't end up doing the bundle installs in + # parallel + + add_bundle + end let(:require) { 'unknown_gemlaksdflkdf' } From b6ee0228c312f1c48517f15e90a4d380365df335 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:32:59 -0500 Subject: [PATCH 098/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index a247b0e32..f00538325 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -252,6 +252,12 @@ def add_bundle end context 'with a Gemfile and a gem preference' do + before :context do + # this tells parallel rspec to run this serially in the same + # worker, so we don't end up doing the bundle installs in + # parallel + end + # find_or_install helper doesn't seem to work on older versions if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0') def find_or_install gem_name, version From ea94901fddef3eb116c2d273f30e5f895167a0d1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 13:39:42 -0500 Subject: [PATCH 099/206] Debug --- spec/workspace/gemspecs_resolve_require_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index f00538325..6f1e08261 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -188,7 +188,9 @@ def add_bundle # this tells parallel rspec to run this serially in the same # worker, so we don't end up doing the bundle installs in # parallel + end + before do add_bundle end @@ -208,7 +210,9 @@ def add_bundle # this tells parallel rspec to run this serially in the same # worker, so we don't end up doing the bundle installs in # parallel + end + before do add_bundle end @@ -224,7 +228,9 @@ def add_bundle # this tells parallel rspec to run this serially in the same # worker, so we don't end up doing the bundle installs in # parallel + end + before do add_bundle end @@ -240,7 +246,9 @@ def add_bundle # this tells parallel rspec to run this serially in the same # worker, so we don't end up doing the bundle installs in # parallel + end + before do add_bundle end From a6a46781395995eac316859a5aaca879372407e3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 14:18:14 -0500 Subject: [PATCH 100/206] Debug --- .github/workflows/rspec.yml | 3 ++- lib/solargraph/pin_cache.rb | 31 ++++++++++++++++------------ lib/solargraph/rbs_map/stdlib_map.rb | 5 +++++ lib/solargraph/shell.rb | 5 +++++ lib/solargraph/workspace.rb | 6 +++++- spec/pin/method_spec.rb | 27 +++++++++++++++--------- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index aae2fda07..eb70212aa 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -97,7 +97,8 @@ jobs: PARALLEL=$(nproc --all) export PARALLEL echo "Running tests with PARALLEL=$PARALLEL" - cat Gemfile.lock + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec rake spec undercover: runs-on: ubuntu-latest diff --git a/lib/solargraph/pin_cache.rb b/lib/solargraph/pin_cache.rb index 886d55838..0e1d69088 100644 --- a/lib/solargraph/pin_cache.rb +++ b/lib/solargraph/pin_cache.rb @@ -134,19 +134,24 @@ def yardoc_processing? gemspec # @return [Array] a list of possible standard library names def possible_stdlibs - # all dirs and .rb files in Gem::RUBYGEMS_DIR - Dir.glob(File.join(Gem::RUBYGEMS_DIR, '*')).map do |file_or_dir| - basename = File.basename(file_or_dir) - # remove .rb - # @sg-ignore flow sensitive typing should be able to handle redefinition - basename = basename[0..-4] if basename.end_with?('.rb') - basename - end.sort.uniq - rescue StandardError => e - logger.info { "Failed to get possible stdlibs: #{e.message}" } - # @sg-ignore Need to add nil check here - logger.debug { e.backtrace.join("\n") } - [] + # all dirs and .rb files in Gem::RUBYGEMS_DIR/rubygems + local_stdlibs = + begin + Dir.glob(File.join(Gem::RUBYGEMS_DIR, 'rubygems', '*')).map do |file_or_dir| + basename = File.basename(file_or_dir) + # remove .rb + # @sg-ignore flow sensitive typing should be able to handle redefinition + basename = basename[0..-4] if basename.end_with?('.rb') + basename + end.sort.uniq + rescue StandardError => e + logger.info { "Failed to get possible stdlibs: #{e.message}" } + # @sg-ignore Need to add nil check here + logger.debug { e.backtrace.join("\n") } + [] + end + rbs_stdlibs = RbsMap::StdlibMap.possible_stdlibs + (local_stdlibs + rbs_stdlibs).sort.uniq end private diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index e6ebcf90f..825587694 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -33,6 +33,7 @@ def initialize library, rebuild: false, out: $stderr generated_pins = pins logger.debug { "Found #{generated_pins.length} pins for stdlib library #{library}" } PinCache.serialize_stdlib_require library, generated_pins + out&.puts "Cached stdlib RBS for #{library}" end end @@ -66,6 +67,10 @@ def resolve_dependencies? def self.load library @stdlib_maps_hash[library] ||= StdlibMap.new(library) end + + def self.possible_stdlibs + RBS::Repository.default.gems.keys + end end end end diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 8ee13eacf..bac676699 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -181,6 +181,11 @@ def gems *names next end + if name == 'stdlib' + workspace.cache_all_stdlibs(out: $stdout, rebuild: options[:rebuild]) + next + end + gemspec = workspace.find_gem(*name.split('=')) if gemspec.nil? warn "Gem '#{name}' not found" diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 351ee28a5..241736f43 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -258,11 +258,15 @@ def cache_all_for_workspace! out, rebuild: false # do this after so that we prefer stdlib requires from gems, # which are likely to be newer and have more pins - pin_cache.cache_all_stdlibs(out: out, rebuild: rebuild) + cache_all_stdlibs(out: out, rebuild: rebuild) out&.puts 'Documentation cached for core, standard library and gems.' end + def cache_all_stdlibs out: nil, rebuild: false + pin_cache.cache_all_stdlibs(out: out, rebuild: rebuild) + end + # Synchronize the workspace from the provided updater. # # @param updater [Source::Updater] diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index 0b8e780f8..32cdcf45c 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -545,16 +545,23 @@ class Foo expect(pin.return_type).to be_undefined end - it 'combines signatures by type' do - # Integer+ in RBS is a number of signatures that dispatch based - # on type. Let's make sure we combine those with anything else - # found (e.g., additions from the BigDecimal RBS collection) - # without collapsing signatures - api_map = Solargraph::ApiMap.load(Dir.pwd) - bench = Solargraph::Bench.new external_requires: ['bigdecimal'] - api_map.catalog(bench) - method = api_map.get_method_stack('Integer', '+', scope: :instance).first - expect(method.signatures.count).to be > 3 + context 'with loaded bigdecimal require' do + before :context do + # serialize this along with other things that might write to + # solargraph cache so we don't interfere with each other + end + + it 'combines signatures by type' do + # Integer+ in RBS is a number of signatures that dispatch based + # on type. Let's make sure we combine those with anything else + # found (e.g., additions from the BigDecimal RBS collection) + # without collapsing signatures + api_map = Solargraph::ApiMap.load(Dir.pwd) + bench = Solargraph::Bench.new external_requires: ['bigdecimal'] + api_map.catalog(bench) + method = api_map.get_method_stack('Integer', '+', scope: :instance).first + expect(method.signatures.count).to be > 3 + end end it 'infers untagged types from instance variables' do From a61c785749f26bdfab8f644d5c00054e4d40a28e Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 14:26:30 -0500 Subject: [PATCH 101/206] Fix --- spec/pin_cache_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index fe26efbb4..06a549cc5 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -47,7 +47,7 @@ it 'is tolerant of less usual Ruby installations' do stub_const('Gem::RUBYGEMS_DIR', nil) - expect(pin_cache.possible_stdlibs).to eq([]) + expect { pin_cache.possible_stdlibs }.not_to raise_error end end From 1be808f687fd3f8f3f5ab58982f8d88956abfcfb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 14:35:21 -0500 Subject: [PATCH 102/206] Debug --- lib/solargraph/workspace/require_paths.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index d12364b07..4ffcb5cdb 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -77,6 +77,7 @@ def require_path_from_gemspec_file gemspec_file_path 'return unless Gem::Specification === spec; ' \ 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] o, e, s = Open3.capture3(*cmd) + STDERR.puts("Evaluating gemspec in #{base}") if s.success? begin hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} From 215c23e4960c0c617377f4dd4fd9ffbb17f7c7cc Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 14:44:13 -0500 Subject: [PATCH 103/206] Use default config if none provided for require paths --- lib/solargraph/workspace/require_paths.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 4ffcb5cdb..7d28eaf36 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -16,7 +16,7 @@ class RequirePaths # @param config [Config, nil] def initialize directory, config @directory = directory - @config = config + @config = config || Config.new(directory) end # Generate require paths from gemspecs if they exist or assume the default From 3b4f3d2d04afa1ed9b7e6b1b05ac72ec92aa67a5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 14:51:29 -0500 Subject: [PATCH 104/206] Fix incorrect spec config --- spec/workspace/require_paths_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index aa027c6c1..f955436fc 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -28,7 +28,11 @@ context 'with config and no gemspec' do let(:dir_path) { File.realpath(Dir.pwd) } - let(:config) { instance_double(Solargraph::Workspace::Config, require_paths: [], allow?: true) } + let(:config) do + instance_double(Solargraph::Workspace::Config, + require_paths: [], + allow?: false) + end it 'includes the lib directory' do expect(paths).to include(File.join(dir_path, 'lib')) From 525c1b31274a77887c8974a2438f0515a16c5bbb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 15:01:01 -0500 Subject: [PATCH 105/206] Debug --- .github/workflows/plugins.yml | 10 ++++++++++ .github/workflows/rspec.yml | 2 ++ lib/solargraph/rbs_map/stdlib_map.rb | 1 + lib/solargraph/workspace.rb | 4 ++++ lib/solargraph/workspace/require_paths.rb | 1 - 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index b0f1142c3..74cc81ed1 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -83,6 +83,8 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec rake spec rails_typechecking: runs-on: ubuntu-latest @@ -152,6 +154,8 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec rake spec rspec_typechecking: runs-on: ubuntu-latest @@ -217,6 +221,8 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec rake spec run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem @@ -288,6 +294,8 @@ jobs: cd ../solargraph-rspec PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec appraisal rspec --format progress run_solargraph_rails_specs: @@ -348,6 +356,8 @@ jobs: bundle info yard PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib ALLOW_IMPROVEMENTS=true bundle exec rake spec env: MATRIX_RAILS_VERSION: "7.0" diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index eb70212aa..92f3df7e4 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -133,5 +133,7 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib bundle exec rake undercover continue-on-error: true diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 825587694..9c92fcefa 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -68,6 +68,7 @@ def self.load library @stdlib_maps_hash[library] ||= StdlibMap.new(library) end + # @return [Array] def self.possible_stdlibs RBS::Repository.default.gems.keys end diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 241736f43..0e4e29b13 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -263,6 +263,10 @@ def cache_all_for_workspace! out, rebuild: false out&.puts 'Documentation cached for core, standard library and gems.' end + # @param out [StringIO, IO, nil] output stream for logging + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached + # + # @return [void] def cache_all_stdlibs out: nil, rebuild: false pin_cache.cache_all_stdlibs(out: out, rebuild: rebuild) end diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 7d28eaf36..1b88ff054 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -77,7 +77,6 @@ def require_path_from_gemspec_file gemspec_file_path 'return unless Gem::Specification === spec; ' \ 'puts({name: spec.name, paths: spec.require_paths}.to_json)'] o, e, s = Open3.capture3(*cmd) - STDERR.puts("Evaluating gemspec in #{base}") if s.success? begin hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {} From e912db65d7c746641bd1c3c0df62f49d6b378297 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 15:29:45 -0500 Subject: [PATCH 106/206] Debug --- spec/doc_map_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index ec6f16c99..36f2dd063 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -19,7 +19,12 @@ let(:plain_doc_map) { described_class.new([], workspace, out: nil) } before do - doc_map.cache_doc_map_gems!(nil) if pre_cache + out = nil + time = Benchmark.measure do + out = doc_map.cache_doc_map_gems!(STDERR) if pre_cache + end + STDERR.puts("Cached doc map gems for #{requires} in #{time.real.round(2)} seconds in pid #{Process.pid}") + out end context 'with a require in solargraph test bundle' do From 56bb7c50ae07186aaff384275818575d7ae9a339 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 16:06:14 -0500 Subject: [PATCH 107/206] Debug --- .github/workflows/plugins.yml | 2 +- lib/solargraph/shell.rb | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 74cc81ed1..9581e36ce 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,7 +84,7 @@ jobs: PARALLEL=$(nproc --all) export PARALLEL # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib + bundle exec solargraph gems core stdlib default bundle exec rake spec rails_typechecking: runs-on: ubuntu-latest diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index bac676699..fdc925e22 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -158,6 +158,15 @@ def uncache *gems The 'core' argument can be used to cache the type documentation for the core Ruby libraries. + The literal 'stdlib' argument will cache all standard + libraries available. + + 'bundler/require' as a gem name will cache all auto-required + gems. + + 'default' will cache all gems used by Solargraph absent + specific requires in the files being looked at. + If the library is already cached, it will be rebuilt if the --rebuild option is set. @@ -186,6 +195,20 @@ def gems *names next end + if name == 'default' + doc_map = Solargraph::DocMap.new([], workspace) + doc_map.cache_doc_map_gems! $stdout + next + end + + if name == 'bundler/require' + gemspecs = workspace.resolve_require(name) + gemspecs&.each do |gs| + workspace.cache_gem(gs, rebuild: options[:rebuild], out: $stdout) + end + next + end + gemspec = workspace.find_gem(*name.split('=')) if gemspec.nil? warn "Gem '#{name}' not found" From 40fe7fd3355b3b00dc4830ecd88b6b0010b2a30c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 16:26:26 -0500 Subject: [PATCH 108/206] Debug --- .github/workflows/plugins.yml | 2 +- spec/parser/flow_sensitive_typing_spec.rb | 307 +++++++++++----------- 2 files changed, 157 insertions(+), 152 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 9581e36ce..93efc5b96 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,7 +84,7 @@ jobs: PARALLEL=$(nproc --all) export PARALLEL # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib default + time bundle exec solargraph gems core stdlib default bundle exec rake spec rails_typechecking: runs-on: ubuntu-latest diff --git a/spec/parser/flow_sensitive_typing_spec.rb b/spec/parser/flow_sensitive_typing_spec.rb index cee6afef1..f531c2c07 100644 --- a/spec/parser/flow_sensitive_typing_spec.rb +++ b/spec/parser/flow_sensitive_typing_spec.rb @@ -1,8 +1,13 @@ # frozen_string_literal: true +require 'tempfile' + # @todo These tests depend on `Clip`, but we're putting the tests here to # avoid overloading clip_spec.rb. describe Solargraph::Parser::FlowSensitiveTyping do + # random temporary filename ending in '.rb' using tmpfile + let(:filename) { Tempfile.new(['flow_sensitive_typing_spec', '.rb']).path } + it 'uses is_a? in a simple if() to refine types' do source = Solargraph::Source.load_string(%( class ReproBase; end @@ -15,12 +20,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.to_s).to eq('Repro') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -37,12 +42,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 10]) + clip = api_map.clip_at(filename, [7, 10]) expect(clip.infer.to_s).to eq('Repro1') - clip = api_map.clip_at('test.rb', [9, 10]) + clip = api_map.clip_at(filename, [9, 10]) expect(clip.infer.to_s).to eq('Repro2') end @@ -60,12 +65,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.to_s).to eq('Foo::Repro') - clip = api_map.clip_at('test.rb', [10, 10]) + clip = api_map.clip_at(filename, [10, 10]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -85,12 +90,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [10, 10]) + clip = api_map.clip_at(filename, [10, 10]) expect(clip.infer.to_s).to eq('Foo::Bar::Repro') - clip = api_map.clip_at('test.rb', [12, 10]) + clip = api_map.clip_at(filename, [12, 10]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -106,12 +111,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.to_s).to eq('ReproBase') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.to_s).to eq('Repro') end @@ -127,12 +132,12 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.to_s).to eq('Repro1') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -151,15 +156,15 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 10]) + clip = api_map.clip_at(filename, [7, 10]) expect(clip.infer.to_s).to eq('Repro1') - clip = api_map.clip_at('test.rb', [9, 10]) + clip = api_map.clip_at(filename, [9, 10]) expect(clip.infer.to_s).to eq('Repro2') - clip = api_map.clip_at('test.rb', [11, 10]) + clip = api_map.clip_at(filename, [11, 10]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -173,9 +178,9 @@ class Repro < ReproBase; end break unless value.is_a? Repro value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('Repro') end @@ -189,9 +194,9 @@ class Repro < ReproBase; end break unless value.is_a? Repro value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('Repro') end @@ -205,9 +210,9 @@ class Repro < ReproBase; end break unless value.is_a? Repro value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('Repro') end @@ -222,16 +227,16 @@ class Repro < ReproBase; end value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [3, 6]) + clip = api_map.clip_at(filename, [3, 6]) expect(clip.infer.to_s).to eq('Array') - clip = api_map.clip_at('test.rb', [5, 8]) + clip = api_map.clip_at(filename, [5, 8]) expect(clip.infer.to_s).to eq('Numeric') - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('Float') end @@ -247,16 +252,16 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('nil') end @@ -270,10 +275,10 @@ class Repro < ReproBase; end break unless value value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -287,10 +292,10 @@ class Repro < ReproBase; end break if value.nil? value end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 8]) + clip = api_map.clip_at(filename, [7, 8]) expect(clip.infer.to_s).to eq('ReproBase') end @@ -304,13 +309,13 @@ def baz; end bar bar = Foo.new bar - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 6]) + clip = api_map.clip_at(filename, [6, 6]) expect(clip.infer.to_s).to eq('Foo') - clip = api_map.clip_at('test.rb', [8, 6]) + clip = api_map.clip_at(filename, [8, 6]) expect(clip.infer.to_s).to eq('Foo') end @@ -319,9 +324,9 @@ def baz; end if is_a? Object x end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [2, 6]) + clip = api_map.clip_at(filename, [2, 6]) expect { clip.infer.to_s }.not_to raise_error end @@ -331,9 +336,9 @@ def baz; end if r.is_a? x end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [3, 6]) + clip = api_map.clip_at(filename, [3, 6]) expect { clip.infer.to_s }.not_to raise_error end @@ -349,15 +354,15 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') end @@ -373,15 +378,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') end @@ -397,15 +402,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') end @@ -421,15 +426,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') end @@ -445,15 +450,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') end @@ -469,15 +474,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('nil') end @@ -493,15 +498,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('nil') end @@ -513,15 +518,15 @@ def verify_repro(repr) repr unless repr.nil? || repr.downcase repr end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 33]) + clip = api_map.clip_at(filename, [4, 33]) expect(clip.infer.rooted_tags).to eq('::String') - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::String') - clip = api_map.clip_at('test.rb', [5, 8]) + clip = api_map.clip_at(filename, [5, 8]) expect(clip.infer.rooted_tags).to eq('::String, nil') end @@ -537,15 +542,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') end @@ -561,15 +566,15 @@ def verify_repro(repr, throw_the_dice) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') end @@ -585,15 +590,15 @@ def verify_repro(repr) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [4, 8]) + clip = api_map.clip_at(filename, [4, 8]) expect(clip.infer.rooted_tags).to eq('::Integer, nil') - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Integer') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.rooted_tags).to eq('nil') end @@ -608,15 +613,15 @@ def verify_repro(repr = nil) repr end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [3, 8]) + clip = api_map.clip_at(filename, [3, 8]) expect(clip.infer.rooted_tags).to eq('nil, 10') - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('10') - clip = api_map.clip_at('test.rb', [7, 10]) + clip = api_map.clip_at(filename, [7, 10]) expect(clip.infer.rooted_tags).to eq('nil, false') end @@ -633,10 +638,10 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 12]) + clip = api_map.clip_at(filename, [7, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') end @@ -652,10 +657,10 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean') end @@ -674,16 +679,16 @@ def bar(arr, baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [9, 12]) + clip = api_map.clip_at(filename, [9, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [11, 10]) + clip = api_map.clip_at(filename, [11, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -701,16 +706,16 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [10, 10]) + clip = api_map.clip_at(filename, [10, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -727,13 +732,13 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 12]) + clip = api_map.clip_at(filename, [7, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [9, 10]) + clip = api_map.clip_at(filename, [9, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -752,16 +757,16 @@ def bar(baz: nil, other: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [11, 10]) + clip = api_map.clip_at(filename, [11, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -778,13 +783,13 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [7, 12]) + clip = api_map.clip_at(filename, [7, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [9, 10]) + clip = api_map.clip_at(filename, [9, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -804,16 +809,16 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [10, 12]) + clip = api_map.clip_at(filename, [10, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [12, 10]) + clip = api_map.clip_at(filename, [12, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -828,19 +833,19 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [6, 44]) + clip = api_map.clip_at(filename, [6, 44]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [6, 51]) + clip = api_map.clip_at(filename, [6, 51]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [7, 10]) + clip = api_map.clip_at(filename, [7, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -858,16 +863,16 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [10, 10]) + clip = api_map.clip_at(filename, [10, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean') end @@ -885,16 +890,16 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [10, 10]) + clip = api_map.clip_at(filename, [10, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean') end @@ -916,24 +921,24 @@ def bar(baz: nil) baz end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 10]) + clip = api_map.clip_at(filename, [5, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [8, 12]) + clip = api_map.clip_at(filename, [8, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') - clip = api_map.clip_at('test.rb', [10, 12]) + clip = api_map.clip_at(filename, [10, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean') pending('better scoping of return if in begin/rescue/ensure') - clip = api_map.clip_at('test.rb', [12, 12]) + clip = api_map.clip_at(filename, [12, 12]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') - clip = api_map.clip_at('test.rb', [14, 10]) + clip = api_map.clip_at(filename, [14, 10]) expect(clip.infer.rooted_tags).to eq('::Boolean, nil') end @@ -949,17 +954,17 @@ def a b c end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [6, 10]) + clip = api_map.clip_at(filename, [6, 10]) expect(clip.infer.to_s).to eq('String') - clip = api_map.clip_at('test.rb', [7, 17]) + clip = api_map.clip_at(filename, [7, 17]) expect(clip.infer.to_s).to eq('nil') - clip = api_map.clip_at('test.rb', [8, 10]) + clip = api_map.clip_at(filename, [8, 10]) expect(clip.infer.to_s).to eq('String') end @@ -973,9 +978,9 @@ def foo a 123 end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [5, 17]) + clip = api_map.clip_at(filename, [5, 17]) expect(clip.infer.to_s).to eq('Integer') end @@ -992,9 +997,9 @@ def foo? out end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [9, 10]) + clip = api_map.clip_at(filename, [9, 10]) expect(clip.infer.to_s).to eq('Boolean') end @@ -1016,12 +1021,12 @@ def check end end end - ), 'test.rb') + ), filename) api_map = Solargraph::ApiMap.new.map(source) - clip = api_map.clip_at('test.rb', [11, 12]) + clip = api_map.clip_at(filename, [11, 12]) expect(clip.infer.to_s).to eq('Repro') - clip = api_map.clip_at('test.rb', [13, 12]) + clip = api_map.clip_at(filename, [13, 12]) expect(clip.infer.to_s).to eq('ReproBase') end end From 102d35743f4b16b87f052df0b71430ee940fc5da Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 16:45:52 -0500 Subject: [PATCH 109/206] Revert debugging --- .rubocop_todo.yml | 2 ++ spec/doc_map_spec.rb | 7 +----- .../gemspecs_fetch_dependencies_spec.rb | 24 ++++++++---------- .../gemspecs_resolve_require_spec.rb | 25 ++++++++----------- spec/workspace/require_paths_spec.rb | 7 +----- 5 files changed, 25 insertions(+), 40 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f8fbe4c88..acc8d3b13 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -194,6 +194,8 @@ RSpec/EmptyHook: - 'spec/gem_pins_spec.rb' - 'spec/library_spec.rb' - 'spec/logging_spec.rb' + - 'spec/pin/method_spec.rb' + - 'spec/workspace/gemspecs_resolve_require_spec.rb' # This cop supports safe autocorrection (--autocorrect). RSpec/ExpectActual: diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 36f2dd063..56c49f68f 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -19,12 +19,7 @@ let(:plain_doc_map) { described_class.new([], workspace, out: nil) } before do - out = nil - time = Benchmark.measure do - out = doc_map.cache_doc_map_gems!(STDERR) if pre_cache - end - STDERR.puts("Cached doc map gems for #{requires} in #{time.real.round(2)} seconds in pid #{Process.pid}") - out + doc_map.cache_doc_map_gems!($stderr) if pre_cache end context 'with a require in solargraph test bundle' do diff --git a/spec/workspace/gemspecs_fetch_dependencies_spec.rb b/spec/workspace/gemspecs_fetch_dependencies_spec.rb index d08613b98..2ea996ab3 100644 --- a/spec/workspace/gemspecs_fetch_dependencies_spec.rb +++ b/spec/workspace/gemspecs_fetch_dependencies_spec.rb @@ -57,21 +57,17 @@ end before do - output = nil - time = Benchmark.measure do - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem '#{gem_name}' - GEMFILE - - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) - end - raise "Failure installing bundle: #{output}" unless status.success? + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem '#{gem_name}' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) end - STDERR.puts("Added #{gem_name} bundle in #{dir_path} gin #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") + raise "Failure installing bundle: #{output}" unless status.success? # ensure Gemfile.lock exists unless File.exist?(File.join(dir_path, 'Gemfile.lock')) diff --git a/spec/workspace/gemspecs_resolve_require_spec.rb b/spec/workspace/gemspecs_resolve_require_spec.rb index 6f1e08261..30a039d8d 100644 --- a/spec/workspace/gemspecs_resolve_require_spec.rb +++ b/spec/workspace/gemspecs_resolve_require_spec.rb @@ -155,21 +155,18 @@ def configure_bundler_spec stub_value let(:dir_path) { File.realpath(Dir.mktmpdir).to_s } def add_bundle - output = nil - time = Benchmark.measure do - # write out Gemfile - File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) - source 'https://rubygems.org' - gem 'backport' - GEMFILE - - # run bundle install - output, status = Solargraph.with_clean_env do - Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) - end - raise "Failure installing bundle: #{output}" unless status.success? + # write out Gemfile + File.write(File.join(dir_path, 'Gemfile'), <<~GEMFILE) + source 'https://rubygems.org' + gem 'backport' + GEMFILE + + # run bundle install + output, status = Solargraph.with_clean_env do + Open3.capture2e('bundle install --verbose --local || bundle install --verbose', chdir: dir_path) end - STDERR.puts("Added backport bundle in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid} - output: \n\n#{output}\n\n") + raise "Failure installing bundle: #{output}" unless status.success? + # ensure Gemfile.lock exists return if File.exist?(File.join(dir_path, 'Gemfile.lock')) raise "Gemfile.lock not found after bundle install in #{dir_path}" diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index f955436fc..4a5ef98b5 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -6,12 +6,7 @@ describe Solargraph::Workspace::RequirePaths do subject(:paths) do - out = nil - time = Benchmark.measure do - out = described_class.new(dir_path, config).generate - end - STDERR.puts("Generated require paths in #{dir_path} in #{time.real.round(2)} seconds in pid #{Process.pid}") - out + described_class.new(dir_path, config).generate end let(:config) { Solargraph::Workspace::Config.new(dir_path) } From 8089dd56cb7bba9f6c61cf0c831176afdea604b5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 17:10:56 -0500 Subject: [PATCH 110/206] Another before :context case --- spec/language_server/message/text_document/rename_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 141242f02..4f0327eef 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true describe Solargraph::LanguageServer::Message::TextDocument::Rename do + before :context do + @temp_file_url = "file:///#{Dir.mktmpdir}/file.rb" + end + let(:temp_file_url) do - "file:///#{Dir.mktmpdir}/file.rb" + @temp_file_url end it 'renames a symbol' do From a5e2bdeb92d8ba556c67dfb0e1b06a25626e8335 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 17:23:23 -0500 Subject: [PATCH 111/206] Avoid actually uncaching things if not needed --- spec/shell_spec.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 2afeb3f22..b63806c5a 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -18,19 +18,33 @@ describe 'uncache' do it 'uncaches without erroring out' do - output = capture_stdout do + allow(Solargraph::PinCache).to receive(:uncache) + + capture_stdout do shell.uncache('backport') end - expect(output).to include('Clearing pin cache in') + expect(Solargraph::PinCache).to have_received(:uncache).twice end it 'uncaches stdlib without erroring out' do - expect { shell.uncache('stdlib') }.not_to raise_error + allow(Solargraph::PinCache).to receive(:uncache) + + capture_stdout do + shell.uncache('stdlib') + end + + expect(Solargraph::PinCache).to have_received(:uncache) end it 'uncaches core without erroring out' do - expect { shell.uncache('core') }.not_to raise_error + allow(Solargraph::PinCache).to receive(:uncache) + + output = capture_stdout do + shell.uncache('core') + end + + expect(Solargraph::PinCache).to have_received(:uncache) end end From a4dfc52841f1ceb2d98dbc37a090387e12376310 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 17:38:25 -0500 Subject: [PATCH 112/206] Debug --- .github/workflows/plugins.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 93efc5b96..7fcffd88a 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -70,7 +70,10 @@ jobs: yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml - name: Install gem types - run: bundle exec rbs collection update + run: | + bundle exec rbs collection update + # avoid trying to do this in parallel during the specs + time bundle exec solargraph gems core stdlib default - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests @@ -81,10 +84,9 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=$(nproc --all) + # PARALLEL=$(nproc --all) - (5m28s-47s)=4m41s + PARALLEL=5 export PARALLEL - # avoid trying to do this in parallel during the specs - time bundle exec solargraph gems core stdlib default bundle exec rake spec rails_typechecking: runs-on: ubuntu-latest From a07be093da5c81ebaba6420bbb08036382a2282f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 17:46:12 -0500 Subject: [PATCH 113/206] Move to PARALLEL=3 --- .github/workflows/plugins.yml | 23 +++++++++++++---------- .github/workflows/rspec.yml | 17 ++++++++++++----- spec/shell_spec.rb | 2 +- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 7fcffd88a..4437a7a33 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,10 +84,11 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle + PARALLEL=3 # PARALLEL=$(nproc --all) - (5m28s-47s)=4m41s - PARALLEL=5 + # PARALLEL=5 # 4m45s export PARALLEL - bundle exec rake spec + bundle exec rake full_spec rails_typechecking: runs-on: ubuntu-latest @@ -143,7 +144,10 @@ jobs: bundle exec solargraph config yq -yi '.plugins += ["solargraph-rails"]' .solargraph.yml - name: Install gem types - run: bundle exec rbs collection update + run: | + bundle exec rbs collection update + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests @@ -156,9 +160,7 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL - # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib - bundle exec rake spec + bundle exec rake full_spec rspec_typechecking: runs-on: ubuntu-latest @@ -210,7 +212,10 @@ jobs: bundle exec solargraph config yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml - name: Install gem types - run: bundle exec rbs collection update + run: | + bundle exec rbs collection update + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests @@ -223,9 +228,7 @@ jobs: bundle config path $PWD/vendor/bundle PARALLEL=$(nproc --all) export PARALLEL - # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib - bundle exec rake spec + bundle exec rake full_spec run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem runs-on: ubuntu-latest diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 92f3df7e4..59beb07c8 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -83,7 +83,10 @@ jobs: run: | bundle update rbs # use latest available for this Ruby version - name: Update types - run: bundle exec rbs collection update + run: | + bundle exec rbs collection update + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib - name: Run tests run: | # Speed up some of the bundle installs we run inside the tests @@ -97,9 +100,7 @@ jobs: PARALLEL=$(nproc --all) export PARALLEL echo "Running tests with PARALLEL=$PARALLEL" - # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib - bundle exec rake spec + bundle exec rake full_spec undercover: runs-on: ubuntu-latest steps: @@ -119,8 +120,14 @@ jobs: - name: Update types run: | bundle exec rbs collection update + # avoid trying to do this in parallel during the specs + bundle exec solargraph gems core stdlib - name: Run tests - run: bundle exec rake spec + run: | + PARALLEL=$(nproc --all) + export PARALLEL + echo "Running tests with PARALLEL=$PARALLEL" + bundle exec rake full_spec - name: Check PR coverage run: | # Speed up some of the bundle installs we run inside the tests diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index b63806c5a..db0f20854 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -40,7 +40,7 @@ it 'uncaches core without erroring out' do allow(Solargraph::PinCache).to receive(:uncache) - output = capture_stdout do + capture_stdout do shell.uncache('core') end From 9953848aaaff26bcb02c6ede1208c5643f42d053 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 17:53:20 -0500 Subject: [PATCH 114/206] Move to WORKERS=3 --- .github/workflows/plugins.yml | 23 +++++++++++------------ .github/workflows/rspec.yml | 16 ++++++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 4437a7a33..bfa6503ff 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,10 +84,9 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=3 - # PARALLEL=$(nproc --all) - (5m28s-47s)=4m41s - # PARALLEL=5 # 4m45s - export PARALLEL + WORKERS=3 + # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 + export WORKERS bundle exec rake full_spec rails_typechecking: runs-on: ubuntu-latest @@ -158,8 +157,8 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=$(nproc --all) - export PARALLEL + WORKERS=$(nproc --all) + export WORKERS bundle exec rake full_spec rspec_typechecking: runs-on: ubuntu-latest @@ -226,8 +225,8 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=$(nproc --all) - export PARALLEL + WORKERS=$(nproc --all) + export WORKERS bundle exec rake full_spec run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem @@ -297,8 +296,8 @@ jobs: # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle cd ../solargraph-rspec - PARALLEL=$(nproc --all) - export PARALLEL + WORKERS=$(nproc --all) + export WORKERS # avoid trying to do this in parallel during the specs bundle exec solargraph gems core stdlib bundle exec appraisal rspec --format progress @@ -359,8 +358,8 @@ jobs: bundle info solargraph bundle info rbs bundle info yard - PARALLEL=$(nproc --all) - export PARALLEL + WORKERS=$(nproc --all) + export WORKERS # avoid trying to do this in parallel during the specs bundle exec solargraph gems core stdlib ALLOW_IMPROVEMENTS=true bundle exec rake spec diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 59beb07c8..f7a316379 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -97,9 +97,9 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=$(nproc --all) - export PARALLEL - echo "Running tests with PARALLEL=$PARALLEL" + WORKERS=$(nproc --all) + export WORKERS + echo "Running tests with WORKERS=$WORKERS" bundle exec rake full_spec undercover: runs-on: ubuntu-latest @@ -124,9 +124,9 @@ jobs: bundle exec solargraph gems core stdlib - name: Run tests run: | - PARALLEL=$(nproc --all) - export PARALLEL - echo "Running tests with PARALLEL=$PARALLEL" + WORKERS=$(nproc --all) + export WORKERS + echo "Running tests with WORKERS=$WORKERS" bundle exec rake full_spec - name: Check PR coverage run: | @@ -138,8 +138,8 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - PARALLEL=$(nproc --all) - export PARALLEL + WORKERS=$(nproc --all) + export WORKERS # avoid trying to do this in parallel during the specs bundle exec solargraph gems core stdlib bundle exec rake undercover From 2291d71288c9f072c56a1cc6a25c561f65dd1b71 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:00:14 -0500 Subject: [PATCH 115/206] Try 2 workers --- .github/workflows/plugins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index bfa6503ff..d1b6f0628 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,7 +84,8 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - WORKERS=3 + WORKERS=2 + # WORKERS=3 # 4m43s # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 export WORKERS bundle exec rake full_spec From 1b0ad596172d8f63a1a2fc5344d1578a6d2fe46a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:06:55 -0500 Subject: [PATCH 116/206] Try 1 worker --- .github/workflows/plugins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index d1b6f0628..c27911b2b 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,7 +84,8 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - WORKERS=2 + WORKERS=1 + # WORKERS=2 # 4m35s # WORKERS=3 # 4m43s # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 export WORKERS From 6a6efe13aea7c092e2c2c9a33c6ee51098a9f510 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:13:16 -0500 Subject: [PATCH 117/206] Set WORKERS=5 --- .github/workflows/plugins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index c27911b2b..45e848e89 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,10 +84,11 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - WORKERS=1 + # WORKERS=1 # 4m35s # WORKERS=2 # 4m35s # WORKERS=3 # 4m43s # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 + WORKERS=5 export WORKERS bundle exec rake full_spec rails_typechecking: From 6943b50123ab5c4bb638480f1f6c3fef773e7db5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:20:02 -0500 Subject: [PATCH 118/206] Set WORKERS=6 --- .github/workflows/plugins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 45e848e89..5035fd5fe 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -88,7 +88,8 @@ jobs: # WORKERS=2 # 4m35s # WORKERS=3 # 4m43s # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 - WORKERS=5 + # WORKERS=5 # 4m31s + WORKERS=6 export WORKERS bundle exec rake full_spec rails_typechecking: From 2adc097be821393c308a672147503a89161d796f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:27:42 -0500 Subject: [PATCH 119/206] Try 7 workers --- .github/workflows/plugins.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 5035fd5fe..6169196a4 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -89,7 +89,8 @@ jobs: # WORKERS=3 # 4m43s # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 # WORKERS=5 # 4m31s - WORKERS=6 + # WORKERS=6 # 4m49s + WORKERS=7 export WORKERS bundle exec rake full_spec rails_typechecking: From b6d8eb6206ed5c077ff04e69f49917605325ede0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:38:02 -0500 Subject: [PATCH 120/206] Disable coverage checking --- .github/workflows/plugins.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 6169196a4..07e06012f 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -90,8 +90,11 @@ jobs: # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 # WORKERS=5 # 4m31s # WORKERS=6 # 4m49s - WORKERS=7 - export WORKERS + WORKERS=7 # 3m39 + # WORKERS=8 + # export WORKERS + SIMPLECOV_DISABLED=true + export SIMPLECOV_DISABLED bundle exec rake full_spec rails_typechecking: runs-on: ubuntu-latest From 474a29fe4de163025454499403d4bdcef16d255f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:44:58 -0500 Subject: [PATCH 121/206] 7 workers --- .github/workflows/plugins.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 07e06012f..4484a087a 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -90,11 +90,11 @@ jobs: # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 # WORKERS=5 # 4m31s # WORKERS=6 # 4m49s - WORKERS=7 # 3m39 + WORKERS=7 # # WORKERS=8 - # export WORKERS - SIMPLECOV_DISABLED=true - export SIMPLECOV_DISABLED + export WORKERS + # SIMPLECOV_DISABLED=true # TODO Reenable - saves maybe 30 seconds + # export SIMPLECOV_DISABLED bundle exec rake full_spec rails_typechecking: runs-on: ubuntu-latest From 0e885ff40b2ee97020346dff8f078be7f978cc94 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:50:48 -0500 Subject: [PATCH 122/206] Avoid uncaching core --- spec/shell_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index db0f20854..875fb9d9d 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -101,11 +101,12 @@ end it 'caches core without erroring out' do - capture_both do - shell.uncache('core') - end + allow(Solargraph::PinCache).to receive(:core?).and_return(false) + allow(Solargraph::PinCache).to receive(:cache_core) expect { shell.cache('core') }.not_to raise_error + + expect(Solargraph::PinCache).to have_received(:cache_core) end it 'gives sensible error for gem that does not exist' do From 642edef69e7994aefecb16d4b76ef81082e64539 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:51:42 -0500 Subject: [PATCH 123/206] 8 workers --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 4484a087a..79fa8eaf0 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -90,8 +90,8 @@ jobs: # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 # WORKERS=5 # 4m31s # WORKERS=6 # 4m49s - WORKERS=7 # - # WORKERS=8 + # WORKERS=7 # 4m38s + WORKERS=8 export WORKERS # SIMPLECOV_DISABLED=true # TODO Reenable - saves maybe 30 seconds # export SIMPLECOV_DISABLED From 9a863a3b5e8ad0af9f477775db0cfa066b64c2d8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 18:57:39 -0500 Subject: [PATCH 124/206] Drop simplecov --- .github/workflows/plugins.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 79fa8eaf0..f52e7b7a4 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,18 +84,18 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - # WORKERS=1 # 4m35s - # WORKERS=2 # 4m35s - # WORKERS=3 # 4m43s - # WORKERS=$(nproc --all) - (5m28s-47s)=4m41s, 4m45s, 4m46 - # WORKERS=5 # 4m31s - # WORKERS=6 # 4m49s - # WORKERS=7 # 4m38s + # WORKERS=1 + # WORKERS=2 + # WORKERS=3 + # WORKERS=$(nproc --all) + # WORKERS=5 + # WORKERS=6 + # WORKERS=7 WORKERS=8 export WORKERS - # SIMPLECOV_DISABLED=true # TODO Reenable - saves maybe 30 seconds - # export SIMPLECOV_DISABLED - bundle exec rake full_spec + SIMPLECOV_DISABLED=true + export SIMPLECOV_DISABLED + bundle exec prspec spec/ rails_typechecking: runs-on: ubuntu-latest From a6926e6fd35b7008c9e6fc1343a69b743f9ad879 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 19:19:53 -0500 Subject: [PATCH 125/206] 1 worker --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index f52e7b7a4..a9f34dfd0 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,14 +84,14 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - # WORKERS=1 + WORKERS=1 # WORKERS=2 # WORKERS=3 # WORKERS=$(nproc --all) # WORKERS=5 # WORKERS=6 # WORKERS=7 - WORKERS=8 + # WORKERS=8 # 4m 32s export WORKERS SIMPLECOV_DISABLED=true export SIMPLECOV_DISABLED From fd86a5eb8d49c11af251eeb280607ba8be3e0ed0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 19:39:58 -0500 Subject: [PATCH 126/206] 4 workers --- .github/workflows/plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a9f34dfd0..f7c83283a 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,10 +84,10 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - WORKERS=1 + # WORKERS=1 # 6m 31s # WORKERS=2 # WORKERS=3 - # WORKERS=$(nproc --all) + WORKERS=$(nproc --all) # WORKERS=5 # WORKERS=6 # WORKERS=7 From da9e50651579deea6a0fed2b5cf686aed3ab9594 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 19:43:28 -0500 Subject: [PATCH 127/206] Debug --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 8cf806275..5d0fe1c5d 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -618,6 +618,7 @@ def cache_next_gemspec spec = cacheable_specs.first return end_cache_progress unless spec + STDERR.puts "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 From a64e773d510bb18df6bea1247113b07f243e9bd5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 19:48:11 -0500 Subject: [PATCH 128/206] Debug --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 5d0fe1c5d..467625315 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -712,6 +712,7 @@ def sync_catalog return if @sync_count.zero? mutex.synchronize do + raise "Just tried to sync in background: #{api_map.uncached_gemspecs} " logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}" source_map_hash.each_value { |map| find_external_requires(map) } api_map.catalog bench From be9369214ce4c553c257666643115d27b9e2a754 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 19:54:10 -0500 Subject: [PATCH 129/206] Debug --- lib/solargraph/library.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 467625315..6b7a23d26 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -618,7 +618,7 @@ def cache_next_gemspec spec = cacheable_specs.first return end_cache_progress unless spec - STDERR.puts "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" + raise "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 @@ -630,8 +630,8 @@ def cache_next_gemspec catalog sync_catalog else - logger.info "Caching #{spec.name} #{spec.version}" Thread.new do + STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" report_cache_progress spec.name, pending kwargs = {} kwargs[:chdir] = workspace.directory.to_s if workspace.directory && !workspace.directory.empty? @@ -712,7 +712,6 @@ def sync_catalog return if @sync_count.zero? mutex.synchronize do - raise "Just tried to sync in background: #{api_map.uncached_gemspecs} " logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}" source_map_hash.each_value { |map| find_external_requires(map) } api_map.catalog bench From a82b6fd330735b917977b3c3dc2ac9f486d10923 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 21:02:59 -0500 Subject: [PATCH 130/206] Reduce background syncs --- spec/language_server/host_spec.rb | 47 ++++++++++++--- .../message/text_document/definition_spec.rb | 14 ++++- .../message/text_document/hover_spec.rb | 8 ++- .../message/text_document/rename_spec.rb | 12 ++++ .../text_document/type_definition_spec.rb | 3 + .../did_change_watched_files_spec.rb | 5 +- spec/language_server/protocol_spec.rb | 3 +- spec/library_spec.rb | 57 ++++++++++++++++++- 8 files changed, 133 insertions(+), 16 deletions(-) diff --git a/spec/language_server/host_spec.rb b/spec/language_server/host_spec.rb index f0497b8f3..c70924cb6 100644 --- a/spec/language_server/host_spec.rb +++ b/spec/language_server/host_spec.rb @@ -67,8 +67,11 @@ File.write(file, "foo = 'foo'") host.start host.prepare dir - Solargraph::LanguageServer::UriHelpers.file_to_uri(file) + file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(file) host.open(file, File.read(file), 1) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) buffer = host.flush times = 0 # @todo Weak timeout for waiting until the diagnostics thread @@ -110,7 +113,13 @@ file1_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri("#{app1_folder}/app.rb") file2_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri("#{app2_folder}/app.rb") host.open_from_disk file1_uri + library = host.library_for(file1_uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) host.open_from_disk file2_uri + library = host.library_for(file2_uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) app1_map = host.document_symbols(file1_uri).map(&:path) expect(app1_map).to include('Folder1App') expect(app1_map).not_to include('Folder2App') @@ -133,6 +142,9 @@ host.prepare(dir) host.open(file_uri, File.read(file), 1) host.remove(dir) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) expect do host.document_symbols(file_uri) end.not_to raise_error @@ -209,6 +221,9 @@ def initialize(foo); end host = described_class.new host.prepare '' host.open uri, code, 1 + library = host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) sleep 0.1 until host.libraries.all?(&:mapped?) result = host.locate_pins({ 'data' => { @@ -247,15 +262,23 @@ def initialize(foo); end it 'rescues InvalidOffset errors' do host = described_class.new - host.open('file:///file.rb', 'class Foo; end', 1) + uri = 'file:///file.rb' + host.open(uri, 'class Foo; end', 1) + library = host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) expect { host.references_from('file:///file.rb', 0, 100) }.not_to raise_error end it 'logs InvalidOffset errors' do allow(Solargraph.logger).to receive(:warn) host = described_class.new - host.open('file:///file.rb', 'class Foo; end', 1) - host.references_from('file:///file.rb', 0, 100) + uri = 'file:///file.rb' + host.open(uri, 'class Foo; end', 1) + library = host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) + host.references_from(uri, 0, 100) expect(Solargraph.logger).to have_received(:warn).with(/InvalidOffsetError/) end end @@ -270,15 +293,23 @@ def initialize(foo); end end it 'creates a library for a file without a workspace' do - @host.open('file:///file.rb', 'class Foo; end', 1) - symbols = @host.document_symbols('file:///file.rb') + uri = 'file:///file.rb' + @host.open(uri, 'class Foo; end', 1) + library = @host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) + symbols = @host.document_symbols(uri) expect(symbols).not_to be_empty end it 'opens a file outside of prepared libraries' do @host.prepare(File.absolute_path(File.join('spec', 'fixtures', 'workspace'))) - @host.open('file:///file.rb', 'class Foo; end', 1) - symbols = @host.document_symbols('file:///file.rb') + uri = 'file:///file.rb' + @host.open(uri, 'class Foo; end', 1) + library = @host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) + symbols = @host.document_symbols(uri) expect(symbols).not_to be_empty end end diff --git a/spec/language_server/message/text_document/definition_spec.rb b/spec/language_server/message/text_document/definition_spec.rb index d84d23cbe..541a9e397 100644 --- a/spec/language_server/message/text_document/definition_spec.rb +++ b/spec/language_server/message/text_document/definition_spec.rb @@ -25,6 +25,9 @@ } } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) message.process expect(message.result.first[:uri]).to eq(other_uri) end @@ -48,6 +51,9 @@ } } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) message.process expect(message.result.first[:uri]).to eq(other_uri) end @@ -58,12 +64,11 @@ host.prepare(path) sleep 0.1 until host.libraries.all?(&:mapped?) host.catalog + file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', 'other.rb')) message = described_class.new(host, { 'params' => { 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join( - path, 'lib', 'other.rb' - )) + 'uri' => file_uri }, 'position' => { 'line' => 0, @@ -71,6 +76,9 @@ } } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) message.process expect(message.result.first[:uri]).to eq(Solargraph::LanguageServer::UriHelpers.file_to_uri(File.join(path, 'lib', 'thing.rb'))) diff --git a/spec/language_server/message/text_document/hover_spec.rb b/spec/language_server/message/text_document/hover_spec.rb index 76b3c9082..6ef633db2 100644 --- a/spec/language_server/message/text_document/hover_spec.rb +++ b/spec/language_server/message/text_document/hover_spec.rb @@ -29,12 +29,13 @@ def foo x = foo.upcase ) host = Solargraph::LanguageServer::Host.new - host.open('file:///test.rb', code, 1) + file_uri = 'file:///test.rb' + host.open(file_uri, code, 1) host.catalog message = described_class.new(host, { 'params' => { 'textDocument' => { - 'uri' => 'file:///test.rb' + 'uri' => file_uri }, 'position' => { 'line' => 4, @@ -42,7 +43,10 @@ def foo } } }) + library = host.library_for(file_uri) + allow(library).to receive(:cacheable_specs).and_return([]) message.process + # keep this from syncing a bunch of bundle gems in background expect(message.result[:contents][:value]).to eq("x\n\n`=~ String`") end end diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 4f0327eef..278d0a9c0 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -32,6 +32,9 @@ class Foo 'newName' => 'Bar' } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(temp_file_url) + allow(library).to receive(:cacheable_specs).and_return([]) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(2) end @@ -62,6 +65,9 @@ def foo(bar) 'newName' => 'baz' } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(temp_file_url) + allow(library).to receive(:cacheable_specs).and_return([]) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end @@ -91,6 +97,9 @@ def foo(bar) 'newName' => 'baz' } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(temp_file_url) + allow(library).to receive(:cacheable_specs).and_return([]) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end @@ -118,6 +127,9 @@ class Namespace::ExampleClass 'newName' => 'Nameplace' } }) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(temp_file_url) + allow(library).to receive(:cacheable_specs).and_return([]) rename.process changes = rename.result[:changes][temp_file_url] expect(changes.length).to eq(3) diff --git a/spec/language_server/message/text_document/type_definition_spec.rb b/spec/language_server/message/text_document/type_definition_spec.rb index 16f7f3006..88eb02bf5 100644 --- a/spec/language_server/message/text_document/type_definition_spec.rb +++ b/spec/language_server/message/text_document/type_definition_spec.rb @@ -19,6 +19,9 @@ } } }) + library = host.library_for(file_uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) message.process expect(message.result.first[:uri]).to eq(something_uri) end diff --git a/spec/language_server/message/workspace/did_change_watched_files_spec.rb b/spec/language_server/message/workspace/did_change_watched_files_spec.rb index ebe76fc50..c5c75fd5f 100644 --- a/spec/language_server/message/workspace/did_change_watched_files_spec.rb +++ b/spec/language_server/message/workspace/did_change_watched_files_spec.rb @@ -73,9 +73,12 @@ ] } }) + library = host.library_for(uri) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) changed.process expect(host.synchronizing?).to be(false) - library = host.library_for(uri) + expect(library.path_pins('Foo')).to be_empty expect(library.path_pins('FooBar')).not_to be_empty expect(changed.error).to be_nil diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 4b55fbf3c..6d969aa31 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -113,9 +113,10 @@ def bar baz end it 'handles textDocument/documentHighlight' do + file_uri = 'file:///file.rb' @protocol.request 'textDocument/documentHighlight', { 'textDocument' => { - 'uri' => 'file:///file.rb' + 'uri' => file_uri }, 'position' => { 'line' => 1, diff --git a/spec/library_spec.rb b/spec/library_spec.rb index b8f152eaa..48d25cffa 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -24,10 +24,13 @@ it 'returns a Completion' do library = described_class.new + filename = 'file.rb' library.attach Solargraph::Source.load_string(%( x = 1 x - ), 'file.rb', 0) + ), filename, 0) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) completion = library.completions_at('file.rb', 2, 7) expect(completion).to be_a(Solargraph::SourceMap::Completion) expect(completion.pins.map(&:name)).to include('x') @@ -98,6 +101,8 @@ def bar end end ), 'file.rb', 0 + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach src paths = library.definitions_at('file.rb', 2, 13).map(&:path) expect(paths).to include('Foo#bar') @@ -115,6 +120,8 @@ def self.bar Foo.bar ), 'file.rb', 0 library.attach src + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) paths = library.type_definitions_at('file.rb', 7, 13).map(&:path) expect(paths).to include('Bar') end @@ -128,6 +135,8 @@ def bar baz, key: '' end Foo.new.bar() ), 'file.rb', 0 + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach src pins = library.signatures_at('file.rb', 5, 18) expect(pins.length).to eq(1) @@ -168,6 +177,8 @@ def bar baz, key: '' src = Solargraph::Source.load_string(%( puts 'hello' ), 'file.rb', 0) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach src result = library.diagnose 'file.rb' expect(result).to be_a(Array) @@ -180,6 +191,8 @@ def bar baz, key: '' allow(config).to receive_messages(plugins: [], required: [], reporters: ['all!']) workspace = Solargraph::Workspace.new directory, config library = described_class.new workspace + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src = Solargraph::Source.load_string(%( puts 'hello' ), 'file.rb', 0) @@ -196,6 +209,8 @@ def bar end end ), 'file.rb', 0) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach src pins = library.document_symbols 'file.rb' expect(pins.length).to eq(2) @@ -212,6 +227,8 @@ def bar it 'collects references to a new method on a constant from assignment of Class.new' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( Foo.new ), 'file1.rb', 0) @@ -229,6 +246,8 @@ def bar it 'collects references to a new method to a constant from assignment' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( Foo.new ), 'file1.rb', 0) @@ -248,6 +267,8 @@ class Foo it 'collects references to an instance method symbol' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( class Foo def bar @@ -275,6 +296,8 @@ def bar; end it 'collects references to a class method symbol' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( class Foo def self.bar @@ -310,6 +333,8 @@ def bar; end it 'collects stripped references to constant symbols' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( class Foo def bar @@ -339,6 +364,8 @@ class Other it 'rejects new references from different classes' do workspace = Solargraph::Workspace.new('*') library = described_class.new(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Foo def bar @@ -370,6 +397,8 @@ def bar it 'returns YARD documentation from sources' do library = described_class.new + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src = Solargraph::Source.load_string(%( class Foo # My bar method @@ -405,6 +434,8 @@ def bar; end it 'finds unique references' do library = described_class.new(Solargraph::Workspace.new('*')) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) src1 = Solargraph::Source.load_string(%( class Foo end @@ -421,6 +452,8 @@ class Foo it 'includes method parameters in references' do library = described_class.new(Solargraph::Workspace.new('*')) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Foo def bar(baz) @@ -437,6 +470,8 @@ def bar(baz) it "lies about names when client can't handle the truth" do library = described_class.new(Solargraph::Workspace.new('*')) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Foo def 🤦🏻foo♀️; 123; end @@ -449,6 +484,8 @@ def 🤦🏻foo♀️; 123; end it 'tells the truth about names when client can handle the truth' do library = described_class.new(Solargraph::Workspace.new('*')) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Foo def 🤦🏻foo♀️; 123; end @@ -461,6 +498,8 @@ def 🤦🏻foo♀️; 123; end it 'includes block parameters in references' do library = described_class.new(Solargraph::Workspace.new('*')) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( 100.times do |foo| puts foo @@ -484,6 +523,8 @@ class CallerExample def foo; end end ), 'test.rb') + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach source # Start of tag pins = library.definitions_at('test.rb', 4, 19) @@ -508,6 +549,8 @@ def foo; end end ), 'test.rb') library.attach source + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) pins = library.definitions_at('test.rb', 5, 19) expect(pins.map(&:path)).to include('Tagged') pins = library.definitions_at('test.rb', 5, 26) @@ -526,6 +569,8 @@ def foo; end end ), 'test.rb') library.attach source + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) pins = library.definitions_at('test.rb', 3, 31) expect(pins.map(&:path)).to include('TaggedExample') end @@ -540,12 +585,16 @@ def foo; end end ), 'test.rb') library.attach source + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) pins = library.definitions_at('test.rb', 3, 31) expect(pins.map(&:path)).to include('TaggedExample') end it 'skips comment text outside of tags' do library = described_class.new + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( # String def foo; end @@ -557,6 +606,8 @@ def foo; end it 'marks aliases as methods or attributes in completion items' do library = described_class.new + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Example attr_reader :foo @@ -580,6 +631,8 @@ def baz it 'marks aliases as methods or attributes in definitions' do library = described_class.new + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) source = Solargraph::Source.load_string(%( class Example attr_reader :foo @@ -638,6 +691,8 @@ def bar; end it 'removes files from Library#source_map_hash' do workspace = File.absolute_path(File.join('spec', 'fixtures', 'workspace')) library = described_class.load(workspace) + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.map! library.catalog other_file = File.absolute_path(File.join('spec', 'fixtures', 'workspace', 'lib', 'other.rb')) From cc37729b8bc7d231773cacda1b7a85d0b60c40d7 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 21:31:32 -0500 Subject: [PATCH 131/206] Try again --- .../message/text_document/rename_spec.rb | 6 ++-- spec/language_server/protocol_spec.rb | 31 +++++++++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 278d0a9c0..feeb6b89c 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -51,6 +51,9 @@ def foo(bar) end ), 1) + # keep this from syncing a bunch of bundle gems in background + library = host.library_for(temp_file_url) + allow(library).to receive(:cacheable_specs).and_return([]) rename = described_class.new(host, { 'id' => 1, 'method' => 'textDocument/rename', @@ -65,9 +68,6 @@ def foo(bar) 'newName' => 'baz' } }) - # keep this from syncing a bunch of bundle gems in background - library = host.library_for(temp_file_url) - allow(library).to receive(:cacheable_specs).and_return([]) rename.process expect(rename.result[:changes][temp_file_url].length).to eq(3) end diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 6d969aa31..6b7d501fb 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'tmpdir' + class Protocol attr_reader :response @@ -39,6 +41,21 @@ def stop @protocol = described_class.new(Solargraph::LanguageServer::Host.new) end + # Ensure we don't start caching gems from current bundle in background + around do |testobj| + original_dir = Dir.pwd + temp_dir = Dir.mktmpdir + Dir.chdir temp_dir + begin + Bundler.with_unbundled_env do + testobj.run + end + ensure + Dir.chdir(original_dir) + FileUtils.remove_entry(temp_dir) + end + end + after :context do @protocol.stop end @@ -438,16 +455,17 @@ def bar baz end it 'handles textDocument/formatting' do + filename = File.realpath('spec/fixtures/formattable.rb', PROJECT_DIRECTORY) @protocol.request 'textDocument/didOpen', { 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable.rb')), - 'text' => File.read('spec/fixtures/formattable.rb'), + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(filename), + 'text' => File.read(filename), 'version' => 1 } } @protocol.request 'textDocument/formatting', { 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable.rb')) + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(filename) } } response = @protocol.response @@ -456,16 +474,17 @@ def bar baz end it 'can format file without file extension' do + filename = File.realpath('spec/fixtures/formattable', PROJECT_DIRECTORY) @protocol.request 'textDocument/didOpen', { 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable')), - 'text' => File.read('spec/fixtures/formattable'), + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(filename), + 'text' => File.read(filename), 'version' => 1 } } @protocol.request 'textDocument/formatting', { 'textDocument' => { - 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(File.realpath('spec/fixtures/formattable')) + 'uri' => Solargraph::LanguageServer::UriHelpers.file_to_uri(filename) } } response = @protocol.response From fd697999c47668303bdc338597d8d435518eb2f8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 21:39:10 -0500 Subject: [PATCH 132/206] Run specs in unbundled env --- spec/library_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 48d25cffa..35cf783d9 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -9,6 +9,13 @@ # need it before end + # Ensure we don't start syncing the entire bundle here in the background' + around do |testobj| + Bundler.with_unbundled_env do + testobj.run + end + end + it 'does not open created files in the workspace' do Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path From b2d8fb6055bb9f378f528358f5f87ac0b0b8de4f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 21:44:46 -0500 Subject: [PATCH 133/206] Try again --- spec/library_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 35cf783d9..fc250a010 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -7,6 +7,7 @@ before :context do # run these in order so we don't uncache backport right when we # need it before + Solargraph::Shell.new.gems('backport') end # Ensure we don't start syncing the entire bundle here in the background' @@ -80,7 +81,6 @@ def foo(adapter) end before do - Solargraph::Shell.new.gems('backport') end it 'returns a Completion' do From 38a93e28c0df4881a40151ed8376371c4fad12ec Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 21:49:31 -0500 Subject: [PATCH 134/206] Debug --- lib/solargraph/library.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 6b7a23d26..94853fd3c 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -618,7 +618,7 @@ def cache_next_gemspec spec = cacheable_specs.first return end_cache_progress unless spec - raise "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" + STDERR.puts "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 From fc6e874075466b633e643dba87aa9bad175c233d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 22:03:41 -0500 Subject: [PATCH 135/206] Try again --- spec/library_spec.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index fc250a010..c735db175 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -7,14 +7,6 @@ before :context do # run these in order so we don't uncache backport right when we # need it before - Solargraph::Shell.new.gems('backport') - end - - # Ensure we don't start syncing the entire bundle here in the background' - around do |testobj| - Bundler.with_unbundled_env do - testobj.run - end end it 'does not open created files in the workspace' do @@ -33,12 +25,12 @@ it 'returns a Completion' do library = described_class.new filename = 'file.rb' + # keep this from syncing a bunch of bundle gems in background + allow(library).to receive(:cacheable_specs).and_return([]) library.attach Solargraph::Source.load_string(%( x = 1 x ), filename, 0) - # keep this from syncing a bunch of bundle gems in background - allow(library).to receive(:cacheable_specs).and_return([]) completion = library.completions_at('file.rb', 2, 7) expect(completion).to be_a(Solargraph::SourceMap::Completion) expect(completion.pins.map(&:name)).to include('x') @@ -229,6 +221,7 @@ def bar before :context do # run these in order so we don't uncache backport right when we # need it before + Solargraph::Shell.new.gems('backport') end it 'collects references to a new method on a constant from assignment of Class.new' do From 849747e7c173ec2f2a3d9edfcac1efc2218a1208 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 22:11:59 -0500 Subject: [PATCH 136/206] Debug --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 94853fd3c..bf17ec75b 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -632,6 +632,7 @@ def cache_next_gemspec else Thread.new do STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" + raise "Was caching #{spec.name}" if ['activesupport'].include?(spec.name) report_cache_progress spec.name, pending kwargs = {} kwargs[:chdir] = workspace.directory.to_s if workspace.directory && !workspace.directory.empty? From 1669309218c50ab9ccca6e35f56e1dfbf7bd3310 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 22:18:28 -0500 Subject: [PATCH 137/206] Debug --- lib/solargraph/library.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index bf17ec75b..e3d83bf26 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -630,9 +630,9 @@ def cache_next_gemspec catalog sync_catalog else + STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" + raise "Was caching #{spec.name}" if ['activesupport'].include?(spec.name) Thread.new do - STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - raise "Was caching #{spec.name}" if ['activesupport'].include?(spec.name) report_cache_progress spec.name, pending kwargs = {} kwargs[:chdir] = workspace.directory.to_s if workspace.directory && !workspace.directory.empty? From 5e72ef43ebdb0926f98051e9aa1ced2cf55d46d0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sun, 1 Feb 2026 22:30:03 -0500 Subject: [PATCH 138/206] Debug --- spec/language_server/protocol_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 6b7d501fb..5abfcd25f 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -47,7 +47,7 @@ def stop temp_dir = Dir.mktmpdir Dir.chdir temp_dir begin - Bundler.with_unbundled_env do + Solargraph.with_clean_env do testobj.run end ensure From de8d4ac73404ba0a31b780a885213cf6e552de58 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:03:34 -0700 Subject: [PATCH 139/206] Drop dead before block --- spec/library_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/library_spec.rb b/spec/library_spec.rb index c735db175..27245bf87 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -72,9 +72,6 @@ def foo(adapter) # need it before end - before do - end - it 'returns a Completion' do library = described_class.new(Solargraph::Workspace.new(Dir.pwd, Solargraph::Workspace::Config.new)) From 9f7cd02e45c7b883d45d303577411982c1a6062b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:15:30 -0700 Subject: [PATCH 140/206] Fix expectations in now-working spec --- spec/type_checker/levels/strict_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index ea6515b80..2bb54f2b9 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -826,8 +826,6 @@ def meth(param1) end it 'uses nil? to refine type' do - pending 'nil? support in flow sensitive typing' - checker = type_checker(%( # @sg-ignore # @type [String, nil] @@ -838,7 +836,7 @@ def meth(param1) foo.downcase end )) - expect(checker.problems.map(&:message)).to eq(['Unresolved call to upcase']) + expect(checker.problems.map(&:message)).to eq(['Unresolved call to upcase on nil']) end it 'refines types on is_a? and && to downcast and avoid false positives' do From d801cf1ad452241b965b663f0f1b129411b35ffa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:17:03 -0700 Subject: [PATCH 141/206] Fix only-failures issues --- Rakefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 71c82e4dc..b1d6e17c6 100755 --- a/Rakefile +++ b/Rakefile @@ -34,7 +34,7 @@ task :typecheck_alpha do end desc 'Run RSpec tests, starting with the ones that failed last time' -task spec: %i[spec_failed undercover_no_fail full_spec] do +task spec: %i[spec_failed full_spec] do undercover end @@ -90,7 +90,9 @@ desc 'Re-run failed specs. Add --fail-fast in your .rspec-local file if desired task :spec_failed do # allow user to check out any persistent failures while looking for # more in the whole test suite - sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec prspec --only-failures || true' + # + # Note: prspec doesn't support --only-failures, so we have to use rspec directly here. + sh 'TEST_COVERAGE_COMMAND_NAME=next-failure bundle exec rspec --only-failures || true' end desc 'Run undercover and show output without failing the task if it fails' From 7704c913a7b73f168e525e62b083d66e3ad2d3b8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:34:18 -0700 Subject: [PATCH 142/206] Switch to gem with fewer depenencies to speed up spec --- spec/gem_pins_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 4b21dbabb..2598317dd 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -15,8 +15,8 @@ # seems like we still have a race condition in pin caching end - let(:path) { 'RBS::EnvironmentLoader#core_root' } - let(:requires) { ['rbs'] } + let(:path) { 'Hashdiff.diff' } + let(:requires) { ['hashdiff'] } it 'can merge YARD and RBS' do expect(pin.source).to eq(:combined), "Expected to merge YARD and RBS for #{path} in #{workspace.directory}" From 8fee67001aea6fae38b278ed8812e9b05b8efcab Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:37:50 -0700 Subject: [PATCH 143/206] Update expectations in other specs --- spec/gem_pins_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/gem_pins_spec.rb b/spec/gem_pins_spec.rb index 2598317dd..f4e83c4dd 100644 --- a/spec/gem_pins_spec.rb +++ b/spec/gem_pins_spec.rb @@ -23,12 +23,12 @@ end it 'finds types from RBS' do - expect(pin.return_type.to_s).to eq('Pathname, nil') + expect(pin.return_type.to_s).to eq('Array') end it 'finds locations from YARD' do expect(pin).not_to be_nil, "Expected to find pin for #{path} in #{workspace.directory}" - expect(pin.location.filename).to end_with('environment_loader.rb') + expect(pin.location.filename).to end_with('diff.rb') end end From 0fa1c002fa0a80914b8daed1ef3b35462718a752 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 07:47:28 -0700 Subject: [PATCH 144/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index feeb6b89c..c293f1165 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -132,6 +132,7 @@ class Namespace::ExampleClass allow(library).to receive(:cacheable_specs).and_return([]) rename.process changes = rename.result[:changes][temp_file_url] + expect(changes).not_to be_nil, -> { "Expected to find changes for #{temp_file_url} in #{rename.result.inspect}" } expect(changes.length).to eq(3) expect(changes.first[:range][:start][:character]).to eq(13) expect(changes.first[:range][:end][:character]).to eq(22) From 68cf467e1c60d29467053fc0d6e68b2ccfa9e692 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 08:32:33 -0700 Subject: [PATCH 145/206] Debug --- lib/solargraph/library.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index e3d83bf26..fe8d66c0c 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -631,7 +631,7 @@ def cache_next_gemspec sync_catalog else STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - raise "Was caching #{spec.name}" if ['activesupport'].include?(spec.name) + raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)}" if ['activesupport'].include?(spec.name) Thread.new do report_cache_progress spec.name, pending kwargs = {} From 3e3be1ad77be99c4c84458f641518ad583f5d194 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 08:39:54 -0700 Subject: [PATCH 146/206] Add caching of default gems --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index f7a316379..e2f09fffe 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -86,7 +86,7 @@ jobs: run: | bundle exec rbs collection update # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib + bundle exec solargraph gems default core stdlib - name: Run tests run: | # Speed up some of the bundle installs we run inside the tests From c4c1e8fa562ca98ca33a53ce8e71c103dc936d9f Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 08:48:10 -0700 Subject: [PATCH 147/206] Revert --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e2f09fffe..f7a316379 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -86,7 +86,7 @@ jobs: run: | bundle exec rbs collection update # avoid trying to do this in parallel during the specs - bundle exec solargraph gems default core stdlib + bundle exec solargraph gems core stdlib - name: Run tests run: | # Speed up some of the bundle installs we run inside the tests From 96482b885e95d3d3676aa544a71faa9f82ac0964 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 08:55:08 -0700 Subject: [PATCH 148/206] Avoid unneeded gem caching for perf --- lib/solargraph/workspace.rb | 5 +++-- lib/solargraph/workspace/config.rb | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 0e4e29b13..914374a87 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -23,8 +23,9 @@ class Workspace attr_reader :gemnames alias source_gems gemnames - # @todo Remove '' and '*' special cases - # @param directory [String] + # @todo Remove '*' special case + # @param directory [String] If empty, no config will be loaded, + # and no RBS collection will be used. Useful for specs. # @param config [Config, nil] # @param server [Hash] def initialize directory = '', config = nil, server = {} diff --git a/lib/solargraph/workspace/config.rb b/lib/solargraph/workspace/config.rb index bd494b380..45e0ebb95 100644 --- a/lib/solargraph/workspace/config.rb +++ b/lib/solargraph/workspace/config.rb @@ -20,7 +20,11 @@ class Config # @param directory [String] def initialize directory = '' - @directory = File.absolute_path(directory) + @directory = if directory.empty? + '' + else + File.absolute_path(directory) + end @raw_data = config_data included excluded From b4556c0f94d5f163f66d3e34555a02b0e2a6235b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 09:10:20 -0700 Subject: [PATCH 149/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index c293f1165..8e43b69dc 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -69,7 +69,7 @@ def foo(bar) } }) rename.process - expect(rename.result[:changes][temp_file_url].length).to eq(3) + expect(rename.result[:changes][temp_file_url].length).to eq(3), -> { "Expected to find 3 changes for #{temp_file_url} in #{rename.result.inspect}" } end it 'renames an argument symbol from method body' do From 5c235dc5c84f3c8618a41a3ed95c3b4d983e0f62 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 09:38:04 -0700 Subject: [PATCH 150/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 8e43b69dc..7ccfb2799 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -69,6 +69,7 @@ def foo(bar) } }) rename.process + expect(rename.result[:changes][temp_file_url]).not_to be_nil, -> { "Expected to find changes for #{temp_file_url} in #{rename.result.inspect}" } expect(rename.result[:changes][temp_file_url].length).to eq(3), -> { "Expected to find 3 changes for #{temp_file_url} in #{rename.result.inspect}" } end From df0c2055e158a2b181888fb718ece51c2df3c9db Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 09:44:18 -0700 Subject: [PATCH 151/206] Debug --- spec/language_server/protocol_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 5abfcd25f..c5e34a100 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -141,6 +141,7 @@ def bar baz } } response = @protocol.response + expect(response['result']).not_to be_nil, -> { "Expected result to be non-nil, got #{response.inspect}" } # Two references to Foo: the class definition and the Foo.new call expect(response['result'].length).to eq(2) end From b34c3d03d4f2f204c7e19bc0a10d4bc5cd2e5c43 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 09:53:28 -0700 Subject: [PATCH 152/206] Debug --- lib/solargraph/library.rb | 2 +- spec/language_server/protocol_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index fe8d66c0c..7a733850d 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -631,7 +631,7 @@ def cache_next_gemspec sync_catalog else STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)}" if ['activesupport'].include?(spec.name) + raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config workspace.config.raw_data.inspect" if ['activesupport'].include?(spec.name) Thread.new do report_cache_progress spec.name, pending kwargs = {} diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index c5e34a100..4fa67752f 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -143,7 +143,7 @@ def bar baz response = @protocol.response expect(response['result']).not_to be_nil, -> { "Expected result to be non-nil, got #{response.inspect}" } # Two references to Foo: the class definition and the Foo.new call - expect(response['result'].length).to eq(2) + expect(response['result'].length).to eq(2), -> { "Expected 2 highlights for Foo, got #{response['result'].length} in #{response.inspect}" } end it 'handles textDocument/didChange' do From eb79d3b5e2b0417e20d6bc114f369969749861c0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 10:15:54 -0700 Subject: [PATCH 153/206] Debug --- lib/solargraph/library.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 7a733850d..d325406fc 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -631,7 +631,7 @@ def cache_next_gemspec sync_catalog else STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config workspace.config.raw_data.inspect" if ['activesupport'].include?(spec.name) + raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config #{workspace.config.raw_data.inspect}" if ['activesupport'].include?(spec.name) Thread.new do report_cache_progress spec.name, pending kwargs = {} From e981123ace49f437bcf2f1a6e85820d5744941b8 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 10:27:30 -0700 Subject: [PATCH 154/206] Debug --- .../language_server/message/text_document/rename_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 7ccfb2799..404b780da 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -69,6 +69,15 @@ def foo(bar) } }) rename.process + # try for 20 seconds to get the result, since this can be slow on CI + timeout = Time.now + 20 + until rename.result[:changes] && rename.result[:changes][temp_file_url] && !rename.result[:changes][temp_file_url].empty? + sleep 0.1 + if Time.now > timeout + raise "Timed out waiting for rename result: #{rename.result.inspect}" + end + end + expect(rename.result[:changes][temp_file_url]).not_to be_nil, -> { "Expected to find changes for #{temp_file_url} in #{rename.result.inspect}" } expect(rename.result[:changes][temp_file_url].length).to eq(3), -> { "Expected to find 3 changes for #{temp_file_url} in #{rename.result.inspect}" } end From 31648e395c2b768b1c23fb987059e18d0a7cd1ce Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 10:28:47 -0700 Subject: [PATCH 155/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 404b780da..5693868e0 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -111,6 +111,14 @@ def foo(bar) library = host.library_for(temp_file_url) allow(library).to receive(:cacheable_specs).and_return([]) rename.process + # try for 20 seconds to get the result, since this can be slow on CI + timeout = Time.now + 20 + until rename.result[:changes] && rename.result[:changes][temp_file_url] && !rename.result[:changes][temp_file_url].empty? + sleep 0.1 + if Time.now > timeout + raise "Timed out waiting for rename result: #{rename.result.inspect}" + end + end expect(rename.result[:changes][temp_file_url].length).to eq(3) end From a55b54aaa9fc1d38646def81d966f87c20ab6393 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 10:46:06 -0700 Subject: [PATCH 156/206] Debug --- lib/solargraph/library.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index d325406fc..25ddb6a73 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -631,7 +631,16 @@ def cache_next_gemspec sync_catalog else STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config #{workspace.config.raw_data.inspect}" if ['activesupport'].include?(spec.name) + if ['activesupport'].include?(spec.name) + STDERR.puts "VMB: Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" + STDERR.puts "VMB: workspace.config.raw_data.inspect: #{workspace.config.raw_data.inspect}" + STDERR.puts "VMB: api_map.uncached_gemspecs.map(&:name): #{api_map.uncached_gemspecs.map(&:name)}" + STDERR.puts "VMB: workspace.directory: #{workspace.directory}" + STDERR.puts "VMB: source_maps: #{source_map_hash.keys}" + STDERR.puts "VMB: workspace.require_paths: #{workspace.require_paths}" + STDERR.puts "VMB: global_environ: #{workspace.global_environ}" + raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config #{workspace.config.raw_data.inspect}" + end Thread.new do report_cache_progress spec.name, pending kwargs = {} From 0e08bf8f81847fb4e9c763f99922474bae023bc1 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 10:55:20 -0700 Subject: [PATCH 157/206] Debug --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 25ddb6a73..01936b886 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -639,6 +639,7 @@ def cache_next_gemspec STDERR.puts "VMB: source_maps: #{source_map_hash.keys}" STDERR.puts "VMB: workspace.require_paths: #{workspace.require_paths}" STDERR.puts "VMB: global_environ: #{workspace.global_environ}" + STDERR.puts "VMB: global_environ.requires: #{workspace.global_environ.requires}" raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config #{workspace.config.raw_data.inspect}" end Thread.new do From 2af160dc12e35c67e71c514b5318f2c9ca065903 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 11:26:41 -0700 Subject: [PATCH 158/206] Debug --- lib/solargraph/library.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 01936b886..3272093c1 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -618,7 +618,6 @@ def cache_next_gemspec spec = cacheable_specs.first return end_cache_progress unless spec - STDERR.puts "cache_next_gemspec: Caching gemspec #{spec.name} #{spec.version}" pending = api_map.uncached_gemspecs.length - cache_errors.length - 1 @@ -630,18 +629,6 @@ def cache_next_gemspec catalog sync_catalog else - STDERR.puts "Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - if ['activesupport'].include?(spec.name) - STDERR.puts "VMB: Caching #{spec.name} #{spec.version} in thread #{Thread.current.object_id}" - STDERR.puts "VMB: workspace.config.raw_data.inspect: #{workspace.config.raw_data.inspect}" - STDERR.puts "VMB: api_map.uncached_gemspecs.map(&:name): #{api_map.uncached_gemspecs.map(&:name)}" - STDERR.puts "VMB: workspace.directory: #{workspace.directory}" - STDERR.puts "VMB: source_maps: #{source_map_hash.keys}" - STDERR.puts "VMB: workspace.require_paths: #{workspace.require_paths}" - STDERR.puts "VMB: global_environ: #{workspace.global_environ}" - STDERR.puts "VMB: global_environ.requires: #{workspace.global_environ.requires}" - raise "Was caching #{spec.name} from #{api_map.uncached_gemspecs.map(&:name)} with config #{workspace.config.raw_data.inspect}" - end Thread.new do report_cache_progress spec.name, pending kwargs = {} From 53676c51b65877bf8c5e17e1c87221feb5ac5510 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 11:31:31 -0700 Subject: [PATCH 159/206] Drop extra / --- spec/language_server/message/text_document/rename_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 5693868e0..34c50a3e3 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -2,7 +2,7 @@ describe Solargraph::LanguageServer::Message::TextDocument::Rename do before :context do - @temp_file_url = "file:///#{Dir.mktmpdir}/file.rb" + @temp_file_url = "file://#{Dir.mktmpdir}/file.rb" end let(:temp_file_url) do From 59b460da80b567a2bc9002682f9e2f7fabc7d486 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 11:41:10 -0700 Subject: [PATCH 160/206] Debug --- lib/solargraph/language_server/message/text_document/rename.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index b0aff3c8a..c75ab9f1a 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -8,6 +8,7 @@ class Rename < Base def process locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true) + STDERR.puts "Rename: #{locs.length} references in #{params['textDocument']['uri']} at line #{params['position']['line']} char #{params['position']['character']}" changes = {} locs.each do |loc| uri = file_to_uri(loc.filename) From b5bcd4b06402c9e8f9861c31d87d81aa69e86c90 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 11:49:46 -0700 Subject: [PATCH 161/206] Debug --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 3272093c1..0df205fc5 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -587,6 +587,7 @@ def read filename def handle_file_not_found filename, error raise error unless workspace.source(filename) Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap" + STDERR.puts "#{filename} is not cataloged in the ApiMap" nil end From dda83226618027d2494c44eedb359edf58aee644 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 11:59:32 -0700 Subject: [PATCH 162/206] Debug --- lib/solargraph/language_server/host.rb | 1 + .../language_server/message/text_document/rename.rb | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index d61283567..06896f226 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -590,6 +590,7 @@ def references_from uri, line, column, strip: true, only: false rescue FileNotFoundError, InvalidOffsetError => e Solargraph.logger.warn "[#{e.class}] #{e.message}" Solargraph.logger.debug e.backtrace + STDERR.puts "[#{e.class}] #{e.message}" [] end diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index c75ab9f1a..4ee2525a4 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -8,7 +8,12 @@ class Rename < Base def process locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true) - STDERR.puts "Rename: #{locs.length} references in #{params['textDocument']['uri']} at line #{params['position']['line']} char #{params['position']['character']}" + STDERR.puts "Rename: #{locs.length} references in #{params['textDocument']['uri']} at line #{params['position']['line']} char #{params['position']['character']}. " + if locs.empty? + library = host.library_for(params['textDocument']['uri']) + STDERR.puts("Total pins length: #{library.pins.length}") + + end changes = {} locs.each do |loc| uri = file_to_uri(loc.filename) From a82ab1ac95d115232fec18219a1015f844399bae Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 12:04:49 -0700 Subject: [PATCH 163/206] Debug --- lib/solargraph/api_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 66f68533f..bfc153680 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -614,7 +614,7 @@ def locate_pins location # @return [SourceMap::Clip] def clip cursor # @sg-ignore Need to add nil check here - raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename) + raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename} - current source map keys are #{source_map_hash.keys}" unless source_map_hash.key?(cursor.filename) SourceMap::Clip.new(self, cursor) end From f7b84a15cd0124d21d439cf9e1bfb8a8a581effe Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 12:13:45 -0700 Subject: [PATCH 164/206] Debug --- lib/solargraph/language_server/host.rb | 1 + lib/solargraph/language_server/message/extended/document.rb | 1 + .../language_server/message/text_document/completion.rb | 1 + lib/solargraph/language_server/message/text_document/hover.rb | 1 + .../language_server/message/text_document/signature_help.rb | 1 + 5 files changed, 5 insertions(+) diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index 06896f226..2af432bef 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -215,6 +215,7 @@ def diagnose uri } rescue DiagnosticsError => e logger.warn "Error in diagnostics: #{e.message}" + STDERR.puts "Error in diagnostics: #{e.message}" options['diagnostics'] = false send_notification 'window/showMessage', { type: LanguageServer::MessageTypes::ERROR, diff --git a/lib/solargraph/language_server/message/extended/document.rb b/lib/solargraph/language_server/message/extended/document.rb index f379d0a6b..ca9ec1159 100644 --- a/lib/solargraph/language_server/message/extended/document.rb +++ b/lib/solargraph/language_server/message/extended/document.rb @@ -16,6 +16,7 @@ def process Solargraph.logger.warn "Error processing document: [#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Solargraph.logger.debug e.backtrace.join("\n") + STDERR.puts "Error processing document: [#{e.class}] #{e.message}" end end end diff --git a/lib/solargraph/language_server/message/text_document/completion.rb b/lib/solargraph/language_server/message/text_document/completion.rb index d2f352a3d..ad6fee33b 100644 --- a/lib/solargraph/language_server/message/text_document/completion.rb +++ b/lib/solargraph/language_server/message/text_document/completion.rb @@ -38,6 +38,7 @@ def process end rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" + STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result empty_result diff --git a/lib/solargraph/language_server/message/text_document/hover.rb b/lib/solargraph/language_server/message/text_document/hover.rb index 6b60969e1..bd6e20a60 100644 --- a/lib/solargraph/language_server/message/text_document/hover.rb +++ b/lib/solargraph/language_server/message/text_document/hover.rb @@ -30,6 +30,7 @@ def process ) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" + STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result nil diff --git a/lib/solargraph/language_server/message/text_document/signature_help.rb b/lib/solargraph/language_server/message/text_document/signature_help.rb index 675daaebe..d4eb5a5f8 100644 --- a/lib/solargraph/language_server/message/text_document/signature_help.rb +++ b/lib/solargraph/language_server/message/text_document/signature_help.rb @@ -14,6 +14,7 @@ def process }) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" + STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result nil From c5d86e3d475809863e8983531d336aefd1ca248c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 12:24:38 -0700 Subject: [PATCH 165/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 34c50a3e3..83e0f8587 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -131,6 +131,8 @@ class Namespace::ExampleClass end obj = Namespace::ExampleClass.new ), 1) + # host.sync_library_map + raise "Not open yet" unless host.open? temp_file_url rename = described_class.new(host, { 'id' => 1, 'method' => 'textDocument/rename', From 25d01ef75d92fc6597fc8222dab02b0a3dbb1eda Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 12:33:09 -0700 Subject: [PATCH 166/206] Debug --- spec/language_server/message/text_document/rename_spec.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 83e0f8587..478716c17 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -1,12 +1,8 @@ # frozen_string_literal: true describe Solargraph::LanguageServer::Message::TextDocument::Rename do - before :context do - @temp_file_url = "file://#{Dir.mktmpdir}/file.rb" - end - let(:temp_file_url) do - @temp_file_url + "file://#{Dir.mktmpdir}/file.rb" end it 'renames a symbol' do From 73de5c54c75fff9d2de91604dd25996a1ac599ec Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 12:53:55 -0700 Subject: [PATCH 167/206] Drop workers experimentation --- .github/workflows/plugins.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index f7c83283a..1d83bed1c 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -84,14 +84,7 @@ jobs: # See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#caching-bundle-install-automatically bundle config path $PWD/vendor/bundle - # WORKERS=1 # 6m 31s - # WORKERS=2 - # WORKERS=3 WORKERS=$(nproc --all) - # WORKERS=5 - # WORKERS=6 - # WORKERS=7 - # WORKERS=8 # 4m 32s export WORKERS SIMPLECOV_DISABLED=true export SIMPLECOV_DISABLED From 25457a85b4e29527d7788a76896830302c09fbeb Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:13:24 -0700 Subject: [PATCH 168/206] Consistency fixes --- .github/workflows/plugins.yml | 3 --- .github/workflows/rspec.yml | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 1d83bed1c..1180f8732 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -52,7 +52,6 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.4 # keep same as typecheck.yml bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest with: @@ -126,8 +125,6 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.4 # keep same as typecheck.yml - # See https://github.com/castwide/solargraph/actions/runs/19000135777/job/54265647107?pr=1119 rubygems: latest bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index f7a316379..e76626e82 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -99,7 +99,7 @@ jobs: bundle config path $PWD/vendor/bundle WORKERS=$(nproc --all) export WORKERS - echo "Running tests with WORKERS=$WORKERS" + bundle exec rake full_spec undercover: runs-on: ubuntu-latest @@ -126,7 +126,7 @@ jobs: run: | WORKERS=$(nproc --all) export WORKERS - echo "Running tests with WORKERS=$WORKERS" + bundle exec rake full_spec - name: Check PR coverage run: | From 3e85551d905101858e1cc95966e9cfd612506f85 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:14:46 -0700 Subject: [PATCH 169/206] Drop @param --- lib/solargraph/complex_type/unique_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/complex_type/unique_type.rb b/lib/solargraph/complex_type/unique_type.rb index 43db544b0..f5f551c31 100644 --- a/lib/solargraph/complex_type/unique_type.rb +++ b/lib/solargraph/complex_type/unique_type.rb @@ -553,8 +553,8 @@ def transform new_name = nil, &transform_type # # @param api_map [ApiMap] The ApiMap that performs qualification # @param gates [Array] The namespaces from which to resolve names + # # @return [self, ComplexType, UniqueType] The generated ComplexType - # @param [Array] gates def qualify api_map, *gates transform do |t| next t if t.name == GENERIC_TAG_NAME From d10e029518e4aad052d68df6b16f52285903590a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:16:52 -0700 Subject: [PATCH 170/206] Docs --- lib/solargraph/diagnostics/base.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/solargraph/diagnostics/base.rb b/lib/solargraph/diagnostics/base.rb index 31b9a4342..cbc181e7c 100644 --- a/lib/solargraph/diagnostics/base.rb +++ b/lib/solargraph/diagnostics/base.rb @@ -21,6 +21,9 @@ def initialize *args # @param source [Solargraph::Source] # @param api_map [Solargraph::ApiMap] # @param workspace [Solargraph::Workspace, nil] + # Explicit workspace to use, instead of the current working + # directory's workspace. Useful in specs for isolation. + # # @return [Array] def diagnose source, api_map, workspace: nil [] From a4bd2bda1736220fcfffacee11fbbaecf6a17a2c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:18:50 -0700 Subject: [PATCH 171/206] Drop debugging --- lib/solargraph/language_server/host.rb | 2 -- lib/solargraph/language_server/message/extended/document.rb | 1 - .../language_server/message/text_document/completion.rb | 1 - lib/solargraph/language_server/message/text_document/hover.rb | 1 - lib/solargraph/language_server/message/text_document/rename.rb | 3 --- .../language_server/message/text_document/signature_help.rb | 1 - lib/solargraph/library.rb | 1 - 7 files changed, 10 deletions(-) diff --git a/lib/solargraph/language_server/host.rb b/lib/solargraph/language_server/host.rb index 2af432bef..d61283567 100644 --- a/lib/solargraph/language_server/host.rb +++ b/lib/solargraph/language_server/host.rb @@ -215,7 +215,6 @@ def diagnose uri } rescue DiagnosticsError => e logger.warn "Error in diagnostics: #{e.message}" - STDERR.puts "Error in diagnostics: #{e.message}" options['diagnostics'] = false send_notification 'window/showMessage', { type: LanguageServer::MessageTypes::ERROR, @@ -591,7 +590,6 @@ def references_from uri, line, column, strip: true, only: false rescue FileNotFoundError, InvalidOffsetError => e Solargraph.logger.warn "[#{e.class}] #{e.message}" Solargraph.logger.debug e.backtrace - STDERR.puts "[#{e.class}] #{e.message}" [] end diff --git a/lib/solargraph/language_server/message/extended/document.rb b/lib/solargraph/language_server/message/extended/document.rb index ca9ec1159..f379d0a6b 100644 --- a/lib/solargraph/language_server/message/extended/document.rb +++ b/lib/solargraph/language_server/message/extended/document.rb @@ -16,7 +16,6 @@ def process Solargraph.logger.warn "Error processing document: [#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Solargraph.logger.debug e.backtrace.join("\n") - STDERR.puts "Error processing document: [#{e.class}] #{e.message}" end end end diff --git a/lib/solargraph/language_server/message/text_document/completion.rb b/lib/solargraph/language_server/message/text_document/completion.rb index ad6fee33b..d2f352a3d 100644 --- a/lib/solargraph/language_server/message/text_document/completion.rb +++ b/lib/solargraph/language_server/message/text_document/completion.rb @@ -38,7 +38,6 @@ def process end rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" - STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result empty_result diff --git a/lib/solargraph/language_server/message/text_document/hover.rb b/lib/solargraph/language_server/message/text_document/hover.rb index bd6e20a60..6b60969e1 100644 --- a/lib/solargraph/language_server/message/text_document/hover.rb +++ b/lib/solargraph/language_server/message/text_document/hover.rb @@ -30,7 +30,6 @@ def process ) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" - STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result nil diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index 4ee2525a4..226ddc11b 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -8,11 +8,8 @@ class Rename < Base def process locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true) - STDERR.puts "Rename: #{locs.length} references in #{params['textDocument']['uri']} at line #{params['position']['line']} char #{params['position']['character']}. " if locs.empty? library = host.library_for(params['textDocument']['uri']) - STDERR.puts("Total pins length: #{library.pins.length}") - end changes = {} locs.each do |loc| diff --git a/lib/solargraph/language_server/message/text_document/signature_help.rb b/lib/solargraph/language_server/message/text_document/signature_help.rb index d4eb5a5f8..675daaebe 100644 --- a/lib/solargraph/language_server/message/text_document/signature_help.rb +++ b/lib/solargraph/language_server/message/text_document/signature_help.rb @@ -14,7 +14,6 @@ def process }) rescue FileNotFoundError => e Logging.logger.warn "[#{e.class}] #{e.message}" - STDERR.puts "[#{e.class}] #{e.message}" # @sg-ignore Need to add nil check here Logging.logger.warn e.backtrace.join("\n") set_result nil diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 0df205fc5..3272093c1 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -587,7 +587,6 @@ def read filename def handle_file_not_found filename, error raise error unless workspace.source(filename) Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap" - STDERR.puts "#{filename} is not cataloged in the ApiMap" nil end From 16fb502c64c3859eb7fd0f74871ef13af820c8e9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:49:17 -0700 Subject: [PATCH 172/206] Drop debugging --- lib/solargraph/language_server/message/text_document/rename.rb | 3 --- spec/language_server/message/text_document/rename_spec.rb | 2 -- 2 files changed, 5 deletions(-) diff --git a/lib/solargraph/language_server/message/text_document/rename.rb b/lib/solargraph/language_server/message/text_document/rename.rb index 226ddc11b..b0aff3c8a 100644 --- a/lib/solargraph/language_server/message/text_document/rename.rb +++ b/lib/solargraph/language_server/message/text_document/rename.rb @@ -8,9 +8,6 @@ class Rename < Base def process locs = host.references_from(params['textDocument']['uri'], params['position']['line'], params['position']['character'], strip: true) - if locs.empty? - library = host.library_for(params['textDocument']['uri']) - end changes = {} locs.each do |loc| uri = file_to_uri(loc.filename) diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 478716c17..2abcb821a 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -127,8 +127,6 @@ class Namespace::ExampleClass end obj = Namespace::ExampleClass.new ), 1) - # host.sync_library_map - raise "Not open yet" unless host.open? temp_file_url rename = described_class.new(host, { 'id' => 1, 'method' => 'textDocument/rename', From d707e0436386c04299299eef5b24354a59882417 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:54:16 -0700 Subject: [PATCH 173/206] Revert debugging --- lib/solargraph/api_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index bfc153680..66f68533f 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -614,7 +614,7 @@ def locate_pins location # @return [SourceMap::Clip] def clip cursor # @sg-ignore Need to add nil check here - raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename} - current source map keys are #{source_map_hash.keys}" unless source_map_hash.key?(cursor.filename) + raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename) SourceMap::Clip.new(self, cursor) end From b8b18c4d8928d164bf7bdc74097117afff4925d5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:56:53 -0700 Subject: [PATCH 174/206] Drop unneeded Config.new --- lib/solargraph/workspace/require_paths.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index 1b88ff054..d12364b07 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -16,7 +16,7 @@ class RequirePaths # @param config [Config, nil] def initialize directory, config @directory = directory - @config = config || Config.new(directory) + @config = config end # Generate require paths from gemspecs if they exist or assume the default From aa8979c2b45991823d2d906e3ff318e85cbf83d9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 13:59:11 -0700 Subject: [PATCH 175/206] Revert updates --- .github/workflows/plugins.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 1180f8732..8e19b91e6 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -52,6 +52,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: 3.4 bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest with: @@ -125,6 +126,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: 3.4 rubygems: latest bundler-cache: true - uses: awalsh128/cache-apt-pkgs-action@latest @@ -225,6 +227,7 @@ jobs: bundle config path $PWD/vendor/bundle WORKERS=$(nproc --all) export WORKERS + bundle exec rake full_spec run_solargraph_rspec_specs: # check out solargraph-rspec as well as this project, and point the former to use the latter as a local gem @@ -296,6 +299,7 @@ jobs: cd ../solargraph-rspec WORKERS=$(nproc --all) export WORKERS + # avoid trying to do this in parallel during the specs bundle exec solargraph gems core stdlib bundle exec appraisal rspec --format progress From fefdeba698895d34e763eadd4dd00664dec069d3 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 14:01:35 -0700 Subject: [PATCH 176/206] Fix annotation --- lib/solargraph/complex_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/complex_type.rb b/lib/solargraph/complex_type.rb index 440edd792..17a2815c8 100644 --- a/lib/solargraph/complex_type.rb +++ b/lib/solargraph/complex_type.rb @@ -33,7 +33,7 @@ def initialize types = [UniqueType::UNDEFINED] # @param gates [Array] # # @return [ComplexType] - # @param [Array] gates + # @param [Array] gates def qualify api_map, *gates red = reduce_object types = red.items.map do |t| From 792e8579c278b145261086fbe6b44ab695713af5 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 15:57:50 -0700 Subject: [PATCH 177/206] Revert --- lib/solargraph/library.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/library.rb b/lib/solargraph/library.rb index 3272093c1..8cf806275 100644 --- a/lib/solargraph/library.rb +++ b/lib/solargraph/library.rb @@ -629,6 +629,7 @@ def cache_next_gemspec catalog sync_catalog else + logger.info "Caching #{spec.name} #{spec.version}" Thread.new do report_cache_progress spec.name, pending kwargs = {} From 23ccc341d81e072c206a5464ccb45103fca61e17 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 16:08:52 -0700 Subject: [PATCH 178/206] Fix log message --- lib/solargraph/rbs_map/stdlib_map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index 9c92fcefa..d80d3b2a9 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -32,8 +32,8 @@ def initialize library, rebuild: false, out: $stderr end generated_pins = pins logger.debug { "Found #{generated_pins.length} pins for stdlib library #{library}" } + out&.puts "Caching RBS gem standard library pins for #{library}" PinCache.serialize_stdlib_require library, generated_pins - out&.puts "Cached stdlib RBS for #{library}" end end From 6e06cd23b5c67f2a6207be3c38a41dfee59d2775 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 17:04:27 -0700 Subject: [PATCH 179/206] Cache workspace gems in parallel --- lib/solargraph/workspace.rb | 19 ++++++++++++++++--- solargraph.gemspec | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 914374a87..81ed481bf 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'concurrent-ruby' require 'open3' require 'json' require 'yaml' @@ -252,10 +253,22 @@ def cache_all_for_workspace! out, rebuild: false # try any possible standard libraries, but be quiet about it stdlib_specs = pin_cache.possible_stdlibs.map { |stdlib| find_gem(stdlib, out: nil) }.compact specs = (gem_specs + stdlib_specs) - specs.each do |spec| - pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec) + + pool_size = Concurrent.processor_count # roughly your CPU count + pool = Concurrent::FixedThreadPool.new(pool_size) + + # Using 'names' as queue, run! + futures = specs.map do |spec| + Concurrent::Promises.future_on(pool, spec) do + pin_cache.cache_gem(gemspec: spec, rebuild: rebuild, out: out) unless pin_cache.cached?(spec) + end end - out&.puts "Documentation cached for all #{specs.length} gems." + + Concurrent::Promises.zip(*futures).value! + pool.shutdown + pool.wait_for_termination + + out&.puts "Documentation cached for all #{specs.length} gems using #{pool_size} threads." # do this after so that we prefer stdlib requires from gems, # which are likely to be newer and have more pins diff --git a/solargraph.gemspec b/solargraph.gemspec index 7908e132a..e1064522f 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -34,6 +34,7 @@ Gem::Specification.new do |s| s.add_dependency 'backport', '~> 1.2' s.add_dependency 'benchmark', '~> 0.4' s.add_dependency 'bundler', '>= 2.0' + s.add_dependency 'concurrent-ruby', '~> 1.3', '>= 1.3.5' s.add_dependency 'diff-lcs', '~> 1.4' s.add_dependency 'jaro_winkler', '~> 1.6', '>= 1.6.1' s.add_dependency 'kramdown', '~> 2.3' From a1aeac2a12eec8fc3e3f2c99cdf3ca06eaa8a39a Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 17:13:46 -0700 Subject: [PATCH 180/206] Cache workspace gems in parallel --- lib/solargraph/shell.rb | 97 +++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index fdc925e22..0c6184be6 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'benchmark' +require 'concurrent-ruby' require 'thor' require 'yard' require 'yaml' @@ -183,46 +184,31 @@ def gems *names if names.empty? workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) else - warn("Caching these gems: #{names}") - names.each do |name| - if name == 'core' - PinCache.cache_core(out: $stdout) if !PinCache.core? || options[:rebuild] - next - end - - if name == 'stdlib' - workspace.cache_all_stdlibs(out: $stdout, rebuild: options[:rebuild]) - next - end - - if name == 'default' - doc_map = Solargraph::DocMap.new([], workspace) - doc_map.cache_doc_map_gems! $stdout - next - end - - if name == 'bundler/require' - gemspecs = workspace.resolve_require(name) - gemspecs&.each do |gs| - workspace.cache_gem(gs, rebuild: options[:rebuild], out: $stdout) - end - next - end - - gemspec = workspace.find_gem(*name.split('=')) - if gemspec.nil? + # run in parallel with a thread pool + + # create thread pool + pool_size = Concurrent.processor_count # roughly your CPU count + pool = Concurrent::FixedThreadPool.new(pool_size) + warn("Caching these gems with #{pool_size} workers: #{names}") + + # Using 'names' as queue, run! + futures = names.map do |name| + Concurrent::Promises.future_on(pool, name) do |_x| + cache_library(workspace, name) + rescue Gem::MissingSpecError warn "Gem '#{name}' not found" - else - workspace.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) + rescue Gem::Requirement::BadRequirementError => e + warn "Gem '#{name}' failed while loading" + warn e.message + # @sg-ignore Need to add nil check here + warn e.backtrace.join("\n") end - rescue Gem::MissingSpecError - warn "Gem '#{name}' not found" - rescue Gem::Requirement::BadRequirementError => e - warn "Gem '#{name}' failed while loading" - warn e.message - # @sg-ignore Need to add nil check here - warn e.backtrace.join("\n") end + + Concurrent::Promises.zip(*futures).value! # raises if any failed + pool.shutdown + pool.wait_for_termination + warn "Documentation cached for #{names.count} gems." end end @@ -495,6 +481,43 @@ def host.send_notification method, params private + # @param name [String] + # @param [Workspace] workspace + # + # @return [void] + def cache_library workspace, name + if name == 'core' + PinCache.cache_core(out: $stdout) if !PinCache.core? || options[:rebuild] + return + end + + if name == 'stdlib' + workspace.cache_all_stdlibs(out: $stdout, rebuild: options[:rebuild]) + return + end + + if name == 'default' + doc_map = Solargraph::DocMap.new([], workspace) + doc_map.cache_doc_map_gems! $stdout + return + end + + if name == 'bundler/require' + gemspecs = workspace.resolve_require(name) + gemspecs&.each do |gs| + workspace.cache_gem(gs, rebuild: options[:rebuild], out: $stdout) + end + return + end + + gemspec = workspace.find_gem(*name.split('=')) + if gemspec.nil? + warn "Gem '#{name}' not found" + else + workspace.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout) + end + end + # @param pin [Solargraph::Pin::Base] # @return [String] def pin_description pin From 81b3ae0a93ddecc861d74192bfde2868fc102f38 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 17:26:57 -0700 Subject: [PATCH 181/206] Cache docmap gems in parallel --- lib/solargraph/doc_map.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 61589e016..41c6ffef9 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -75,14 +75,23 @@ def cache_doc_map_gems! out, rebuild: false "Caching pins for gems: #{gem_desc}" end end + pool_size = Concurrent.processor_count # roughly your CPU count + pool = Concurrent::FixedThreadPool.new(pool_size) time = Benchmark.measure do - uncached_gemspecs.each do |gemspec| - cache(gemspec, rebuild: rebuild, out: out) + # Using 'names' as queue, run! + futures = uncached_gemspecs.map do |spec| + Concurrent::Promises.future_on(pool, spec) do + cache(spec, rebuild: rebuild, out: out) + end end + + Concurrent::Promises.zip(*futures).value! + pool.shutdown + pool.wait_for_termination end milliseconds = (time.real * 1000).round if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any? - out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms" + out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms in #{pool_size} threads" end reset_pins! end From 91b8852a56514f49b965d4510823bf0d0c333955 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 17:58:44 -0700 Subject: [PATCH 182/206] Add --workspace flag, default to docmap caching --- lib/solargraph/api_map.rb | 3 ++- lib/solargraph/doc_map.rb | 3 +++ lib/solargraph/shell.rb | 17 ++++++++++++----- lib/solargraph/workspace.rb | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 66f68533f..3472437f1 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -219,10 +219,11 @@ class << self # # @param directory [String] # @param out [IO, StringIO, nil] The output stream for messages + # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @param loose_unions [Boolean] See #initialize # # @return [ApiMap] - def self.load_with_cache directory, out = $stderr, loose_unions: true + def self.load_with_cache directory, out: $stderr, rebuild: false, loose_unions: true api_map = load(directory, loose_unions: loose_unions) if api_map.uncached_gemspecs.empty? logger.info { "All gems cached for #{directory}" } diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 41c6ffef9..1c3d06baf 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -3,6 +3,7 @@ require 'pathname' require 'benchmark' require 'open3' +require 'concurrent-ruby' module Solargraph # A collection of pins generated from specific 'require' statements @@ -69,6 +70,8 @@ def any_uncached? # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] def cache_doc_map_gems! out, rebuild: false + out&.puts 'Caching gems used by project' + PinCache.cache_core(out: out) unless PinCache.core? && !rebuild unless uncached_gemspecs.empty? logger.info do gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ') diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 0c6184be6..9295bf5da 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -174,15 +174,21 @@ def uncache *gems Cached documentation is stored in #{PinCache.base_dir}, which can be stored between CI runs. ) + option :workspace, type: :boolean, desc: 'Rebuild all accessible gems, not just those used', default: false option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false # @param names [Array] # @return [void] def gems *names # print time with ms - workspace = Solargraph::Workspace.new('.') + api_map = Solargraph::ApiMap.load('.') + workspace = api_map.workspace if names.empty? - workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) + if options[:workspace] + workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild]) + else + api_map.cache_all_for_doc_map!(out: $stdout) + end else # run in parallel with a thread pool @@ -235,7 +241,8 @@ def typecheck *files level = options[:level].to_sym rules = workspace.rules(level) api_map = - Solargraph::ApiMap.load_with_cache(directory, $stdout, + Solargraph::ApiMap.load_with_cache(directory, + out: $stdout, loose_unions: !rules.require_all_unique_types_support_call?) probcount = 0 @@ -284,7 +291,7 @@ def scan # @type [Solargraph::ApiMap, nil] api_map = nil time = Benchmark.measure do - api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) + api_map = Solargraph::ApiMap.load_with_cache(directory, out: $stdout) # @sg-ignore flow sensitive typing should be able to handle redefinition api_map.pins.each do |pin| puts pin_description(pin) if options[:verbose] @@ -329,7 +336,7 @@ def list # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method' # @return [void] def pin path - api_map = Solargraph::ApiMap.load_with_cache('.', $stderr) + api_map = Solargraph::ApiMap.load_with_cache('.', out: $stderr) is_method = path.include?('#') || path.include?('.') if is_method && options[:stack] scope, ns, meth = if path.include? '#' diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index 81ed481bf..bf0690bdb 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -247,6 +247,7 @@ def all_gemspecs_from_bundle # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] def cache_all_for_workspace! out, rebuild: false + out.puts 'Caching all gems available' PinCache.cache_core(out: out) unless PinCache.core? && !rebuild gem_specs = all_gemspecs_from_bundle From 013deafe88f097946f79689952eb117b065217be Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 18:04:10 -0700 Subject: [PATCH 183/206] Fix API break --- lib/solargraph/api_map.rb | 3 ++- lib/solargraph/shell.rb | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index 3472437f1..7d0e02b23 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -223,7 +223,8 @@ class << self # @param loose_unions [Boolean] See #initialize # # @return [ApiMap] - def self.load_with_cache directory, out: $stderr, rebuild: false, loose_unions: true + # @api Used by solargraph-rails at least + def self.load_with_cache directory, out = $stderr, rebuild: false, loose_unions: true api_map = load(directory, loose_unions: loose_unions) if api_map.uncached_gemspecs.empty? logger.info { "All gems cached for #{directory}" } diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index 9295bf5da..abcf859a8 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -241,8 +241,7 @@ def typecheck *files level = options[:level].to_sym rules = workspace.rules(level) api_map = - Solargraph::ApiMap.load_with_cache(directory, - out: $stdout, + Solargraph::ApiMap.load_with_cache(directory, $stdout, loose_unions: !rules.require_all_unique_types_support_call?) probcount = 0 @@ -291,7 +290,7 @@ def scan # @type [Solargraph::ApiMap, nil] api_map = nil time = Benchmark.measure do - api_map = Solargraph::ApiMap.load_with_cache(directory, out: $stdout) + api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout) # @sg-ignore flow sensitive typing should be able to handle redefinition api_map.pins.each do |pin| puts pin_description(pin) if options[:verbose] @@ -336,7 +335,7 @@ def list # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method' # @return [void] def pin path - api_map = Solargraph::ApiMap.load_with_cache('.', out: $stderr) + api_map = Solargraph::ApiMap.load_with_cache('.', $stderr) is_method = path.include?('#') || path.include?('.') if is_method && options[:stack] scope, ns, meth = if path.include? '#' From 98953f7d9bb518862bfa89bdef5e07fe76cb74a0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 18:05:21 -0700 Subject: [PATCH 184/206] Fix nil issue --- lib/solargraph/workspace.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/workspace.rb b/lib/solargraph/workspace.rb index bf0690bdb..2cc57b1b0 100644 --- a/lib/solargraph/workspace.rb +++ b/lib/solargraph/workspace.rb @@ -247,7 +247,7 @@ def all_gemspecs_from_bundle # @param rebuild [Boolean] whether to rebuild the pins even if they are cached # @return [void] def cache_all_for_workspace! out, rebuild: false - out.puts 'Caching all gems available' + out&.puts 'Caching all gems available' PinCache.cache_core(out: out) unless PinCache.core? && !rebuild gem_specs = all_gemspecs_from_bundle From e0b28e0fe185b92ce6a639de545d650338d393ca Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 18:16:24 -0700 Subject: [PATCH 185/206] Fix spec --- spec/shell_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index 875fb9d9d..b2ca80711 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -120,18 +120,20 @@ context 'with mocked Workspace' do let(:workspace) { instance_double(Solargraph::Workspace) } + let(:api_map) { instance_double(Solargraph::ApiMap) } let(:gemspec) { instance_double(Gem::Specification, name: 'backport') } before do - allow(Solargraph::Workspace).to receive(:new).and_return(workspace) + allow(Solargraph::ApiMap).to receive(:load).and_return(api_map) + allow(api_map).to receive(:workspace).and_return(workspace) end it 'caches all without erroring out' do - allow(workspace).to receive(:cache_all_for_workspace!) + allow(api_map).to receive(:cache_all_for_doc_map!) _output = capture_both { shell.gems } - expect(workspace).to have_received(:cache_all_for_workspace!) + expect(api_map).to have_received(:cache_all_for_doc_map!) end it 'caches single gem without erroring out' do From c6d7ea981333bc40e9ad2d37dd902f5057af37b0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 18:25:33 -0700 Subject: [PATCH 186/206] Cache more up front in rspec_specs --- .github/workflows/plugins.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 8e19b91e6..a76941ee8 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -213,8 +213,10 @@ jobs: - name: Install gem types run: | bundle exec rbs collection update + + rspec_gems=$(bundle exec ruby -r 'solargraph-rspec' -e 'puts Solargraph::Rspec::Gems.gem_names.join(" ")' 2>/dev/null | tail -n1) # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib + bundle exec solargraph gems core stdlib $rspec_gems - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests From 1f599e9cb3481a8d0aa805cb9b8a070e073cda94 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 18:48:29 -0700 Subject: [PATCH 187/206] Drop unneeded nil case --- lib/solargraph/workspace/require_paths.rb | 2 +- spec/workspace/require_paths_spec.rb | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/solargraph/workspace/require_paths.rb b/lib/solargraph/workspace/require_paths.rb index d12364b07..23057a1af 100644 --- a/lib/solargraph/workspace/require_paths.rb +++ b/lib/solargraph/workspace/require_paths.rb @@ -13,7 +13,7 @@ class RequirePaths attr_reader :directory, :config # @param directory [String, nil] - # @param config [Config, nil] + # @param config [Config] def initialize directory, config @directory = directory @config = config diff --git a/spec/workspace/require_paths_spec.rb b/spec/workspace/require_paths_spec.rb index 4a5ef98b5..b89979022 100644 --- a/spec/workspace/require_paths_spec.rb +++ b/spec/workspace/require_paths_spec.rb @@ -11,15 +11,6 @@ let(:config) { Solargraph::Workspace::Config.new(dir_path) } - context 'with no config' do - let(:dir_path) { Dir.pwd } - let(:config) { nil } - - it 'includes the lib directory' do - expect(paths).to include(File.join(dir_path, 'lib')) - end - end - context 'with config and no gemspec' do let(:dir_path) { File.realpath(Dir.pwd) } From 17d3a2bf65a96e224b55be850f7f3f6569db6996 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Fri, 6 Feb 2026 19:00:33 -0700 Subject: [PATCH 188/206] Avoid chdir issues --- .../message/text_document/type_definition_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/language_server/message/text_document/type_definition_spec.rb b/spec/language_server/message/text_document/type_definition_spec.rb index 88eb02bf5..52daf5ecd 100644 --- a/spec/language_server/message/text_document/type_definition_spec.rb +++ b/spec/language_server/message/text_document/type_definition_spec.rb @@ -1,6 +1,13 @@ # frozen_string_literal: true describe Solargraph::LanguageServer::Message::TextDocument::TypeDefinition do + around do |testobj| + # we need a consistent directory + Solargraph::CHDIR_MUTEX.synchronize do + testobj.run + end + end + it 'finds definitions of methods' do host = Solargraph::LanguageServer::Host.new host.prepare('spec/fixtures/workspace') From 093bd2a98d1daec159e5b13fd312792213e21754 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:07:38 -0700 Subject: [PATCH 189/206] Cache more gems --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index a76941ee8..44248a914 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -216,7 +216,7 @@ jobs: rspec_gems=$(bundle exec ruby -r 'solargraph-rspec' -e 'puts Solargraph::Rspec::Gems.gem_names.join(" ")' 2>/dev/null | tail -n1) # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib $rspec_gems + bundle exec solargraph gems core stdlib $rspec_gems diff-lcs addressable ast rexml crack hashdiff - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests From 48b45400a3bd56f0a1b84cc5408f030fa04ff3fa Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:09:22 -0700 Subject: [PATCH 190/206] Cache more gems --- .github/workflows/plugins.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 44248a914..727c41361 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -212,6 +212,8 @@ jobs: yq -yi '.plugins += ["solargraph-rspec"]' .solargraph.yml - name: Install gem types run: | + set -x + bundle exec rbs collection update rspec_gems=$(bundle exec ruby -r 'solargraph-rspec' -e 'puts Solargraph::Rspec::Gems.gem_names.join(" ")' 2>/dev/null | tail -n1) From b1bdc91a8b2861586ebfe674d9a3ceeb6b22b710 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:25:14 -0700 Subject: [PATCH 191/206] Add speedups --- .github/workflows/plugins.yml | 2 +- spec/yardoc_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 727c41361..86124c0bb 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -218,7 +218,7 @@ jobs: rspec_gems=$(bundle exec ruby -r 'solargraph-rspec' -e 'puts Solargraph::Rspec::Gems.gem_names.join(" ")' 2>/dev/null | tail -n1) # avoid trying to do this in parallel during the specs - bundle exec solargraph gems core stdlib $rspec_gems diff-lcs addressable ast rexml crack hashdiff + bundle exec solargraph gems core stdlib $rspec_gems diff-lcs addressable ast rexml crack hashdiff rspec-support bigdecimal public_suffix - name: Ensure specs still run run: | # Speed up some of the bundle installs we run inside the tests diff --git a/spec/yardoc_spec.rb b/spec/yardoc_spec.rb index 80e8d4764..6ce0877ef 100644 --- a/spec/yardoc_spec.rb +++ b/spec/yardoc_spec.rb @@ -39,7 +39,7 @@ describe '#build_docs' do let(:workspace) { Solargraph::Workspace.new(Dir.pwd) } - let(:gemspec) { workspace.find_gem('rubocop') } + let(:gemspec) { workspace.find_gem('backport') } let(:output) { '' } before do From a4341d153472ddb13b31a03a7b71a850ee8af567 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:44:21 -0700 Subject: [PATCH 192/206] Speed up gem caching --- lib/solargraph/shell.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index abcf859a8..e08bdd069 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -180,7 +180,7 @@ def uncache *gems # @return [void] def gems *names # print time with ms - api_map = Solargraph::ApiMap.load('.') + api_map = Solargraph::ApiMap.new workspace = api_map.workspace if names.empty? From 5283a52b588245779f218b1c1cc8061e5c33ef1d Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:48:06 -0700 Subject: [PATCH 193/206] Spec perf fixes --- spec/pin/method_spec.rb | 2 +- spec/pin_cache_spec.rb | 2 ++ spec/rbs_map/conversions_spec.rb | 4 ++-- spec/yard_map/mapper_spec.rb | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/pin/method_spec.rb b/spec/pin/method_spec.rb index 32cdcf45c..e0a9fc791 100644 --- a/spec/pin/method_spec.rb +++ b/spec/pin/method_spec.rb @@ -556,7 +556,7 @@ class Foo # on type. Let's make sure we combine those with anything else # found (e.g., additions from the BigDecimal RBS collection) # without collapsing signatures - api_map = Solargraph::ApiMap.load(Dir.pwd) + api_map = Solargraph::ApiMap.new bench = Solargraph::Bench.new external_requires: ['bigdecimal'] api_map.catalog(bench) method = api_map.get_method_stack('Integer', '+', scope: :instance).first diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 06a549cc5..22b9f7747 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -98,6 +98,8 @@ end it 'uncaches when asked' do + allow(FileUtils).to receive(:rm_rf) + gemspec = Gem::Specification.find_by_name('kramdown') expect do pin_cache.uncache_gem(gemspec, out: $stderr) diff --git a/spec/rbs_map/conversions_spec.rb b/spec/rbs_map/conversions_spec.rb index 9f3e34d75..e1e34e106 100644 --- a/spec/rbs_map/conversions_spec.rb +++ b/spec/rbs_map/conversions_spec.rb @@ -98,7 +98,7 @@ def bar: () -> untyped context 'with superclass pin for Parser::AST::Node' do # Use :context here instead of :all so that parallel_rspec runs these on the same worker and we only have to cache these gems on one worker before :context do - @api_map = Solargraph::ApiMap.load('.') + @api_map = Solargraph::ApiMap.new gems = %w[parser ast open3] bench = Solargraph::Bench.new(workspace: @api_map.workspace, external_requires: gems) @api_map.catalog(bench) @@ -155,7 +155,7 @@ class Sub < Hash[Symbol, untyped] if Gem::Version.new(RBS::VERSION) >= Gem::Version.new('3.9.1') context 'with method pin for Open3.capture2e' do it 'accepts chdir kwarg' do - api_map = Solargraph::ApiMap.load('.') + api_map = Solargraph::ApiMap.new bench = Solargraph::Bench.new(external_requires: ['open3']) api_map.catalog(bench) diff --git a/spec/yard_map/mapper_spec.rb b/spec/yard_map/mapper_spec.rb index 43b5da7cf..5f317bd8b 100644 --- a/spec/yard_map/mapper_spec.rb +++ b/spec/yard_map/mapper_spec.rb @@ -5,7 +5,7 @@ # would be needed regardless as we are changing the working # directory before :context do - @api_map = Solargraph::ApiMap.load('.') + @api_map = Solargraph::ApiMap.new end def pins_with require From cb06aea788b697856cc5cd0210c1a88453549db0 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 10:57:02 -0700 Subject: [PATCH 194/206] Fix expectations --- spec/shell_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/shell_spec.rb b/spec/shell_spec.rb index b2ca80711..a76a7fc89 100644 --- a/spec/shell_spec.rb +++ b/spec/shell_spec.rb @@ -124,7 +124,7 @@ let(:gemspec) { instance_double(Gem::Specification, name: 'backport') } before do - allow(Solargraph::ApiMap).to receive(:load).and_return(api_map) + allow(Solargraph::ApiMap).to receive(:new).and_return(api_map) allow(api_map).to receive(:workspace).and_return(workspace) end From a5d028d16bc2496b886f96a612a8f2d666fbf289 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 11:24:40 -0700 Subject: [PATCH 195/206] Speed up --- spec/pin/base_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index 90d9a8850..c1df4d6c1 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -53,7 +53,7 @@ it 'deals well with known closure combination issue' do # if this fails you might not have an rbs collection installed - api_map = Solargraph::ApiMap.load '' + api_map = Solargraph::ApiMap.new spec = Gem::Specification.find_by_name('yard') api_map.cache_gem(spec) From 5cd2d83603b4a0fa825cbeacac31daa37db4dfaf Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 11:27:41 -0700 Subject: [PATCH 196/206] Try empty dir load --- spec/pin/base_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pin/base_spec.rb b/spec/pin/base_spec.rb index c1df4d6c1..90d9a8850 100644 --- a/spec/pin/base_spec.rb +++ b/spec/pin/base_spec.rb @@ -53,7 +53,7 @@ it 'deals well with known closure combination issue' do # if this fails you might not have an rbs collection installed - api_map = Solargraph::ApiMap.new + api_map = Solargraph::ApiMap.load '' spec = Gem::Specification.find_by_name('yard') api_map.cache_gem(spec) From 07acb5ce45831fdecf6fa001e84a970a70c318e9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 12:18:04 -0700 Subject: [PATCH 197/206] Precache less in spec --- spec/doc_map_spec.rb | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 56c49f68f..6d46194c7 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -9,7 +9,7 @@ end let(:out) { StringIO.new } - let(:pre_cache) { true } + let(:pre_cache) { false } let(:requires) { [] } let(:workspace) do @@ -23,6 +23,8 @@ end context 'with a require in solargraph test bundle' do + let(:pre_cache) { true } + let(:requires) do ['ast'] end @@ -67,12 +69,16 @@ end end - it 'does not warn for redundant requires' do - # Requiring 'set' is unnecessary because it's already included in core. It - # might make sense to log redundant requires, but a warning is overkill. - allow(Solargraph.logger).to receive(:warn).and_call_original - described_class.new(['set'], workspace) - expect(Solargraph.logger).not_to have_received(:warn).with(/path set/) + context 'with a redundant require' do + let(:pre_cache) { false } + + it 'does not warn' do + # Requiring 'set' is unnecessary because it's already included in core. It + # might make sense to log redundant requires, but a warning is overkill. + allow(Solargraph.logger).to receive(:warn).and_call_original + described_class.new(['set'], workspace) + expect(Solargraph.logger).not_to have_received(:warn).with(/path set/) + end end context 'when deserialization takes a while' do @@ -114,6 +120,8 @@ end context 'with require as bundle/require' do + let(:pre_cache) { false } + it 'imports all gems when bundler/require used' do doc_map_with_bundler_require = described_class.new(['bundler/require'], workspace, out: $stderr) if doc_map_with_bundler_require.pins.length <= plain_doc_map.pins.length From 1e3f1a4ae98c4e2702e3822f60bf18ab0875fd1c Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 12:20:46 -0700 Subject: [PATCH 198/206] Fix precache --- spec/doc_map_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 6d46194c7..45f65d743 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -36,6 +36,8 @@ end context 'when understanding rspec + rspec-mocks require pattern' do + let(:pre_cache) { true } + let(:requires) do ['rspec-mocks'] end From 30812e73ec1b875e21883668bbf6f5ac019c5243 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 16:05:25 -0700 Subject: [PATCH 199/206] Speed up specs --- spec/api_map_method_spec.rb | 18 ++++++++++++------ spec/type_checker/levels/strict_spec.rb | 12 ++++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 2f1dc7c8d..276cab647 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -4,12 +4,13 @@ let(:api_map) { described_class.new } let(:bench) do Solargraph::Bench.new(external_requires: external_requires, - workspace: Solargraph::Workspace.new(PROJECT_DIRECTORY)) + workspace: Solargraph::Workspace.new) end let(:external_requires) { [] } + let(:catalog) { false } before do - api_map.catalog bench + api_map.catalog bench if catalog end describe '#resolve_method_alias' do @@ -124,10 +125,12 @@ class B let(:method_stack) { api_map.get_method_stack('YAML', 'safe_load', scope: :class) } it 'handles the YAML gem aliased to Psych' do - specs = (api_map.resolve_require('yaml') || []) + (api_map.resolve_require('psych') || []) - expect(specs).not_to be_empty - specs.each { |spec| api_map.cache_gem(spec) } - api_map.catalog bench + if method_stack.nil? + specs = (api_map.resolve_require('yaml') || []) + (api_map.resolve_require('psych') || []) + expect(specs).not_to be_empty + specs.each { |spec| api_map.cache_gem(spec) } + api_map.catalog bench + end expect(method_stack).not_to be_nil end @@ -135,10 +138,12 @@ class B context 'with thor' do let(:external_requires) { ['thor'] } + let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } it 'handles finding Thor.desc' do specs = api_map.resolve_require('thor') + specs.each { |spec| api_map.cache_gem(spec) } api_map.catalog bench @@ -168,6 +173,7 @@ class B describe '#uncached_gemspecs' do it 'can get uncached gemspecs workspace without a bench' do api_map = described_class.new + expect(api_map.uncached_gemspecs).not_to be_nil end end diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 2bb54f2b9..2ccaa5cb3 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -105,8 +105,16 @@ def bar(a); end require 'kramdown-parser-gfm' Kramdown::Parser::GFM.undefined_call ), 'test.rb') - api_map = Solargraph::ApiMap.load '.' - api_map.catalog Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) + + api_map = Solargraph::ApiMap.new + workspace = Solargraph::Workspace.new('.') + library = Solargraph::Library.new(workspace) + library.map! + api_map.catalog library.bench + bench = Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) + api_map.catalog bench + api_map.cache_all_for_doc_map!(out: STDERR) # rubocop:disable Style/GlobalStdStream + api_map.catalog bench checker = described_class.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty end From 7246007621cfd3ec2d45e16012a48657abe3c543 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 16:19:37 -0700 Subject: [PATCH 200/206] ? --- spec/api_map_method_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api_map_method_spec.rb b/spec/api_map_method_spec.rb index 276cab647..99f48cfcf 100644 --- a/spec/api_map_method_spec.rb +++ b/spec/api_map_method_spec.rb @@ -140,10 +140,10 @@ class B let(:external_requires) { ['thor'] } let(:method_stack) { api_map.get_method_stack('Thor', 'desc', scope: :class) } + let(:catalog) { true } it 'handles finding Thor.desc' do specs = api_map.resolve_require('thor') - specs.each { |spec| api_map.cache_gem(spec) } api_map.catalog bench From 940fc3f5ef93d6826307b5f1897534eba3704e07 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 16:23:57 -0700 Subject: [PATCH 201/206] Simplify spec --- spec/type_checker/levels/strict_spec.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/spec/type_checker/levels/strict_spec.rb b/spec/type_checker/levels/strict_spec.rb index 2ccaa5cb3..dbbe3f566 100644 --- a/spec/type_checker/levels/strict_spec.rb +++ b/spec/type_checker/levels/strict_spec.rb @@ -107,14 +107,11 @@ def bar(a); end ), 'test.rb') api_map = Solargraph::ApiMap.new - workspace = Solargraph::Workspace.new('.') - library = Solargraph::Library.new(workspace) - library.map! - api_map.catalog library.bench + specs = api_map.resolve_require('kramdown-parser-gfm') + specs.each { |spec| api_map.cache_gem(spec) } bench = Solargraph::Bench.new(source_maps: [source_map], external_requires: ['kramdown-parser-gfm']) api_map.catalog bench - api_map.cache_all_for_doc_map!(out: STDERR) # rubocop:disable Style/GlobalStdStream - api_map.catalog bench + checker = described_class.new('test.rb', api_map: api_map, level: :strict) expect(checker.problems).to be_empty end From 055281f07ddd6a9631e2aa18558868006f46dbc6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 16:31:16 -0700 Subject: [PATCH 202/206] Run specs in order --- spec/language_server/protocol_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 4fa67752f..53f68180c 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -36,7 +36,7 @@ def stop end end -describe Protocol do +describe Protocol, order: :defined do before :context do @protocol = described_class.new(Solargraph::LanguageServer::Host.new) end @@ -309,6 +309,7 @@ def bar baz } response = @protocol.response expect(response['error']).to be_nil + expect(response['result']).not_to be_nil, -> { "Expected result to be non-nil, got #{response.inspect}" } expect(response['result']['signatures']).not_to be_empty end From 0f2b5f3c91ea03e5885aa2296088561d9ec748e6 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 18:38:50 -0700 Subject: [PATCH 203/206] Move to parallel_test gem --- .rspec | 4 ++-- .rspec_parallel | 10 ++++++++++ Rakefile | 2 +- solargraph.gemspec | 2 +- spec/diagnostics/rubocop_helpers_spec.rb | 2 +- .../message/text_document/rename_spec.rb | 12 ++++++++++-- spec/language_server/protocol_spec.rb | 14 +++++++++----- spec/library_spec.rb | 16 ++++------------ spec/pin_cache_spec.rb | 2 +- spec/source_map/clip_spec.rb | 2 +- spec/spec_helper.rb | 16 ++++++++-------- 11 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 .rspec_parallel diff --git a/.rspec b/.rspec index 7198dff51..1cf25e355 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,3 @@ ---color +# --color --require spec_helper ---profile +# --profile diff --git a/.rspec_parallel b/.rspec_parallel new file mode 100644 index 000000000..ae99528f5 --- /dev/null +++ b/.rspec_parallel @@ -0,0 +1,10 @@ +--require spec_helper +-- +# --format progress +# --verbose-process-command +# --format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log +--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log +# --verbose +# --color +# --profile +-- diff --git a/Rakefile b/Rakefile index b1d6e17c6..21b6150d8 100755 --- a/Rakefile +++ b/Rakefile @@ -41,7 +41,7 @@ end desc 'Run all RSpec tests' task :full_spec do warn 'starting spec' - sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec prspec spec/' # --profile' + sh 'TEST_COVERAGE_COMMAND_NAME=full-new bundle exec parallel_rspec spec/' # --profile' warn 'ending spec' # move coverage/full-new to coverage/full on success so that we # always have the last successful run's 'coverage info diff --git a/solargraph.gemspec b/solargraph.gemspec index e1064522f..f54446db9 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -54,7 +54,7 @@ Gem::Specification.new do |s| s.add_dependency 'yard-activesupport-concern', '~> 0.0' s.add_dependency 'yard-solargraph', '~> 0.1' - s.add_development_dependency 'parallel_rspec', '~> 3.0' + s.add_development_dependency 'parallel_tests', '~> 3.8', '>= 3.8.1' s.add_development_dependency 'pry', '~> 0.15' s.add_development_dependency 'public_suffix', '~> 3.1' s.add_development_dependency 'rake', '~> 13.2' diff --git a/spec/diagnostics/rubocop_helpers_spec.rb b/spec/diagnostics/rubocop_helpers_spec.rb index 7bf374d67..98e31c233 100644 --- a/spec/diagnostics/rubocop_helpers_spec.rb +++ b/spec/diagnostics/rubocop_helpers_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -describe Solargraph::Diagnostics::RubocopHelpers do +describe Solargraph::Diagnostics::RubocopHelpers, order: :defined do context 'with custom version' do around do |example| old_gem_path = Gem.paths.path diff --git a/spec/language_server/message/text_document/rename_spec.rb b/spec/language_server/message/text_document/rename_spec.rb index 2abcb821a..6ce0c953f 100644 --- a/spec/language_server/message/text_document/rename_spec.rb +++ b/spec/language_server/message/text_document/rename_spec.rb @@ -65,8 +65,8 @@ def foo(bar) } }) rename.process - # try for 20 seconds to get the result, since this can be slow on CI - timeout = Time.now + 20 + # wait to get the result generated in a background thread, since this can be slow on CI + timeout = Time.now + 40 until rename.result[:changes] && rename.result[:changes][temp_file_url] && !rename.result[:changes][temp_file_url].empty? sleep 0.1 if Time.now > timeout @@ -145,6 +145,14 @@ class Namespace::ExampleClass library = host.library_for(temp_file_url) allow(library).to receive(:cacheable_specs).and_return([]) rename.process + # try for 20 seconds to get the result, since this can be slow on CI + timeout = Time.now + 20 + until rename.result[:changes] && rename.result[:changes][temp_file_url] && !rename.result[:changes][temp_file_url].empty? + sleep 0.1 + if Time.now > timeout + raise "Timed out waiting for rename result: #{rename.result.inspect}" + end + end changes = rename.result[:changes][temp_file_url] expect(changes).not_to be_nil, -> { "Expected to find changes for #{temp_file_url} in #{rename.result.inspect}" } expect(changes.length).to eq(3) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index 53f68180c..ee4605bb2 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'tmpdir' +require 'rubocop' class Protocol attr_reader :response @@ -45,15 +46,13 @@ def stop around do |testobj| original_dir = Dir.pwd temp_dir = Dir.mktmpdir - Dir.chdir temp_dir - begin + Dir.chdir temp_dir do Solargraph.with_clean_env do testobj.run end - ensure - Dir.chdir(original_dir) - FileUtils.remove_entry(temp_dir) end + ensure + FileUtils.remove_entry(temp_dir) end after :context do @@ -64,6 +63,11 @@ def stop version = instance_double(Gem::Version, version: Gem::Version.new('1.0.0')) Solargraph::LanguageServer::Message::Extended::CheckGemVersion.fetcher = instance_double(Gem::SpecFetcher, search_for_dependency: [version]) + example_name = RSpec.current_example.description + allow(Dir).to receive(:chdir) do + raise "where did this happen - came from #{example_name}" + end + allow(RuboCop::Runner).to receive(:new).and_return(instance_double(RuboCop::Runner, run: [])) end after do diff --git a/spec/library_spec.rb b/spec/library_spec.rb index 27245bf87..cd129fa3d 100644 --- a/spec/library_spec.rb +++ b/spec/library_spec.rb @@ -3,12 +3,9 @@ require 'tmpdir' require 'yard' -describe Solargraph::Library do - before :context do - # run these in order so we don't uncache backport right when we - # need it before - end - +# run these in order so we don't uncache backport right when we +# need it before +describe Solargraph::Library, order: :defined do it 'does not open created files in the workspace' do Dir.mktmpdir do |temp_dir_path| # Ensure we resolve any symlinks to their real path @@ -66,12 +63,7 @@ def foo(adapter) end end - context 'with a require from an already-cached external gem' do - before :context do - # run these in order so we don't uncache backport right when we - # need it before - end - + context 'with a require from an already-cached external gem', order: :defined do it 'returns a Completion' do library = described_class.new(Solargraph::Workspace.new(Dir.pwd, Solargraph::Workspace::Config.new)) diff --git a/spec/pin_cache_spec.rb b/spec/pin_cache_spec.rb index 22b9f7747..12deda597 100644 --- a/spec/pin_cache_spec.rb +++ b/spec/pin_cache_spec.rb @@ -3,7 +3,7 @@ require 'bundler' require 'benchmark' -describe Solargraph::PinCache do +describe Solargraph::PinCache, order: :defined do subject(:pin_cache) do described_class.new(rbs_collection_path: '.gem_rbs_collection', rbs_collection_config_path: 'rbs_collection.yaml', diff --git a/spec/source_map/clip_spec.rb b/spec/source_map/clip_spec.rb index 67b3085b2..dddcef640 100644 --- a/spec/source_map/clip_spec.rb +++ b/spec/source_map/clip_spec.rb @@ -2590,7 +2590,7 @@ def bar; end ), 'test.rb') api_map = Solargraph::ApiMap.new.map(source) clip = api_map.clip_at('test.rb', [7, 6]) - expect(clip.infer.to_s).to eq('Symbol, Integer') + expect(clip.infer.to_s).to eq('123, :foo') end it 'replaces type with alternate reassignments' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3b7bd5aea..74a297577 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'parallel_tests' require 'bundler/setup' require 'webmock/rspec' require 'rspec_time_guard' @@ -29,6 +30,13 @@ RSpec.configure do |c| # Allow use of --only-failures with rspec, handy for local development c.example_status_persistence_file_path = 'rspec-examples.txt' + c.before(:suite) do + files = c.files_to_run + # normalized = files.map { Pathname.new(File.absolute_path(it)).relative_path_from(Rails.root) } + normalized = files + banner = "PID (#{Process.pid}) #{files.count} files to run:" + puts [banner, *normalized].join("\n\t") + end end RspecTimeGuard.setup RspecTimeGuard.configure do |config| @@ -48,14 +56,6 @@ def set_logging end set_logging -require 'parallel_rspec' - -ParallelRSpec.configure do |config| - config.after_fork do |worker_number| - warn "ParallelRSpec worker #{worker_number} starting in pid #{Process.pid}" - set_logging - end -end # @param name [String] # @param value [String] From 66f9cc1109fca42643ac3f4dea3675deb87d6aff Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 18:42:44 -0700 Subject: [PATCH 204/206] Move to parallel_test gem --- .github/workflows/plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 86124c0bb..7d078f5b9 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -88,7 +88,7 @@ jobs: export WORKERS SIMPLECOV_DISABLED=true export SIMPLECOV_DISABLED - bundle exec prspec spec/ + bundle exec parallel_rspec spec/ rails_typechecking: runs-on: ubuntu-latest From 4665e458bff8ac6a0177e6249896ab0345bca032 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 19:23:48 -0700 Subject: [PATCH 205/206] Fix RuboCop issue --- spec/language_server/protocol_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/language_server/protocol_spec.rb b/spec/language_server/protocol_spec.rb index ee4605bb2..059c24fde 100644 --- a/spec/language_server/protocol_spec.rb +++ b/spec/language_server/protocol_spec.rb @@ -44,7 +44,6 @@ def stop # Ensure we don't start caching gems from current bundle in background around do |testobj| - original_dir = Dir.pwd temp_dir = Dir.mktmpdir Dir.chdir temp_dir do Solargraph.with_clean_env do From 19f71d6bc1b71842e1d2172c8d8028424d956be9 Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 7 Feb 2026 19:30:08 -0700 Subject: [PATCH 206/206] Add missing require found by specs --- lib/solargraph/diagnostics/rubocop.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/solargraph/diagnostics/rubocop.rb b/lib/solargraph/diagnostics/rubocop.rb index 39b79d9b7..5d99db9a7 100644 --- a/lib/solargraph/diagnostics/rubocop.rb +++ b/lib/solargraph/diagnostics/rubocop.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'stringio' +require 'rubocop' module Solargraph module Diagnostics