ruby-lsp-refactor 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +68 -0
  3. data/README.md +553 -115
  4. data/lib/ruby/lsp/refactor/version.rb +1 -1
  5. data/lib/ruby_lsp/ruby_lsp_refactor/addon.rb +54 -17
  6. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/accessor_listener.rb +156 -0
  7. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/array_listener.rb +2 -2
  8. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/conditional_listener.rb +14 -14
  9. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/constant_listener.rb +118 -0
  10. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/early_return_listener.rb +105 -0
  11. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/enumerable_listener.rb +90 -0
  12. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/extract_include_file_listener.rb +172 -0
  13. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/extract_predicate_listener.rb +138 -0
  14. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/hash_listener.rb +7 -7
  15. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/logical_operator_listener.rb +70 -0
  16. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/method_listener.rb +37 -109
  17. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/raise_listener.rb +70 -0
  18. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/rescue_listener.rb +85 -0
  19. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/rspec_let_listener.rb +61 -0
  20. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/string_array_listener.rb +88 -0
  21. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/string_freeze_listener.rb +86 -0
  22. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/string_listener.rb +2 -2
  23. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/super_listener.rb +95 -0
  24. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/tap_listener.rb +133 -0
  25. data/lib/ruby_lsp/ruby_lsp_refactor/listeners/variable_listener.rb +7 -49
  26. data/lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb +62 -12
  27. data/lib/ruby_lsp/test_helper.rb +5 -5
  28. data/test/ruby_lsp_refactor/accessor_listener_test.rb +91 -0
  29. data/test/ruby_lsp_refactor/constant_listener_test.rb +68 -0
  30. data/test/ruby_lsp_refactor/early_return_listener_test.rb +156 -0
  31. data/test/ruby_lsp_refactor/enumerable_listener_test.rb +80 -0
  32. data/test/ruby_lsp_refactor/extract_include_file_listener_test.rb +189 -0
  33. data/test/ruby_lsp_refactor/extract_predicate_listener_test.rb +113 -0
  34. data/test/ruby_lsp_refactor/logical_operator_listener_test.rb +66 -0
  35. data/test/ruby_lsp_refactor/method_listener_test.rb +3 -52
  36. data/test/ruby_lsp_refactor/raise_listener_test.rb +64 -0
  37. data/test/ruby_lsp_refactor/rescue_listener_test.rb +72 -0
  38. data/test/ruby_lsp_refactor/rspec_let_listener_test.rb +54 -0
  39. data/test/ruby_lsp_refactor/string_array_listener_test.rb +64 -0
  40. data/test/ruby_lsp_refactor/string_freeze_listener_test.rb +52 -0
  41. data/test/ruby_lsp_refactor/string_listener_test.rb +2 -2
  42. data/test/ruby_lsp_refactor/super_listener_test.rb +65 -0
  43. data/test/ruby_lsp_refactor/tap_listener_test.rb +144 -0
  44. data/test/ruby_lsp_refactor/variable_listener_test.rb +0 -23
  45. metadata +42 -13
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "ruby_lsp/test_helper"
5
+
6
+ module RubyLsp
7
+ module Refactor
8
+ class StringFreezeListenerTest < Minitest::Test
9
+ include RubyLsp::Refactor::TestHelper
10
+
11
+ def find_action(actions, title)
12
+ actions.find { |a| a.title == title }
13
+ end
14
+
15
+ def single_edit(action)
16
+ edits = action.edit.changes.values.flatten
17
+ assert_equal 1, edits.size
18
+ edits.first
19
+ end
20
+
21
+ def test_wraps_string_in_freeze
22
+ source = "\"hello world\"\n"
23
+ actions = code_actions_for(source, line: 0)
24
+ action = find_action(actions, "Wrap in freeze")
25
+ refute_nil action
26
+
27
+ edit = single_edit(action)
28
+ assert_equal '"hello world".freeze', edit.new_text
29
+ end
30
+
31
+ def test_removes_freeze_from_frozen_string
32
+ source = "\"hello world\".freeze\n"
33
+ actions = code_actions_for(source, line: 0)
34
+ action = find_action(actions, "Remove freeze")
35
+ refute_nil action
36
+
37
+ edit = single_edit(action)
38
+ assert_equal '"hello world"', edit.new_text
39
+ end
40
+
41
+ def test_does_not_offer_wrap_when_already_frozen
42
+ source = "\"hello\".freeze\n"
43
+ actions = code_actions_for(source, line: 0)
44
+ assert_nil find_action(actions, "Wrap in freeze")
45
+ end
46
+
47
+ def test_does_not_raise_on_empty_source
48
+ assert_silent { code_actions_for("", line: 0) }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -47,13 +47,13 @@ module RubyLsp
47
47
  # ---------------------------------------------------------------------------
48
48
 
49
49
  def test_does_not_offer_action_for_already_double_quoted_string
50
- source = '"hello world"' + "\n"
50
+ source = "\"hello world\"\n"
51
51
  actions = code_actions_for(source, line: 0)
52
52
  assert_nil find_action(actions, "Convert to interpolated string")
53
53
  end
54
54
 
55
55
  def test_does_not_offer_action_for_interpolated_string
56
- source = '"hello #{name}"' + "\n"
56
+ source = "\"hello #{name}\"\n"
57
57
  actions = code_actions_for(source, line: 0)
58
58
  assert_nil find_action(actions, "Convert to interpolated string")
59
59
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "ruby_lsp/test_helper"
5
+
6
+ module RubyLsp
7
+ module Refactor
8
+ class SuperListenerTest < Minitest::Test
9
+ include RubyLsp::Refactor::TestHelper
10
+
11
+ def find_action(actions, title_pattern)
12
+ actions.find { |a| a.title.match?(title_pattern) }
13
+ end
14
+
15
+ def single_edit(action)
16
+ edits = action.edit.changes.values.flatten
17
+ assert_equal 1, edits.size
18
+ edits.first
19
+ end
20
+
21
+ def test_converts_bare_super_to_explicit
22
+ source = <<~RUBY
23
+ def initialize(name, age)
24
+ super
25
+ end
26
+ RUBY
27
+
28
+ actions = code_actions_for(source, line: 1)
29
+ action = find_action(actions, /explicit super/)
30
+ refute_nil action
31
+
32
+ edit = single_edit(action)
33
+ assert_equal "super(name, age)", edit.new_text.strip
34
+ end
35
+
36
+ def test_title_includes_param_names
37
+ source = <<~RUBY
38
+ def initialize(name, age)
39
+ super
40
+ end
41
+ RUBY
42
+
43
+ actions = code_actions_for(source, line: 1)
44
+ action = find_action(actions, /explicit super/)
45
+ refute_nil action
46
+ assert_match(/name, age/, action.title)
47
+ end
48
+
49
+ def test_does_not_offer_when_no_params
50
+ source = <<~RUBY
51
+ def initialize
52
+ super
53
+ end
54
+ RUBY
55
+
56
+ actions = code_actions_for(source, line: 1)
57
+ assert_nil find_action(actions, /explicit super/)
58
+ end
59
+
60
+ def test_does_not_raise_on_empty_source
61
+ assert_silent { code_actions_for("", line: 0) }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "ruby_lsp/test_helper"
5
+
6
+ module RubyLsp
7
+ module Refactor
8
+ class TapListenerTest < Minitest::Test
9
+ include RubyLsp::Refactor::TestHelper
10
+
11
+ def find_action(actions, title)
12
+ actions.find { |a| a.title == title }
13
+ end
14
+
15
+ def single_edit(action)
16
+ edits = action.edit.changes.values.flatten
17
+ assert_equal 1, edits.size
18
+ edits.first
19
+ end
20
+
21
+ # ── core acceptance ────────────────────────────────────────────────────
22
+
23
+ def test_converts_sequence_to_tap
24
+ source = <<~RUBY
25
+ def do_something
26
+ obj.do_first_thing
27
+ obj.do_second_thing
28
+ obj.do_third_thing
29
+ obj
30
+ end
31
+ RUBY
32
+
33
+ actions = code_actions_for(source, line: 0)
34
+ action = find_action(actions, "Convert to tap")
35
+ refute_nil action
36
+
37
+ edit = single_edit(action)
38
+ assert_match(/obj\.tap do \|o\|/, edit.new_text)
39
+ assert_match(/o\.do_first_thing/, edit.new_text)
40
+ assert_match(/o\.do_second_thing/, edit.new_text)
41
+ assert_match(/o\.do_third_thing/, edit.new_text)
42
+ assert_match(/end/, edit.new_text)
43
+ refute_match(/\bobj\b(?!\.tap)/, edit.new_text.sub("obj.tap", ""))
44
+ end
45
+
46
+ def test_preserves_method_arguments
47
+ source = <<~RUBY
48
+ def setup
49
+ user.assign_role(:admin)
50
+ user.set_name("Alice")
51
+ user
52
+ end
53
+ RUBY
54
+
55
+ actions = code_actions_for(source, line: 0)
56
+ action = find_action(actions, "Convert to tap")
57
+ refute_nil action
58
+
59
+ edit = single_edit(action)
60
+ assert_match(/user\.tap do \|o\|/, edit.new_text)
61
+ assert_match(/o\.assign_role\(:admin\)/, edit.new_text)
62
+ assert_match(/o\.set_name\("Alice"\)/, edit.new_text)
63
+ end
64
+
65
+ def test_preserves_indentation
66
+ source = <<~RUBY
67
+ class Builder
68
+ def build
69
+ obj.step_one
70
+ obj.step_two
71
+ obj
72
+ end
73
+ end
74
+ RUBY
75
+
76
+ actions = code_actions_for(source, line: 1)
77
+ action = find_action(actions, "Convert to tap")
78
+ refute_nil action
79
+
80
+ edit = single_edit(action)
81
+ # The body range starts at the method body's indentation (4 spaces).
82
+ assert_match(/\A obj\.tap do \|o\|/, edit.new_text)
83
+ assert_match(/ o\.step_one/, edit.new_text)
84
+ end
85
+
86
+ # ── negative cases ─────────────────────────────────────────────────────
87
+
88
+ def test_does_not_offer_when_last_statement_is_not_bare_receiver
89
+ source = <<~RUBY
90
+ def do_something
91
+ obj.step_one
92
+ obj.step_two
93
+ obj.result
94
+ end
95
+ RUBY
96
+
97
+ # last statement is a call on obj, not a bare variable read
98
+ actions = code_actions_for(source, line: 0)
99
+ assert_nil find_action(actions, "Convert to tap")
100
+ end
101
+
102
+ def test_does_not_offer_when_receivers_differ
103
+ source = <<~RUBY
104
+ def do_something
105
+ foo.step_one
106
+ bar.step_two
107
+ foo
108
+ end
109
+ RUBY
110
+
111
+ actions = code_actions_for(source, line: 0)
112
+ assert_nil find_action(actions, "Convert to tap")
113
+ end
114
+
115
+ def test_does_not_offer_for_single_statement_method
116
+ source = <<~RUBY
117
+ def do_something
118
+ obj
119
+ end
120
+ RUBY
121
+
122
+ actions = code_actions_for(source, line: 0)
123
+ assert_nil find_action(actions, "Convert to tap")
124
+ end
125
+
126
+ def test_does_not_offer_outside_a_method
127
+ source = <<~RUBY
128
+ obj.step_one
129
+ obj.step_two
130
+ obj
131
+ RUBY
132
+
133
+ actions = code_actions_for(source, line: 0)
134
+ assert_nil find_action(actions, "Convert to tap")
135
+ end
136
+
137
+ # ── resilience ─────────────────────────────────────────────────────────
138
+
139
+ def test_does_not_raise_on_empty_source
140
+ assert_silent { code_actions_for("", line: 0) }
141
+ end
142
+ end
143
+ end
144
+ end
@@ -62,29 +62,6 @@ module RubyLsp
62
62
  assert_nil find_action(actions, /Inline variable/)
63
63
  end
64
64
 
65
- # ===========================================================================
66
- # 2. Extract local variable
67
- # ===========================================================================
68
-
69
- def test_extract_local_variable_inserts_assignment_above
70
- source = "user.full_name.upcase\n"
71
- actions = code_actions_for(source, line: 0)
72
- action = find_action(actions, "Extract local variable")
73
- refute_nil action
74
-
75
- edits = all_edits(action)
76
- assert_equal 2, edits.size
77
-
78
- insert_edit = edits.find { |e| e.new_text.include?("variable =") }
79
- replace_edit = edits.find { |e| e.new_text == "variable" }
80
-
81
- refute_nil insert_edit
82
- refute_nil replace_edit
83
-
84
- assert_match(/variable = user\.full_name\.upcase/, insert_edit.new_text)
85
- assert_equal 0, insert_edit.range.start.line
86
- end
87
-
88
65
  # ---------------------------------------------------------------------------
89
66
  # Resilience
90
67
  # ---------------------------------------------------------------------------
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-refactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aboobacker MK
@@ -10,39 +10,39 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: ruby-lsp
13
+ name: prism
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0.17'
19
- - - "<"
20
- - !ruby/object:Gem::Version
21
- version: '2'
18
+ version: '0.29'
22
19
  type: :runtime
23
20
  prerelease: false
24
21
  version_requirements: !ruby/object:Gem::Requirement
25
22
  requirements:
26
23
  - - ">="
27
24
  - !ruby/object:Gem::Version
28
- version: '0.17'
29
- - - "<"
30
- - !ruby/object:Gem::Version
31
- version: '2'
25
+ version: '0.29'
32
26
  - !ruby/object:Gem::Dependency
33
- name: prism
27
+ name: ruby-lsp
34
28
  requirement: !ruby/object:Gem::Requirement
35
29
  requirements:
36
30
  - - ">="
37
31
  - !ruby/object:Gem::Version
38
- version: '0.29'
32
+ version: '0.17'
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '2'
39
36
  type: :runtime
40
37
  prerelease: false
41
38
  version_requirements: !ruby/object:Gem::Requirement
42
39
  requirements:
43
40
  - - ">="
44
41
  - !ruby/object:Gem::Version
45
- version: '0.29'
42
+ version: '0.17'
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '2'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: minitest
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,7 @@ executables: []
94
94
  extensions: []
95
95
  extra_rdoc_files: []
96
96
  files:
97
+ - ".rubocop_todo.yml"
97
98
  - CHANGELOG.md
98
99
  - LICENSE.txt
99
100
  - README.md
@@ -101,20 +102,48 @@ files:
101
102
  - lib/ruby/lsp/refactor.rb
102
103
  - lib/ruby/lsp/refactor/version.rb
103
104
  - lib/ruby_lsp/ruby_lsp_refactor/addon.rb
105
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/accessor_listener.rb
104
106
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/array_listener.rb
105
107
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/conditional_listener.rb
108
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/constant_listener.rb
109
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/early_return_listener.rb
110
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/enumerable_listener.rb
111
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/extract_include_file_listener.rb
112
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/extract_predicate_listener.rb
106
113
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/hash_listener.rb
114
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/logical_operator_listener.rb
107
115
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/method_listener.rb
116
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/raise_listener.rb
117
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/rescue_listener.rb
118
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/rspec_let_listener.rb
119
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/string_array_listener.rb
120
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/string_freeze_listener.rb
108
121
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/string_listener.rb
122
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/super_listener.rb
123
+ - lib/ruby_lsp/ruby_lsp_refactor/listeners/tap_listener.rb
109
124
  - lib/ruby_lsp/ruby_lsp_refactor/listeners/variable_listener.rb
110
125
  - lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb
111
126
  - lib/ruby_lsp/test_helper.rb
112
127
  - sig/ruby/lsp/refactor.rbs
128
+ - test/ruby_lsp_refactor/accessor_listener_test.rb
113
129
  - test/ruby_lsp_refactor/array_listener_test.rb
114
130
  - test/ruby_lsp_refactor/conditional_listener_test.rb
131
+ - test/ruby_lsp_refactor/constant_listener_test.rb
132
+ - test/ruby_lsp_refactor/early_return_listener_test.rb
133
+ - test/ruby_lsp_refactor/enumerable_listener_test.rb
134
+ - test/ruby_lsp_refactor/extract_include_file_listener_test.rb
135
+ - test/ruby_lsp_refactor/extract_predicate_listener_test.rb
115
136
  - test/ruby_lsp_refactor/hash_listener_test.rb
137
+ - test/ruby_lsp_refactor/logical_operator_listener_test.rb
116
138
  - test/ruby_lsp_refactor/method_listener_test.rb
139
+ - test/ruby_lsp_refactor/raise_listener_test.rb
140
+ - test/ruby_lsp_refactor/rescue_listener_test.rb
141
+ - test/ruby_lsp_refactor/rspec_let_listener_test.rb
142
+ - test/ruby_lsp_refactor/string_array_listener_test.rb
143
+ - test/ruby_lsp_refactor/string_freeze_listener_test.rb
117
144
  - test/ruby_lsp_refactor/string_listener_test.rb
145
+ - test/ruby_lsp_refactor/super_listener_test.rb
146
+ - test/ruby_lsp_refactor/tap_listener_test.rb
118
147
  - test/ruby_lsp_refactor/variable_listener_test.rb
119
148
  homepage: https://github.com/tachyons/ruby-lsp-refactor
120
149
  licenses: