From 0e95c7f23b8f32dd3de531e4c5f9bf7da30def74 Mon Sep 17 00:00:00 2001 From: Kurt Werle Date: Tue, 13 Jan 2026 20:13:01 -0800 Subject: [PATCH 1/2] Update dependencies and enhance scope parsing functionality - Bump prism to version 1.8.0 and update other gem versions in Gemfile.lock. - Modify location hash generation in code_file.rb to include column information. - Change refresh_scopes_if_needed to perform a full refresh in project_manager.rb. - Refactor code_file_spec.rb to improve test structure and add tests for location ranges. --- Gemfile.lock | 36 ++++----- lib/ruby_language_server/code_file.rb | 4 +- lib/ruby_language_server/project_manager.rb | 2 +- .../ruby_language_server/code_file_spec.rb | 80 ++++++++++++++++--- .../ruby_language_server/scope_parser_spec.rb | 21 +++++ 5 files changed, 109 insertions(+), 34 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 75cae8b..83e7b01 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,19 +9,19 @@ PATH fuzzy_match json ostruct - prism + prism (>= 1.8.0) sqlite3 GEM remote: https://rubygems.org/ specs: - activemodel (8.1.1) - activesupport (= 8.1.1) - activerecord (8.1.1) - activemodel (= 8.1.1) - activesupport (= 8.1.1) + activemodel (8.1.2) + activesupport (= 8.1.2) + activerecord (8.1.2) + activemodel (= 8.1.2) + activesupport (= 8.1.2) timeout (>= 0.4.0) - activesupport (8.1.1) + activesupport (8.1.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -111,7 +111,7 @@ GEM pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.7.0) + prism (1.8.0) pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) @@ -126,7 +126,7 @@ GEM rb-inotify (0.11.1) ffi (~> 1.0) rb-readline (0.5.5) - rdoc (7.0.3) + rdoc (7.1.0) erb psych (>= 4.0.0) tsort @@ -160,7 +160,7 @@ GEM rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) - rubocop-rspec (3.8.0) + rubocop-rspec (3.9.0) lint_roller (~> 1.1) rubocop (~> 1.81) ruby-progressbar (1.13.0) @@ -175,7 +175,7 @@ GEM sqlite3 (2.9.0-aarch64-linux-musl) stringio (3.2.0) sync (0.5.0) - thor (1.4.0) + thor (1.5.0) timeout (0.6.0) tins (1.51.0) bigdecimal @@ -213,9 +213,9 @@ DEPENDENCIES simplecov_json_formatter CHECKSUMS - activemodel (8.1.1) sha256=8b7e2496b9e333ced06248c16a43217b950192c98e0fe3aa117eee21501c6fbd - activerecord (8.1.1) sha256=e32c3a03e364fd803498eb4150c21bedc995aa83bc27122a94d480ab1dcb3d17 - activesupport (8.1.1) sha256=5e92534e8d0c8b8b5e6b16789c69dbea65c1d7b752269f71a39422e9546cea67 + activemodel (8.1.2) sha256=e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e + activerecord (8.1.2) sha256=acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44 + activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae amatch (0.6.0) sha256=247996bdce87754a42ed38ce8b22a4b8162e1699a8d353bc36bc96b544bd509d ansi (1.5.0) sha256=5408253274e33d9d27d4a98c46d2998266fd51cba58a7eb9d08f50e57ed23592 ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 @@ -258,7 +258,7 @@ CHECKSUMS parser (3.3.10.0) sha256=ce3587fa5cc55a88c4ba5b2b37621b3329aadf5728f9eafa36bbd121462aabd6 pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6 prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 - prism (1.7.0) sha256=10062f734bf7985c8424c44fac382ac04a58124ea3d220ec3ba9fe4f2da65103 + prism (1.8.0) sha256=84453a16ef5530ea62c5f03ec16b52a459575ad4e7b9c2b360fd8ce2c39c1254 pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f @@ -267,7 +267,7 @@ CHECKSUMS rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e rb-readline (0.5.5) sha256=9e9bd7e198bdef0822c46902f6c592b882c1f9777894a4c3dcf5b320824a8793 - rdoc (7.0.3) sha256=dfe3d0981d19b7bba71d9dbaeb57c9f4e3a7a4103162148a559c4fc687ea81f9 + rdoc (7.1.0) sha256=494899df0706c178596ca6e1d50f1b7eb285a9b2aae715be5abd742734f17363 readline (0.0.4) sha256=6138eef17be2b98298b672c3ea63bf9cb5158d401324f26e1e84f235879c1d6a regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835 @@ -276,7 +276,7 @@ CHECKSUMS rubocop-minitest (0.38.2) sha256=5a9dfb5a538973d0601aa51e59637d3998bb8df81233edf1ff421504c6280068 rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d - rubocop-rspec (3.8.0) sha256=28440dccb3f223a9938ca1f946bd3438275b8c6c156dab909e2cb8bc424cab33 + rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 ruby_language_server (0.9.3) securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 @@ -287,7 +287,7 @@ CHECKSUMS sqlite3 (2.9.0-aarch64-linux-musl) sha256=56a35cb2d70779afc2ac191baf2c2148242285ecfed72f9b021218c5c4917913 stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 sync (0.5.0) sha256=668356cc07c59ac7ed9ecf34fec3929831f179c07adb1f3e1c3b7a1609a638fd - thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d + thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 timeout (0.6.0) sha256=6d722ad619f96ee383a0c557ec6eb8c4ecb08af3af62098a0be5057bf00de1af tins (1.51.0) sha256=9f83c534bfca23973c5e641308828d71d5ffa79fc32c0ef90996efa699d0696f tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f diff --git a/lib/ruby_language_server/code_file.rb b/lib/ruby_language_server/code_file.rb index cfa600f..44bc49b 100644 --- a/lib/ruby_language_server/code_file.rb +++ b/lib/ruby_language_server/code_file.rb @@ -71,7 +71,7 @@ def tags scope_hash = { name: scope.name, kind:, - location: Location.hash(uri, scope.top_line) + location: Location.hash(uri, scope.top_line, scope.column, scope.bottom_line) } container_name = ancestor_scope_name(scope) scope_hash[:containerName] = container_name unless container_name.blank? @@ -82,7 +82,7 @@ def tags { name:, kind: SYMBOL_KIND[:constant], - location: Location.hash(uri, variable.line - 1), + location: Location.hash(uri, variable.line - 1, variable.column), containerName: variable.scope.name } end diff --git a/lib/ruby_language_server/project_manager.rb b/lib/ruby_language_server/project_manager.rb index 7bcbc07..85b920a 100644 --- a/lib/ruby_language_server/project_manager.rb +++ b/lib/ruby_language_server/project_manager.rb @@ -115,7 +115,7 @@ def scan_all_project_files begin ActiveRecord::Base.connection_pool.with_connection do |_connection| update_document_content(host_uri, text) - code_file_for_uri(host_uri).refresh_scopes_if_needed(shallow: true) + code_file_for_uri(host_uri).refresh_scopes_if_needed(shallow: false) end rescue StandardError => e RubyLanguageServer.logger.warn("Error updating: #{e}\n#{e.backtrace * "\n"}") diff --git a/spec/lib/ruby_language_server/code_file_spec.rb b/spec/lib/ruby_language_server/code_file_spec.rb index 50af3f0..3d4a23c 100644 --- a/spec/lib/ruby_language_server/code_file_spec.rb +++ b/spec/lib/ruby_language_server/code_file_spec.rb @@ -5,13 +5,8 @@ describe RubyLanguageServer::CodeFile do describe 'CodeFile' do - it 'must init' do - RubyLanguageServer::CodeFile.build('uri', "class Foo\nend\n") - end - - describe 'tags' do - let(:source) do - <<-SOURCE + let(:source) do + <<-SOURCE class Foo def self.foo_class_method end @@ -22,14 +17,19 @@ def foo_method end FOO_CONSTANT = 1 end - SOURCE - end + SOURCE + end - let(:tags) { code_file(source).tags } + def code_file(text) + RubyLanguageServer::CodeFile.build('uri', text) + end - def code_file(text) - RubyLanguageServer::CodeFile.build('uri', text) - end + it 'must init' do + RubyLanguageServer::CodeFile.build('uri', "class Foo\nend\n") + end + + describe 'tags' do + let(:tags) { code_file(source).tags } it 'should find classes' do code_file = code_file("class Foo\nend\n") @@ -72,6 +72,60 @@ def code_file(text) tag = tags.detect { |t| t[:name] == 'initialize' } assert_equal(9, tag[:kind]) end + + describe 'location ranges' do + let(:cf) { code_file(source) } + + it 'should have correct start and end lines for a simple class' do + tag = cf.tags.detect { |t| t[:name] == 'Foo' } + + # Line numbers are 0-indexed in LSP + expected_range = { + start: { line: 0, character: 0 }, + end: { line: 9, character: 0 } + } + assert_equal(expected_range, tag[:location][:range]) + end + + it 'should have correct start and end lines for a method' do + tag = cf.tags.detect { |t| t[:name] == 'foo_method' } + + expected_range = { + start: { line: 5, character: 2 }, + end: { line: 7, character: 2 } + } + assert_equal(expected_range, tag[:location][:range]) + end + + it 'should have correct start and end lines for multiple methods' do + foo_tag = cf.tags.detect { |t| t[:name] == 'Foo' } + foo_class_method_tag = cf.tags.detect { |t| t[:name] == 'foo_class_method' } + initialize_tag = cf.tags.detect { |t| t[:name] == 'initialize' } + foo_method_tag = cf.tags.detect { |t| t[:name] == 'foo_method' } + + expected_foo_range = { + start: { line: 0, character: 0 }, + end: { line: 9, character: 0 } + } + expected_foo_class_method_range = { + start: { line: 1, character: 2 }, + end: { line: 2, character: 2 } + } + expected_initialize_range = { + start: { line: 3, character: 2 }, + end: { line: 4, character: 2 } + } + expected_foo_method_range = { + start: { line: 5, character: 2 }, + end: { line: 7, character: 2 } + } + + assert_equal(expected_foo_range, foo_tag[:location][:range]) + assert_equal(expected_foo_class_method_range, foo_class_method_tag[:location][:range]) + assert_equal(expected_initialize_range, initialize_tag[:location][:range]) + assert_equal(expected_foo_method_range, foo_method_tag[:location][:range]) + end + end end end end diff --git a/spec/lib/ruby_language_server/scope_parser_spec.rb b/spec/lib/ruby_language_server/scope_parser_spec.rb index 03553c6..700024f 100644 --- a/spec/lib/ruby_language_server/scope_parser_spec.rb +++ b/spec/lib/ruby_language_server/scope_parser_spec.rb @@ -253,5 +253,26 @@ module Baz assert_equal(0, bar.children.size, "Bar should have no children") end end + + describe 'module with single constant' do + before do + @parser = RubyLanguageServer::ScopeParser.new(<<-RUBY) + module MyModule + MY_CONSTANT = 42 + end + RUBY + end + + it 'should have the constant as a variable child of the module' do + my_module = @parser.root_scope.children.first + assert_equal('MyModule', my_module.name) + assert_equal(:module, my_module.type, "MyModule should be a module") + + # The constant should be a variable within the module scope + constant_vars = my_module.variables + assert_equal(1, constant_vars.size, "MyModule should have 1 constant variable") + assert_equal('MY_CONSTANT', constant_vars.first.name, "The constant should be named MY_CONSTANT") + end + end end end From d6e8040af3d2f206e01a2f2befd825b9750c0878 Mon Sep 17 00:00:00 2001 From: Kurt Werle Date: Tue, 13 Jan 2026 20:17:24 -0800 Subject: [PATCH 2/2] Fix the string thing --- spec/lib/ruby_language_server/code_file_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/ruby_language_server/code_file_spec.rb b/spec/lib/ruby_language_server/code_file_spec.rb index 3d4a23c..6da023d 100644 --- a/spec/lib/ruby_language_server/code_file_spec.rb +++ b/spec/lib/ruby_language_server/code_file_spec.rb @@ -6,7 +6,7 @@ describe RubyLanguageServer::CodeFile do describe 'CodeFile' do let(:source) do - <<-SOURCE + <<~SOURCE class Foo def self.foo_class_method end