ruby-lsp 0.7.6 → 0.8.1
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +41 -33
- data/exe/ruby-lsp-check +2 -2
- data/lib/core_ext/uri.rb +40 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +91 -0
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +122 -0
- data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +121 -0
- data/lib/ruby_indexer/ruby_indexer.rb +19 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +204 -0
- data/lib/ruby_indexer/test/configuration_test.rb +35 -0
- data/lib/ruby_indexer/test/constant_test.rb +108 -0
- data/lib/ruby_indexer/test/index_test.rb +94 -0
- data/lib/ruby_indexer/test/test_case.rb +42 -0
- data/lib/ruby_lsp/document.rb +3 -3
- data/lib/ruby_lsp/executor.rb +131 -24
- data/lib/ruby_lsp/extension.rb +24 -0
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/listener.rb +15 -14
- data/lib/ruby_lsp/requests/code_actions.rb +3 -3
- data/lib/ruby_lsp/requests/code_lens.rb +10 -24
- data/lib/ruby_lsp/requests/definition.rb +55 -8
- data/lib/ruby_lsp/requests/diagnostics.rb +3 -2
- data/lib/ruby_lsp/requests/document_link.rb +4 -3
- data/lib/ruby_lsp/requests/formatting.rb +3 -2
- data/lib/ruby_lsp/requests/hover.rb +4 -18
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -6
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +5 -0
- data/lib/ruby_lsp/requests/support/formatter_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +2 -3
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +2 -3
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -2
- data/lib/ruby_lsp/server.rb +10 -2
- data/lib/ruby_lsp/setup_bundler.rb +28 -14
- data/lib/ruby_lsp/store.rb +20 -13
- data/lib/ruby_lsp/utils.rb +1 -1
- metadata +27 -3
@@ -0,0 +1,204 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "test_case"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class ClassesAndModulesTest < TestCase
|
8
|
+
def test_empty_statements_class
|
9
|
+
index(<<~RUBY)
|
10
|
+
class Foo
|
11
|
+
end
|
12
|
+
RUBY
|
13
|
+
|
14
|
+
assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_class_with_statements
|
18
|
+
index(<<~RUBY)
|
19
|
+
class Foo
|
20
|
+
def something; end
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
|
24
|
+
assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:2-2")
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_colon_colon_class
|
28
|
+
index(<<~RUBY)
|
29
|
+
class ::Foo
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
|
33
|
+
assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_colon_colon_class_inside_class
|
37
|
+
index(<<~RUBY)
|
38
|
+
class Bar
|
39
|
+
class ::Foo
|
40
|
+
end
|
41
|
+
end
|
42
|
+
RUBY
|
43
|
+
|
44
|
+
assert_entry("Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:3-2")
|
45
|
+
assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-4")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_namespaced_class
|
49
|
+
index(<<~RUBY)
|
50
|
+
class Foo::Bar
|
51
|
+
end
|
52
|
+
RUBY
|
53
|
+
|
54
|
+
assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_dynamically_namespaced_class
|
58
|
+
index(<<~RUBY)
|
59
|
+
class self::Bar
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
|
63
|
+
refute_entry("self::Bar")
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_empty_statements_module
|
67
|
+
index(<<~RUBY)
|
68
|
+
module Foo
|
69
|
+
end
|
70
|
+
RUBY
|
71
|
+
|
72
|
+
assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_module_with_statements
|
76
|
+
index(<<~RUBY)
|
77
|
+
module Foo
|
78
|
+
def something; end
|
79
|
+
end
|
80
|
+
RUBY
|
81
|
+
|
82
|
+
assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:2-2")
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_colon_colon_module
|
86
|
+
index(<<~RUBY)
|
87
|
+
module ::Foo
|
88
|
+
end
|
89
|
+
RUBY
|
90
|
+
|
91
|
+
assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_namespaced_module
|
95
|
+
index(<<~RUBY)
|
96
|
+
module Foo::Bar
|
97
|
+
end
|
98
|
+
RUBY
|
99
|
+
|
100
|
+
assert_entry("Foo::Bar", Index::Entry::Module, "/fake/path/foo.rb:0-0:1-2")
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_dynamically_namespaced_module
|
104
|
+
index(<<~RUBY)
|
105
|
+
module self::Bar
|
106
|
+
end
|
107
|
+
RUBY
|
108
|
+
|
109
|
+
refute_entry("self::Bar")
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_nested_modules_and_classes
|
113
|
+
index(<<~RUBY)
|
114
|
+
module Foo
|
115
|
+
class Bar
|
116
|
+
end
|
117
|
+
|
118
|
+
module Baz
|
119
|
+
class Qux
|
120
|
+
class Something
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
RUBY
|
126
|
+
|
127
|
+
assert_entry("Foo", Index::Entry::Module, "/fake/path/foo.rb:0-0:10-2")
|
128
|
+
assert_entry("Foo::Bar", Index::Entry::Class, "/fake/path/foo.rb:1-2:2-4")
|
129
|
+
assert_entry("Foo::Baz", Index::Entry::Module, "/fake/path/foo.rb:4-2:9-4")
|
130
|
+
assert_entry("Foo::Baz::Qux", Index::Entry::Class, "/fake/path/foo.rb:5-4:8-6")
|
131
|
+
assert_entry("Foo::Baz::Qux::Something", Index::Entry::Class, "/fake/path/foo.rb:6-6:7-8")
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_deleting_from_index_based_on_file_path
|
135
|
+
index(<<~RUBY)
|
136
|
+
class Foo
|
137
|
+
end
|
138
|
+
RUBY
|
139
|
+
|
140
|
+
assert_entry("Foo", Index::Entry::Class, "/fake/path/foo.rb:0-0:1-2")
|
141
|
+
|
142
|
+
@index.delete("/fake/path/foo.rb")
|
143
|
+
refute_entry("Foo")
|
144
|
+
assert_empty(@index.instance_variable_get(:@files_to_entries))
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_comments_can_be_attached_to_a_class
|
148
|
+
index(<<~RUBY)
|
149
|
+
# This is method comment
|
150
|
+
def foo; end
|
151
|
+
# This is a Foo comment
|
152
|
+
# This is another Foo comment
|
153
|
+
class Foo
|
154
|
+
# This should not be attached
|
155
|
+
end
|
156
|
+
|
157
|
+
# Ignore me
|
158
|
+
|
159
|
+
# This Bar comment has 1 line padding
|
160
|
+
|
161
|
+
class Bar; end
|
162
|
+
RUBY
|
163
|
+
|
164
|
+
foo_entry = @index["Foo"].first
|
165
|
+
assert_equal("# This is a Foo comment\n# This is another Foo comment\n", foo_entry.comments.join)
|
166
|
+
|
167
|
+
bar_entry = @index["Bar"].first
|
168
|
+
assert_equal("# This Bar comment has 1 line padding\n", bar_entry.comments.join)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_comments_can_be_attached_to_a_namespaced_class
|
172
|
+
index(<<~RUBY)
|
173
|
+
# This is a Foo comment
|
174
|
+
# This is another Foo comment
|
175
|
+
class Foo
|
176
|
+
# This is a Bar comment
|
177
|
+
class Bar; end
|
178
|
+
end
|
179
|
+
RUBY
|
180
|
+
|
181
|
+
foo_entry = @index["Foo"].first
|
182
|
+
assert_equal("# This is a Foo comment\n# This is another Foo comment\n", foo_entry.comments.join)
|
183
|
+
|
184
|
+
bar_entry = @index["Foo::Bar"].first
|
185
|
+
assert_equal("# This is a Bar comment\n", bar_entry.comments.join)
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_comments_can_be_attached_to_a_reopened_class
|
189
|
+
index(<<~RUBY)
|
190
|
+
# This is a Foo comment
|
191
|
+
class Foo; end
|
192
|
+
|
193
|
+
# This is another Foo comment
|
194
|
+
class Foo; end
|
195
|
+
RUBY
|
196
|
+
|
197
|
+
first_foo_entry = @index["Foo"][0]
|
198
|
+
assert_equal("# This is a Foo comment\n", first_foo_entry.comments.join)
|
199
|
+
|
200
|
+
second_foo_entry = @index["Foo"][1]
|
201
|
+
assert_equal("# This is another Foo comment\n", second_foo_entry.comments.join)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "test_helper"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class ConfigurationTest < Minitest::Test
|
8
|
+
def setup
|
9
|
+
@config = Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_load_configuration_executes_configure_block
|
13
|
+
@config.load_config
|
14
|
+
files_to_index = @config.files_to_index
|
15
|
+
|
16
|
+
assert(files_to_index.none? { |path| path.include?("test/fixtures") })
|
17
|
+
assert(files_to_index.none? { |path| path.include?("minitest-reporters") })
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_paths_are_unique
|
21
|
+
@config.load_config
|
22
|
+
files_to_index = @config.files_to_index
|
23
|
+
|
24
|
+
assert_equal(files_to_index.uniq.length, files_to_index.length)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_configuration_raises_for_unknown_keys
|
28
|
+
Psych::Nodes::Document.any_instance.expects(:to_ruby).returns({ "unknown_config" => 123 })
|
29
|
+
|
30
|
+
assert_raises(ArgumentError) do
|
31
|
+
@config.load_config
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "test_case"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class ConstantTest < TestCase
|
8
|
+
def test_constant_writes
|
9
|
+
index(<<~RUBY)
|
10
|
+
FOO = 1
|
11
|
+
|
12
|
+
class ::Bar
|
13
|
+
FOO = 2
|
14
|
+
end
|
15
|
+
RUBY
|
16
|
+
|
17
|
+
assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-6")
|
18
|
+
assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-8")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_constant_or_writes
|
22
|
+
index(<<~RUBY)
|
23
|
+
FOO ||= 1
|
24
|
+
|
25
|
+
class ::Bar
|
26
|
+
FOO ||= 2
|
27
|
+
end
|
28
|
+
RUBY
|
29
|
+
|
30
|
+
assert_entry("FOO", Index::Entry::Constant, "/fake/path/foo.rb:0-0:0-8")
|
31
|
+
assert_entry("Bar::FOO", Index::Entry::Constant, "/fake/path/foo.rb:3-2:3-10")
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_constant_path_writes
|
35
|
+
index(<<~RUBY)
|
36
|
+
class A
|
37
|
+
FOO = 1
|
38
|
+
::BAR = 1
|
39
|
+
|
40
|
+
module B
|
41
|
+
FOO = 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
A::BAZ = 1
|
46
|
+
RUBY
|
47
|
+
|
48
|
+
assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-8")
|
49
|
+
assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-10")
|
50
|
+
assert_entry("A::B::FOO", Index::Entry::Constant, "/fake/path/foo.rb:5-4:5-10")
|
51
|
+
assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:9-0:9-9")
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_constant_path_or_writes
|
55
|
+
index(<<~RUBY)
|
56
|
+
class A
|
57
|
+
FOO ||= 1
|
58
|
+
::BAR ||= 1
|
59
|
+
end
|
60
|
+
|
61
|
+
A::BAZ ||= 1
|
62
|
+
RUBY
|
63
|
+
|
64
|
+
assert_entry("A::FOO", Index::Entry::Constant, "/fake/path/foo.rb:1-2:1-10")
|
65
|
+
assert_entry("BAR", Index::Entry::Constant, "/fake/path/foo.rb:2-2:2-12")
|
66
|
+
assert_entry("A::BAZ", Index::Entry::Constant, "/fake/path/foo.rb:5-0:5-11")
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_comments_for_constants
|
70
|
+
index(<<~RUBY)
|
71
|
+
# FOO comment
|
72
|
+
FOO = 1
|
73
|
+
|
74
|
+
class A
|
75
|
+
# A::FOO comment
|
76
|
+
FOO = 1
|
77
|
+
|
78
|
+
# ::BAR comment
|
79
|
+
::BAR = 1
|
80
|
+
end
|
81
|
+
|
82
|
+
# A::BAZ comment
|
83
|
+
A::BAZ = 1
|
84
|
+
RUBY
|
85
|
+
|
86
|
+
foo_comment = @index["FOO"].first.comments.join("\n")
|
87
|
+
assert_equal("# FOO comment\n", foo_comment)
|
88
|
+
|
89
|
+
a_foo_comment = @index["A::FOO"].first.comments.join("\n")
|
90
|
+
assert_equal("# A::FOO comment\n", a_foo_comment)
|
91
|
+
|
92
|
+
bar_comment = @index["BAR"].first.comments.join("\n")
|
93
|
+
assert_equal("# ::BAR comment\n", bar_comment)
|
94
|
+
|
95
|
+
a_baz_comment = @index["A::BAZ"].first.comments.join("\n")
|
96
|
+
assert_equal("# A::BAZ comment\n", a_baz_comment)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_variable_path_constants_are_ignored
|
100
|
+
index(<<~RUBY)
|
101
|
+
var::FOO = 1
|
102
|
+
self.class::FOO = 1
|
103
|
+
RUBY
|
104
|
+
|
105
|
+
assert_no_entry
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "test_case"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class IndexTest < TestCase
|
8
|
+
def test_deleting_one_entry_for_a_class
|
9
|
+
@index.index_single("/fake/path/foo.rb", <<~RUBY)
|
10
|
+
class Foo
|
11
|
+
end
|
12
|
+
RUBY
|
13
|
+
@index.index_single("/fake/path/other_foo.rb", <<~RUBY)
|
14
|
+
class Foo
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
|
18
|
+
entries = @index["Foo"]
|
19
|
+
assert_equal(2, entries.length)
|
20
|
+
|
21
|
+
@index.delete("/fake/path/other_foo.rb")
|
22
|
+
entries = @index["Foo"]
|
23
|
+
assert_equal(1, entries.length)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_deleting_all_entries_for_a_class
|
27
|
+
@index.index_single("/fake/path/foo.rb", <<~RUBY)
|
28
|
+
class Foo
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
|
32
|
+
entries = @index["Foo"]
|
33
|
+
assert_equal(1, entries.length)
|
34
|
+
|
35
|
+
@index.delete("/fake/path/foo.rb")
|
36
|
+
entries = @index["Foo"]
|
37
|
+
assert_nil(entries)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_index_resolve
|
41
|
+
@index.index_single("/fake/path/foo.rb", <<~RUBY)
|
42
|
+
class Bar; end
|
43
|
+
|
44
|
+
module Foo
|
45
|
+
class Bar
|
46
|
+
end
|
47
|
+
|
48
|
+
class Baz
|
49
|
+
class Something
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
|
55
|
+
entries = @index.resolve("Something", ["Foo", "Baz"])
|
56
|
+
refute_empty(entries)
|
57
|
+
assert_equal("Foo::Baz::Something", entries.first.name)
|
58
|
+
|
59
|
+
entries = @index.resolve("Bar", ["Foo"])
|
60
|
+
refute_empty(entries)
|
61
|
+
assert_equal("Foo::Bar", entries.first.name)
|
62
|
+
|
63
|
+
entries = @index.resolve("Bar", ["Foo", "Baz"])
|
64
|
+
refute_empty(entries)
|
65
|
+
assert_equal("Foo::Bar", entries.first.name)
|
66
|
+
|
67
|
+
entries = @index.resolve("Foo::Bar", ["Foo", "Baz"])
|
68
|
+
refute_empty(entries)
|
69
|
+
assert_equal("Foo::Bar", entries.first.name)
|
70
|
+
|
71
|
+
assert_nil(@index.resolve("DoesNotExist", ["Foo"]))
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_accessing_with_colon_colon_prefix
|
75
|
+
@index.index_single("/fake/path/foo.rb", <<~RUBY)
|
76
|
+
class Bar; end
|
77
|
+
|
78
|
+
module Foo
|
79
|
+
class Bar
|
80
|
+
end
|
81
|
+
|
82
|
+
class Baz
|
83
|
+
class Something
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
RUBY
|
88
|
+
|
89
|
+
entries = @index["::Foo::Baz::Something"]
|
90
|
+
refute_empty(entries)
|
91
|
+
assert_equal("Foo::Baz::Something", entries.first.name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "test_helper"
|
5
|
+
|
6
|
+
module RubyIndexer
|
7
|
+
class TestCase < Minitest::Test
|
8
|
+
def setup
|
9
|
+
@index = Index.new
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def index(source)
|
15
|
+
@index.index_single("/fake/path/foo.rb", source)
|
16
|
+
end
|
17
|
+
|
18
|
+
def assert_entry(expected_name, type, expected_location)
|
19
|
+
entries = @index[expected_name]
|
20
|
+
refute_empty(entries, "Expected #{expected_name} to be indexed")
|
21
|
+
|
22
|
+
entry = entries.first
|
23
|
+
assert_instance_of(type, entry, "Expected #{expected_name} to be a #{type}")
|
24
|
+
|
25
|
+
location = entry.location
|
26
|
+
location_string =
|
27
|
+
"#{entry.file_path}:#{location.start_line - 1}-#{location.start_column}" \
|
28
|
+
":#{location.end_line - 1}-#{location.end_column}"
|
29
|
+
|
30
|
+
assert_equal(expected_location, location_string)
|
31
|
+
end
|
32
|
+
|
33
|
+
def refute_entry(expected_name)
|
34
|
+
entries = @index[expected_name]
|
35
|
+
assert_nil(entries, "Expected #{expected_name} to not be indexed")
|
36
|
+
end
|
37
|
+
|
38
|
+
def assert_no_entry
|
39
|
+
assert_empty(@index.instance_variable_get(:@entries), "Expected nothing to be indexed")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -18,16 +18,16 @@ module RubyLsp
|
|
18
18
|
sig { returns(Integer) }
|
19
19
|
attr_reader :version
|
20
20
|
|
21
|
-
sig { returns(
|
21
|
+
sig { returns(URI::Generic) }
|
22
22
|
attr_reader :uri
|
23
23
|
|
24
|
-
sig { params(source: String, version: Integer, uri:
|
24
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, encoding: String).void }
|
25
25
|
def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
|
26
26
|
@cache = T.let({}, T::Hash[String, T.untyped])
|
27
27
|
@encoding = T.let(encoding, String)
|
28
28
|
@source = T.let(source, String)
|
29
29
|
@version = T.let(version, Integer)
|
30
|
-
@uri = T.let(uri,
|
30
|
+
@uri = T.let(uri, URI::Generic)
|
31
31
|
@unparsed_edits = T.let([], T::Array[EditShape])
|
32
32
|
@syntax_error = T.let(false, T::Boolean)
|
33
33
|
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|