From fe89079a01a62b0308511ec05aac751c2249c8c6 Mon Sep 17 00:00:00 2001 From: obsidiannnn Date: Tue, 2 Jun 2026 00:32:01 +0530 Subject: [PATCH 1/6] Add export(typed:) to ParamProxy and rewrite_signature to MethodProxy --- lib/proxies/method_proxy.rb | 14 ++++++++ lib/proxies/param_proxy.rb | 37 +++++++++++++++++++- spec/units/method_proxy_rewrite_spec.rb | 36 +++++++++++++++++++ spec/units/param_proxy_spec.rb | 46 +++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 spec/units/method_proxy_rewrite_spec.rb create mode 100644 spec/units/param_proxy_spec.rb diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 2a1338c..3f5f510 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -29,6 +29,20 @@ def tagged_params(tag) @tagged_params[tag] || [] end + def rewrite_signature + sig_index = start_line - 1 + original = lines[sig_index] + indent = original[/^\s*/] + scope_prefix = original.match?(/def self\./) ? 'def self.' : 'def ' + plain_params = params_with_expressions.map { |p| p.export(typed: false) }.join(', ') + all_params = @params.map do |p| + p.expression ? p.export(typed: false) : p.name.to_s + end + rebuilt = "#{indent}#{scope_prefix}#{@name}(#{all_params.join(', ')})" + rebuilt += original[/\).*$/].delete_prefix(')') if original =~ /\)/ + lines[sig_index] = "#{rebuilt}\n" + end + def expressions? @params.any?(&:expression) || @return_proxy end diff --git a/lib/proxies/param_proxy.rb b/lib/proxies/param_proxy.rb index e0e1bac..6cf6be8 100644 --- a/lib/proxies/param_proxy.rb +++ b/lib/proxies/param_proxy.rb @@ -18,7 +18,42 @@ def initialize(name:, source:, type:, position: nil, value: :LOWKEY_UNDEFINED, e end def required? - @value == :LOWKEY_UNDEFINED + @value == :LOWKEY_UNDEFINED || @value == ':LOWKEY_UNDEFINED' + end + + def export(typed: true) + return @source.export if typed + return plain_keyword_param if %i[key_req key_opt].include?(@type) + + plain_positional_param + end + + private + + def plain_keyword_param + default = resolved_default + default.nil? ? "#{@name}:" : "#{@name}: #{default}" + end + + def plain_positional_param + default = resolved_default + default.nil? ? @name.to_s : "#{@name} = #{default}" + end + + def resolved_default + if @expression + return nil if @expression.default_value == :LOW_TYPE_UNDEFINED + return nil if @expression.default_value.nil? && required? + + value = @expression.default_value + return value.value.inspect if value.is_a?(ValueExpression) + + return value.inspect + end + + return nil if @value == :LOWKEY_UNDEFINED || @value == ':LOWKEY_UNDEFINED' + + @value end end end diff --git a/spec/units/method_proxy_rewrite_spec.rb b/spec/units/method_proxy_rewrite_spec.rb new file mode 100644 index 0000000..9b0b64f --- /dev/null +++ b/spec/units/method_proxy_rewrite_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'prism' +require_relative '../../lib/lowkey' + +RSpec.describe Lowkey::MethodProxy do + subject(:file_proxy) { Lowkey.load('spec/fixtures/mock_node.rbx') } + + let(:method_proxy) { file_proxy['Lowkey::MockNode'][:render] } + + describe '#rewrite_signature' do + it 'replaces the signature line in the shared lines array' do + original_line = method_proxy.lines[method_proxy.start_line - 1].dup + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_line - 1] + expect(rewritten).not_to eq(original_line) + end + + it 'removes type annotations from the signature' do + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_line - 1] + expect(rewritten).not_to match(/String|Integer|Symbol/) + end + + it 'preserves the method name' do + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_line - 1] + expect(rewritten).to include('def render') + end + + it 'is reflected in file_proxy.export since lines are shared' do + method_proxy.rewrite_signature + expect(file_proxy.export).to include(method_proxy.lines[method_proxy.start_line - 1].strip) + end + end +end diff --git a/spec/units/param_proxy_spec.rb b/spec/units/param_proxy_spec.rb new file mode 100644 index 0000000..f98d278 --- /dev/null +++ b/spec/units/param_proxy_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'prism' +require_relative '../../lib/lowkey' + +RSpec.describe Lowkey::ParamProxy do + subject(:file_proxy) { Lowkey.load('spec/fixtures/mock_node.rbx') } + + let(:method_proxy) { file_proxy['Lowkey::MockNode'][:render] } + + describe '#export' do + context 'typed: true (default)' do + it 'returns the raw source for a positional param' do + param = method_proxy[:one] + expect(param.export).to eq(param.export(typed: true)) + end + + it 'returns the raw source for a keyword param' do + param = method_proxy[:three] + expect(param.export).to eq(param.export(typed: true)) + end + end + + context 'typed: false with no expression set' do + it 'returns plain name for a required positional param' do + param = method_proxy[:one] + expect(param.export(typed: false)).to eq('one') + end + + it 'returns plain name for a required keyword param' do + param = method_proxy[:three] + expect(param.export(typed: false)).to eq('three:') + end + + it 'returns name with default for an optional positional param' do + param = method_proxy[:two] + expect(param.export(typed: false)).to eq("two = 'mock value'") + end + + it 'returns name with default for an optional keyword param' do + param = method_proxy[:four] + expect(param.export(typed: false)).to eq("four: 'mock value'") + end + end + end +end From 4406112287a593195574a5b384cf0369806a5c05 Mon Sep 17 00:00:00 2001 From: Aditya chauhan Date: Tue, 9 Jun 2026 21:35:54 +0530 Subject: [PATCH 2/6] Address review feedback: start_index, indentation via sig_lines, rename plain to untyped --- lib/interfaces/proxy.rb | 2 +- lib/models/source.rb | 4 ++-- lib/proxies/method_proxy.rb | 15 +++++---------- lib/proxies/param_proxy.rb | 8 ++++---- spec/units/method_proxy_rewrite_spec.rb | 10 +++++----- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/interfaces/proxy.rb b/lib/interfaces/proxy.rb index daabde8..ac2ff80 100644 --- a/lib/interfaces/proxy.rb +++ b/lib/interfaces/proxy.rb @@ -10,7 +10,7 @@ class Proxy attr_reader :name - def_delegators :@source, :file_path, :lines, :lines=, :start_line, :end_line, :scope + def_delegators :@source, :file_path, :lines, :lines=, :start_line, :end_line, :start_index, :scope def_delegators :@source, :wrap, :export def initialize(name:, source:) diff --git a/lib/models/source.rb b/lib/models/source.rb index a4b6c8d..c6404d8 100644 --- a/lib/models/source.rb +++ b/lib/models/source.rb @@ -38,8 +38,6 @@ def export lines[start_index..end_index].join end - private - def start_index start_line - 1 end @@ -47,5 +45,7 @@ def start_index def end_index end_line - 1 end + + private end end diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 3f5f510..8c4a6f7 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -30,17 +30,12 @@ def tagged_params(tag) end def rewrite_signature - sig_index = start_line - 1 - original = lines[sig_index] - indent = original[/^\s*/] + original = lines[start_index] scope_prefix = original.match?(/def self\./) ? 'def self.' : 'def ' - plain_params = params_with_expressions.map { |p| p.export(typed: false) }.join(', ') - all_params = @params.map do |p| - p.expression ? p.export(typed: false) : p.name.to_s - end - rebuilt = "#{indent}#{scope_prefix}#{@name}(#{all_params.join(', ')})" - rebuilt += original[/\).*$/].delete_prefix(')') if original =~ /\)/ - lines[sig_index] = "#{rebuilt}\n" + all_params = @params.map { |p| p.expression ? p.export(typed: false) : p.name.to_s } + sig_lines = ["#{scope_prefix}#{@name}(#{all_params.join(', ')})\n"] + indent = original[/^\s*/] + lines[start_index] = indent + sig_lines[0].lstrip end def expressions? diff --git a/lib/proxies/param_proxy.rb b/lib/proxies/param_proxy.rb index 6cf6be8..22c6240 100644 --- a/lib/proxies/param_proxy.rb +++ b/lib/proxies/param_proxy.rb @@ -23,19 +23,19 @@ def required? def export(typed: true) return @source.export if typed - return plain_keyword_param if %i[key_req key_opt].include?(@type) + return untyped_keyword_param if %i[key_req key_opt].include?(@type) - plain_positional_param + untyped_positional_param end private - def plain_keyword_param + def untyped_keyword_param default = resolved_default default.nil? ? "#{@name}:" : "#{@name}: #{default}" end - def plain_positional_param + def untyped_positional_param default = resolved_default default.nil? ? @name.to_s : "#{@name} = #{default}" end diff --git a/spec/units/method_proxy_rewrite_spec.rb b/spec/units/method_proxy_rewrite_spec.rb index 9b0b64f..f7975b8 100644 --- a/spec/units/method_proxy_rewrite_spec.rb +++ b/spec/units/method_proxy_rewrite_spec.rb @@ -10,27 +10,27 @@ describe '#rewrite_signature' do it 'replaces the signature line in the shared lines array' do - original_line = method_proxy.lines[method_proxy.start_line - 1].dup + original_line = method_proxy.lines[method_proxy.start_index].dup method_proxy.rewrite_signature - rewritten = method_proxy.lines[method_proxy.start_line - 1] + rewritten = method_proxy.lines[method_proxy.start_index] expect(rewritten).not_to eq(original_line) end it 'removes type annotations from the signature' do method_proxy.rewrite_signature - rewritten = method_proxy.lines[method_proxy.start_line - 1] + rewritten = method_proxy.lines[method_proxy.start_index] expect(rewritten).not_to match(/String|Integer|Symbol/) end it 'preserves the method name' do method_proxy.rewrite_signature - rewritten = method_proxy.lines[method_proxy.start_line - 1] + rewritten = method_proxy.lines[method_proxy.start_index] expect(rewritten).to include('def render') end it 'is reflected in file_proxy.export since lines are shared' do method_proxy.rewrite_signature - expect(file_proxy.export).to include(method_proxy.lines[method_proxy.start_line - 1].strip) + expect(file_proxy.export).to include(method_proxy.lines[method_proxy.start_index].strip) end end end From 48c50415debdb57ba5ddab99ac165840b9dbec2c Mon Sep 17 00:00:00 2001 From: Aditya chauhan Date: Fri, 26 Jun 2026 19:34:39 +0530 Subject: [PATCH 3/6] Use lines= setter for indentation, delegate lines= through Proxy, handle return type in rewrite_signature --- lib/proxies/method_proxy.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 8c4a6f7..5d9e813 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -33,9 +33,8 @@ def rewrite_signature original = lines[start_index] scope_prefix = original.match?(/def self\./) ? 'def self.' : 'def ' all_params = @params.map { |p| p.expression ? p.export(typed: false) : p.name.to_s } - sig_lines = ["#{scope_prefix}#{@name}(#{all_params.join(', ')})\n"] - indent = original[/^\s*/] - lines[start_index] = indent + sig_lines[0].lstrip + return_suffix = @return_proxy ? " #{@return_proxy.export}" : '' + self.lines = ["#{scope_prefix}#{@name}(#{all_params.join(', ')})#{return_suffix}\n"] end def expressions? From ee3e09f8583b869055ed48b6d21955a988d76963 Mon Sep 17 00:00:00 2001 From: Aditya chauhan Date: Tue, 7 Jul 2026 20:16:12 +0530 Subject: [PATCH 4/6] Rename original to old_line in rewrite_signature --- lib/proxies/method_proxy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 5d9e813..000871c 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -30,8 +30,8 @@ def tagged_params(tag) end def rewrite_signature - original = lines[start_index] - scope_prefix = original.match?(/def self\./) ? 'def self.' : 'def ' + old_line = lines[start_index] + scope_prefix = old_line.match?(/def self\./) ? 'def self.' : 'def ' all_params = @params.map { |p| p.expression ? p.export(typed: false) : p.name.to_s } return_suffix = @return_proxy ? " #{@return_proxy.export}" : '' self.lines = ["#{scope_prefix}#{@name}(#{all_params.join(', ')})#{return_suffix}\n"] From 866b56535e094053bdcfe10c391519826c9a0fb5 Mon Sep 17 00:00:00 2001 From: Aditya chauhan Date: Sat, 11 Jul 2026 15:07:14 +0530 Subject: [PATCH 5/6] Remove empty private block from Source --- lib/models/source.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/models/source.rb b/lib/models/source.rb index c6404d8..60b58c3 100644 --- a/lib/models/source.rb +++ b/lib/models/source.rb @@ -45,7 +45,5 @@ def start_index def end_index end_line - 1 end - - private end end From 6cefcc2b153aeaf2476a3dfa37258736df86abd6 Mon Sep 17 00:00:00 2001 From: Aditya chauhan Date: Thu, 30 Jul 2026 03:46:03 +0530 Subject: [PATCH 6/6] Fix rewrite_signature to use direct lines[start_index] instead of lines= setter --- lib/proxies/method_proxy.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 000871c..74e9d7a 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -34,7 +34,8 @@ def rewrite_signature scope_prefix = old_line.match?(/def self\./) ? 'def self.' : 'def ' all_params = @params.map { |p| p.expression ? p.export(typed: false) : p.name.to_s } return_suffix = @return_proxy ? " #{@return_proxy.export}" : '' - self.lines = ["#{scope_prefix}#{@name}(#{all_params.join(', ')})#{return_suffix}\n"] + indent = old_line[/^\s*/] + lines[start_index] = "#{indent}#{scope_prefix}#{@name}(#{all_params.join(', ')})#{return_suffix}\n" end def expressions?