rubylens 0.2.0 → 0.2.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/README.md +3 -3
- data/lib/rubylens/art_model_builder.rb +184 -94
- data/lib/rubylens/index/constant_reference_collector.rb +150 -0
- data/lib/rubylens/index/declaration_collector.rb +288 -0
- data/lib/rubylens/index/git_package_source.rb +202 -0
- data/lib/rubylens/index/location_index.rb +103 -0
- data/lib/rubylens/index/manifest.rb +54 -150
- data/lib/rubylens/index/rubydex_adapter.rb +109 -449
- data/lib/rubylens/model/dependency_aggregation.rb +3 -1
- data/lib/rubylens/version.rb +1 -1
- metadata +7 -3
|
@@ -2,65 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
require "rubydex"
|
|
4
4
|
require "set"
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "constant_reference_collector"
|
|
6
|
+
require_relative "declaration_collector"
|
|
7
|
+
require_relative "location_index"
|
|
6
8
|
require_relative "rspec_extractor"
|
|
7
|
-
require_relative "source_path"
|
|
8
9
|
|
|
9
10
|
module RubyLens
|
|
10
11
|
module Index
|
|
12
|
+
# Drives one Rubydex indexing run over the manifest and assembles the
|
|
13
|
+
# snapshot payload from what the collectors return.
|
|
14
|
+
#
|
|
15
|
+
# The snapshot is the privacy boundary: every namespace and dependency star
|
|
16
|
+
# leaves here as a row of integers, and the only strings that survive are
|
|
17
|
+
# namespace names, package names, and the project name.
|
|
11
18
|
class RubydexAdapter
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
CONSTANT_REFERENCE_ATTRIBUTION_LIMIT = CONSTANT_REFERENCE_LINK_LIMIT * 2
|
|
15
|
-
|
|
16
|
-
ReferenceSummary = Data.define(:inbound_counts, :links)
|
|
19
|
+
SCHEMA = "rubylens.snapshot.v9"
|
|
20
|
+
RSPEC_ROW = [0, 1, *Array.new(11, 0)].freeze
|
|
17
21
|
|
|
22
|
+
# The manifest is duck-typed: production passes a Manifest, tests pass
|
|
23
|
+
# stubs, and `index` probes it with respond_to? for the optional parts.
|
|
24
|
+
#
|
|
25
|
+
#: (untyped manifest) -> void
|
|
18
26
|
def initialize(manifest)
|
|
19
27
|
@manifest = manifest
|
|
20
|
-
@
|
|
21
|
-
@workspace_location_cache = {}
|
|
22
|
-
@definition_scope_by_uri = {}
|
|
23
|
-
@package_index_by_uri = {}
|
|
24
|
-
@indexed_package_document_paths = Set.new
|
|
28
|
+
@locations = LocationIndex.new(manifest)
|
|
25
29
|
end
|
|
26
30
|
|
|
31
|
+
#: () -> Hash[String, untyped]
|
|
27
32
|
def index
|
|
28
33
|
graph = Rubydex::Graph.new(workspace_path: @manifest.root.to_s)
|
|
29
34
|
index_errors = graph.index_all(@manifest.files)
|
|
30
|
-
|
|
35
|
+
# Documents resolve before `resolve` so package attribution sees exactly
|
|
36
|
+
# the documents Rubydex accepted, not the paths the manifest offered.
|
|
37
|
+
documents_with_paths = @locations.resolve_documents(graph)
|
|
31
38
|
graph.resolve
|
|
32
39
|
integrity_failures = graph.check_integrity
|
|
33
|
-
|
|
40
|
+
|
|
41
|
+
collected = DeclarationCollector.new(manifest: @manifest, locations: @locations).call(graph.declarations)
|
|
34
42
|
rspec = RSpecExtractor.new(
|
|
35
43
|
graph: graph,
|
|
36
44
|
manifest: @manifest,
|
|
37
|
-
package_document_paths: @
|
|
45
|
+
package_document_paths: @locations.package_document_paths,
|
|
38
46
|
documents_with_paths: documents_with_paths,
|
|
39
47
|
).call
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
collected.
|
|
48
|
+
|
|
49
|
+
namespaces = collected.namespaces
|
|
50
|
+
namespace_names = namespaces.map(&:name) + rspec.groups
|
|
51
|
+
references = ConstantReferenceCollector.new(locations: @locations).call(
|
|
52
|
+
graph: graph,
|
|
53
|
+
namespaces: namespaces,
|
|
54
|
+
definition_ranges: collected.definition_ranges,
|
|
55
|
+
ordinal_by_name: ordinal_by_name(namespaces),
|
|
56
|
+
dependency_ordinal_by_name: collected.dependency_ordinal_by_name,
|
|
57
|
+
namespace_count: namespace_names.length,
|
|
49
58
|
)
|
|
50
|
-
|
|
59
|
+
|
|
60
|
+
category_stats = collected.category_stats
|
|
51
61
|
category_stats.fetch("tests")[0] += rspec.groups.length
|
|
52
62
|
category_stats.fetch("tests")[2] += rspec.method_count
|
|
53
63
|
|
|
54
64
|
{
|
|
55
|
-
"schema" =>
|
|
65
|
+
"schema" => SCHEMA,
|
|
56
66
|
"project_name" => project_name,
|
|
57
|
-
"namespace_names" =>
|
|
58
|
-
"namespaces" =>
|
|
59
|
-
"constant_reference_links" =>
|
|
67
|
+
"namespace_names" => namespace_names,
|
|
68
|
+
"namespaces" => namespace_rows(namespaces, references.inbound_counts, rspec.groups.length),
|
|
69
|
+
"constant_reference_links" => references.links,
|
|
60
70
|
"category_stats" => category_stats,
|
|
61
|
-
"dependency_signal_maxima" => collected.
|
|
62
|
-
"packages" =>
|
|
63
|
-
"dependency_systems" =>
|
|
71
|
+
"dependency_signal_maxima" => collected.dependency_signal_maxima,
|
|
72
|
+
"packages" => package_rows(collected.dependency_packages),
|
|
73
|
+
"dependency_systems" => dependency_system_rows,
|
|
64
74
|
"dependency_warnings" => @manifest.respond_to?(:dependency_warnings) ? @manifest.dependency_warnings : [],
|
|
65
75
|
"warning_counts" => {
|
|
66
76
|
"manifest" => @manifest.warnings.length,
|
|
@@ -72,162 +82,86 @@ module RubyLens
|
|
|
72
82
|
|
|
73
83
|
private
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if canonical_site_keys
|
|
93
|
-
canonical_site_keys = canonical_site_keys.uniq if canonical_site_keys.length > 1
|
|
94
|
-
ordinal = records.length
|
|
95
|
-
canonical_site_keys.each do |uri, start_line, start_column, end_line, end_column|
|
|
96
|
-
workspace_definition_ranges[uri] << [start_line, start_column, end_line, end_column, ordinal]
|
|
97
|
-
end
|
|
98
|
-
records << [declaration, canonical_site_keys.length, canonical_scope, name]
|
|
99
|
-
end
|
|
100
|
-
if construct_index && workspace_count.positive?
|
|
101
|
-
category_stats.fetch(tests_only ? "tests" : "core")[construct_index] += 1
|
|
102
|
-
end
|
|
103
|
-
collect_dependency_declaration(
|
|
104
|
-
aggregation,
|
|
105
|
-
dependency_positions,
|
|
106
|
-
declaration,
|
|
107
|
-
name,
|
|
108
|
-
is_namespace,
|
|
109
|
-
construct_index,
|
|
110
|
-
package_site_keys,
|
|
111
|
-
reference_count,
|
|
112
|
-
)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
dependency_packages = aggregation.packages
|
|
116
|
-
dependency_offsets = []
|
|
117
|
-
offset = 0
|
|
118
|
-
dependency_packages.each do |package|
|
|
119
|
-
dependency_offsets << offset
|
|
120
|
-
offset += package.fetch(:declarations).length
|
|
121
|
-
end
|
|
122
|
-
dependency_ordinal_by_name = dependency_positions.transform_values! do |position|
|
|
123
|
-
package_index, local_index = position
|
|
124
|
-
dependency_offsets.fetch(package_index) + local_index
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
workspace_records: records,
|
|
129
|
-
workspace_definition_ranges:,
|
|
130
|
-
category_stats:,
|
|
131
|
-
dependency_packages:,
|
|
132
|
-
dependency_signal_maxima: aggregation.signal_maxima,
|
|
133
|
-
dependency_ordinal_by_name: dependency_ordinal_by_name.freeze,
|
|
134
|
-
}
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# One pass over a declaration's definitions gathers everything the
|
|
138
|
-
# collectors need, so each definition's location and its per-URI
|
|
139
|
-
# workspace/scope/package answers cross the Rubydex boundary once.
|
|
140
|
-
# Canonical site keys and package site keys stay nil until a definition
|
|
141
|
-
# actually contributes to them, since most declarations produce neither.
|
|
142
|
-
def summarize_definitions(declaration, is_namespace)
|
|
143
|
-
workspace_count = 0
|
|
144
|
-
tests_seen = false
|
|
145
|
-
core_seen = false
|
|
146
|
-
canonical_tests_seen = false
|
|
147
|
-
canonical_core_seen = false
|
|
148
|
-
canonical_site_keys = nil
|
|
149
|
-
package_site_keys = nil
|
|
150
|
-
|
|
151
|
-
declaration.definitions.each do |definition|
|
|
152
|
-
location = definition.location
|
|
153
|
-
site_key = nil
|
|
154
|
-
if workspace_location?(location)
|
|
155
|
-
workspace_count += 1
|
|
156
|
-
scope = definition_scope(location.uri)
|
|
157
|
-
if scope == 1
|
|
158
|
-
tests_seen = true
|
|
159
|
-
elsif scope
|
|
160
|
-
core_seen = true
|
|
161
|
-
end
|
|
162
|
-
if is_namespace && canonical_namespace_definition?(declaration, definition)
|
|
163
|
-
(canonical_site_keys ||= []) << (site_key = location.comparable_values)
|
|
164
|
-
if scope == 1
|
|
165
|
-
canonical_tests_seen = true
|
|
166
|
-
elsif scope
|
|
167
|
-
canonical_core_seen = true
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
package_index = package_index_for_location(location)
|
|
172
|
-
if package_index
|
|
173
|
-
((package_site_keys ||= {})[package_index] ||= []) << (site_key || location.comparable_values)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
canonical_scope = canonical_tests_seen ? (canonical_core_seen ? 2 : 1) : 0
|
|
178
|
-
[workspace_count, tests_seen && !core_seen, canonical_site_keys, canonical_scope, package_site_keys]
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def workspace_namespaces(records, rspec_groups, definition_ranges = {})
|
|
182
|
-
namespace_names = []
|
|
183
|
-
ordinal_by_name = {}
|
|
184
|
-
records.each_with_index do |record, index|
|
|
185
|
-
name = record.fetch(3)
|
|
186
|
-
namespace_names << name
|
|
187
|
-
ordinal_by_name[name] = index
|
|
188
|
-
end
|
|
189
|
-
{
|
|
190
|
-
records: records,
|
|
191
|
-
definition_ranges: definition_ranges,
|
|
192
|
-
rspec_groups: rspec_groups,
|
|
193
|
-
namespace_names: namespace_names + rspec_groups,
|
|
194
|
-
ordinal_by_name: ordinal_by_name,
|
|
195
|
-
}
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def build_workspace_rows(workspace, inbound_references)
|
|
199
|
-
ordinal_by_name = workspace.fetch(:ordinal_by_name)
|
|
200
|
-
rows = workspace.fetch(:records).each_with_index.map do |record, index|
|
|
201
|
-
declaration, sites, scope, name = record
|
|
85
|
+
#: (Array[DeclarationCollector::Namespace]) -> Hash[String, Integer]
|
|
86
|
+
def ordinal_by_name(namespaces)
|
|
87
|
+
ordinals = {}
|
|
88
|
+
namespaces.each_with_index { |namespace, index| ordinals[namespace.name] = index }
|
|
89
|
+
ordinals
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#: (Array[DeclarationCollector::Namespace], Hash[Integer, Integer], Integer) -> Array[Array[Integer]]
|
|
93
|
+
def namespace_rows(namespaces, inbound_counts, rspec_group_count)
|
|
94
|
+
drawn_names = ordinal_by_name(namespaces)
|
|
95
|
+
rows = namespaces.each_with_index.map do |namespace, index|
|
|
96
|
+
declaration = namespace.declaration
|
|
97
|
+
sites = namespace.definition_sites
|
|
98
|
+
# A namespace only earns a row through a class or module definition,
|
|
99
|
+
# so its own construct is exactly this kind.
|
|
100
|
+
kind = declaration.is_a?(Rubydex::Class) ? DeclarationCollector::CLASS : DeclarationCollector::MODULE
|
|
202
101
|
descendants = declaration.descendants.count do |descendant|
|
|
203
|
-
|
|
204
|
-
|
|
102
|
+
name = descendant.name
|
|
103
|
+
name != namespace.name && drawn_names.key?(name)
|
|
205
104
|
end
|
|
206
|
-
|
|
105
|
+
members, ruby_counts, instance_variables = member_statistics(
|
|
207
106
|
declaration,
|
|
208
|
-
|
|
107
|
+
kind: kind,
|
|
108
|
+
# Instance variables read as state on a class; test classes carry
|
|
109
|
+
# fixture state that would misrepresent them next to core classes.
|
|
110
|
+
count_instance_variables: kind == DeclarationCollector::CLASS && namespace.scope != LocationIndex::TEST,
|
|
209
111
|
)
|
|
210
112
|
[
|
|
211
|
-
|
|
212
|
-
scope,
|
|
113
|
+
kind,
|
|
114
|
+
namespace.scope,
|
|
213
115
|
[declaration.ancestors.count - 1, 0].max,
|
|
214
116
|
sites,
|
|
215
117
|
[sites - 1, 0].max,
|
|
216
118
|
descendants,
|
|
217
|
-
|
|
218
|
-
|
|
119
|
+
inbound_counts.fetch(index, 0),
|
|
120
|
+
members,
|
|
219
121
|
*ruby_counts,
|
|
220
|
-
|
|
122
|
+
instance_variables,
|
|
221
123
|
]
|
|
222
124
|
end
|
|
223
|
-
rows.concat(
|
|
125
|
+
rows.concat(Array.new(rspec_group_count) { RSPEC_ROW.dup })
|
|
224
126
|
end
|
|
225
127
|
|
|
226
|
-
|
|
227
|
-
|
|
128
|
+
# One sweep over a namespace's direct and singleton members yields the
|
|
129
|
+
# workspace member count, the method and constant construct counts, and
|
|
130
|
+
# the instance-variable count together, since each of the three otherwise
|
|
131
|
+
# re-reads every member's definitions.
|
|
132
|
+
#
|
|
133
|
+
#: (Rubydex::Namespace, kind: Integer, count_instance_variables: bool) -> [Integer, Array[Integer], Integer]
|
|
134
|
+
def member_statistics(declaration, kind:, count_instance_variables:)
|
|
135
|
+
members = 0
|
|
136
|
+
instance_variables = 0
|
|
137
|
+
ruby_counts = Array.new(4, 0)
|
|
138
|
+
ruby_counts[kind] += 1
|
|
139
|
+
|
|
140
|
+
seen = Set.new
|
|
141
|
+
groups = [[declaration.members, true]]
|
|
142
|
+
singleton = declaration.singleton_class
|
|
143
|
+
groups << [singleton.members, false] if singleton
|
|
144
|
+
groups.each do |group, direct|
|
|
145
|
+
group.each do |member|
|
|
146
|
+
next unless seen.add?(member.name)
|
|
147
|
+
next unless member.definitions.any? { |definition| @locations.workspace?(definition.location) }
|
|
148
|
+
|
|
149
|
+
members += 1
|
|
150
|
+
construct = case member
|
|
151
|
+
when Rubydex::Method then DeclarationCollector::METHOD
|
|
152
|
+
when Rubydex::Constant, Rubydex::ConstantAlias then DeclarationCollector::CONSTANT
|
|
153
|
+
end
|
|
154
|
+
ruby_counts[construct] += 1 if construct
|
|
155
|
+
instance_variables += 1 if direct && count_instance_variables && member.is_a?(Rubydex::InstanceVariable)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
[members, ruby_counts, instance_variables]
|
|
159
|
+
rescue StandardError
|
|
160
|
+
[members, ruby_counts, instance_variables]
|
|
228
161
|
end
|
|
229
162
|
|
|
230
|
-
|
|
163
|
+
#: (Array[DeclarationCollector::dependency_package]) -> Array[Hash[String, untyped]]
|
|
164
|
+
def package_rows(aggregates)
|
|
231
165
|
@manifest.packages.each_with_index.map do |package, index|
|
|
232
166
|
aggregate = aggregates.fetch(index)
|
|
233
167
|
{
|
|
@@ -240,7 +174,8 @@ module RubyLens
|
|
|
240
174
|
end
|
|
241
175
|
end
|
|
242
176
|
|
|
243
|
-
|
|
177
|
+
#: () -> Array[Hash[String, untyped]]
|
|
178
|
+
def dependency_system_rows
|
|
244
179
|
return [] unless @manifest.respond_to?(:dependency_systems)
|
|
245
180
|
|
|
246
181
|
@manifest.dependency_systems.map do |system|
|
|
@@ -252,282 +187,7 @@ module RubyLens
|
|
|
252
187
|
end
|
|
253
188
|
end
|
|
254
189
|
|
|
255
|
-
|
|
256
|
-
aggregation,
|
|
257
|
-
positions,
|
|
258
|
-
declaration,
|
|
259
|
-
name,
|
|
260
|
-
is_namespace,
|
|
261
|
-
construct_index,
|
|
262
|
-
package_site_keys,
|
|
263
|
-
reference_count
|
|
264
|
-
)
|
|
265
|
-
return unless package_site_keys
|
|
266
|
-
|
|
267
|
-
package_index = nil
|
|
268
|
-
site_keys = nil
|
|
269
|
-
package_site_keys.each do |index, keys|
|
|
270
|
-
if site_keys.nil? || keys.length > site_keys.length ||
|
|
271
|
-
(keys.length == site_keys.length && index < package_index)
|
|
272
|
-
package_index = index
|
|
273
|
-
site_keys = keys
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
sites = site_keys.length > 1 ? site_keys.uniq.length : site_keys.length
|
|
278
|
-
row = [
|
|
279
|
-
namespace_kind(declaration),
|
|
280
|
-
is_namespace ? [declaration.ancestors.count - 1, 0].max : 0,
|
|
281
|
-
sites,
|
|
282
|
-
[sites - 1, 0].max,
|
|
283
|
-
is_namespace ? [declaration.descendants.count - 1, 0].max : 0,
|
|
284
|
-
reference_count,
|
|
285
|
-
is_namespace ? safe_length(declaration, :members) : 0,
|
|
286
|
-
].freeze
|
|
287
|
-
local_index = aggregation.add(
|
|
288
|
-
package_index:,
|
|
289
|
-
row:,
|
|
290
|
-
construct_index:,
|
|
291
|
-
)
|
|
292
|
-
if reference_count.positive? && constant_reference_target?(declaration, is_namespace)
|
|
293
|
-
positions[name] = [package_index, local_index]
|
|
294
|
-
end
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
def workspace_reference_summary(graph, workspace, dependency_ordinal_by_name = {})
|
|
298
|
-
ordinal_by_name = workspace.fetch(:ordinal_by_name)
|
|
299
|
-
namespace_count = workspace.fetch(:namespace_names, ordinal_by_name).length
|
|
300
|
-
workspace_ranges = workspace.fetch(:definition_ranges, {})
|
|
301
|
-
inbound_counts = Hash.new(0)
|
|
302
|
-
workspace.fetch(:records).each_with_index do |record, index|
|
|
303
|
-
record.fetch(0).references.each do |reference|
|
|
304
|
-
location = begin
|
|
305
|
-
reference.location
|
|
306
|
-
rescue StandardError
|
|
307
|
-
next
|
|
308
|
-
end
|
|
309
|
-
inbound_counts[index] += 1 if workspace_location?(location)
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
links = []
|
|
313
|
-
retained_links = Set.new
|
|
314
|
-
attribution_count = 0
|
|
315
|
-
|
|
316
|
-
graph.constant_references.each do |reference|
|
|
317
|
-
break if links.length >= CONSTANT_REFERENCE_LINK_LIMIT ||
|
|
318
|
-
attribution_count >= CONSTANT_REFERENCE_ATTRIBUTION_LIMIT
|
|
319
|
-
next unless reference.is_a?(Rubydex::ResolvedConstantReference)
|
|
320
|
-
|
|
321
|
-
referenced_name = resolved_constant_reference_name(reference)
|
|
322
|
-
next unless referenced_name
|
|
323
|
-
referenced_ordinal = ordinal_by_name[referenced_name]
|
|
324
|
-
dependency_ordinal = dependency_ordinal_by_name[referenced_name] unless referenced_ordinal
|
|
325
|
-
next unless referenced_ordinal || dependency_ordinal
|
|
326
|
-
|
|
327
|
-
location = begin
|
|
328
|
-
reference.location
|
|
329
|
-
rescue StandardError
|
|
330
|
-
next
|
|
331
|
-
end
|
|
332
|
-
next unless workspace_location?(location)
|
|
333
|
-
|
|
334
|
-
attribution_count += 1
|
|
335
|
-
|
|
336
|
-
referenced_endpoint = referenced_ordinal || namespace_count + dependency_ordinal
|
|
337
|
-
|
|
338
|
-
location_values = begin
|
|
339
|
-
location.comparable_values
|
|
340
|
-
rescue StandardError
|
|
341
|
-
next
|
|
342
|
-
end
|
|
343
|
-
referring_ordinal = enclosing_definition_ordinal(location_values, workspace_ranges)
|
|
344
|
-
next unless referring_ordinal
|
|
345
|
-
next if referenced_endpoint == referring_ordinal
|
|
346
|
-
|
|
347
|
-
link = [referring_ordinal, referenced_endpoint]
|
|
348
|
-
next unless retained_links.add?(link)
|
|
349
|
-
|
|
350
|
-
links << link
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
ReferenceSummary.new(inbound_counts.freeze, links.freeze)
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def resolved_constant_reference_name(reference)
|
|
357
|
-
reference.declaration.name
|
|
358
|
-
rescue StandardError
|
|
359
|
-
nil
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def enclosing_definition_ordinal(location_values, ranges_by_uri)
|
|
363
|
-
uri, start_line, start_column, end_line, end_column = location_values
|
|
364
|
-
best = nil
|
|
365
|
-
ambiguous = false
|
|
366
|
-
ranges_by_uri.fetch(uri, []).each do |range|
|
|
367
|
-
range_start_line, range_start_column, range_end_line, range_end_column, ordinal = range
|
|
368
|
-
next if position_compare(range_start_line, range_start_column, start_line, start_column).positive?
|
|
369
|
-
next if position_compare(range_end_line, range_end_column, end_line, end_column).negative?
|
|
370
|
-
|
|
371
|
-
start_comparison = best ? position_compare(range_start_line, range_start_column, best[0], best[1]) : 1
|
|
372
|
-
end_comparison = best && start_comparison.zero? ? position_compare(range_end_line, range_end_column, best[2], best[3]) : 0
|
|
373
|
-
if start_comparison.positive? || (start_comparison.zero? && end_comparison.negative?)
|
|
374
|
-
best = range
|
|
375
|
-
ambiguous = false
|
|
376
|
-
elsif start_comparison.zero? && end_comparison.zero? && ordinal != best[4]
|
|
377
|
-
ambiguous = true
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
ambiguous ? nil : best&.fetch(4)
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def position_compare(left_line, left_column, right_line, right_column)
|
|
385
|
-
line_comparison = left_line <=> right_line
|
|
386
|
-
line_comparison.zero? ? left_column <=> right_column : line_comparison
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
def package_index_for_location(location)
|
|
390
|
-
uri = location.uri
|
|
391
|
-
@package_index_by_uri.fetch(uri) do
|
|
392
|
-
path = source_path(uri)
|
|
393
|
-
@package_index_by_uri[uri] =
|
|
394
|
-
if path && @indexed_package_document_paths.include?(path)
|
|
395
|
-
@manifest.package_index_for(path)
|
|
396
|
-
end
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
# One sweep over the indexed documents resolves each URI once, yielding
|
|
401
|
-
# both the package-audit set and the [document, path] pairs the RSpec
|
|
402
|
-
# extractor reuses without re-enumerating documents or re-parsing URIs.
|
|
403
|
-
def resolve_documents(graph)
|
|
404
|
-
audited = @manifest.packages.flat_map(&:files).to_set
|
|
405
|
-
package_paths = Set.new
|
|
406
|
-
documents_with_paths = []
|
|
407
|
-
graph.documents.each do |document|
|
|
408
|
-
path = source_path(document.uri)
|
|
409
|
-
next unless path
|
|
410
|
-
|
|
411
|
-
documents_with_paths << [document, path]
|
|
412
|
-
package_paths << path if audited.include?(path)
|
|
413
|
-
end
|
|
414
|
-
[package_paths, documents_with_paths]
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
# One sweep over a namespace's direct and singleton members yields the
|
|
418
|
-
# workspace member count, the method/constant construct counts, and the
|
|
419
|
-
# instance-variable count that previously took three walks each re-reading
|
|
420
|
-
# every member's definitions. Membership comes from each member's own
|
|
421
|
-
# definition locations; the per-URI caches keep that scan cheap.
|
|
422
|
-
def member_statistics(declaration, count_instance_variables:)
|
|
423
|
-
member_count = 0
|
|
424
|
-
instance_variable_count = 0
|
|
425
|
-
ruby_counts = Array.new(4, 0)
|
|
426
|
-
own_construct = ruby_construct_index(declaration)
|
|
427
|
-
ruby_counts[own_construct] += 1 if own_construct == 0 || own_construct == 1
|
|
428
|
-
|
|
429
|
-
seen_names = Set.new
|
|
430
|
-
member_groups = [[declaration.members, true]]
|
|
431
|
-
singleton = declaration.singleton_class
|
|
432
|
-
member_groups << [singleton.members, false] if singleton
|
|
433
|
-
member_groups.each do |members, direct|
|
|
434
|
-
members.each do |member|
|
|
435
|
-
next unless seen_names.add?(member.name)
|
|
436
|
-
next unless member.definitions.any? { |definition| workspace_location?(definition.location) }
|
|
437
|
-
|
|
438
|
-
member_count += 1
|
|
439
|
-
construct_index = ruby_construct_index(member)
|
|
440
|
-
ruby_counts[construct_index] += 1 if construct_index && construct_index >= 2
|
|
441
|
-
if direct && count_instance_variables && member.is_a?(Rubydex::InstanceVariable)
|
|
442
|
-
instance_variable_count += 1
|
|
443
|
-
end
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
[member_count, ruby_counts, instance_variable_count]
|
|
447
|
-
rescue StandardError
|
|
448
|
-
[member_count, ruby_counts, instance_variable_count]
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
def workspace_location?(location)
|
|
452
|
-
uri = location.uri
|
|
453
|
-
@workspace_location_cache.fetch(uri) do
|
|
454
|
-
path = source_path(uri)
|
|
455
|
-
@workspace_location_cache[uri] = path ? @manifest.workspace_path?(path) : false
|
|
456
|
-
end
|
|
457
|
-
rescue StandardError
|
|
458
|
-
false
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
# 1 for a test-tree file, 0 for any other workspace file, nil when the
|
|
462
|
-
# URI has no workspace-relative path to classify.
|
|
463
|
-
def definition_scope(uri)
|
|
464
|
-
@definition_scope_by_uri.fetch(uri) do
|
|
465
|
-
relative = @manifest.relative_workspace_path(source_path(uri))
|
|
466
|
-
@definition_scope_by_uri[uri] =
|
|
467
|
-
if relative
|
|
468
|
-
relative.split(File::SEPARATOR).any? { |segment| TEST_SEGMENTS.include?(segment) } ? 1 : 0
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
def constant_reference_target?(declaration, is_namespace)
|
|
474
|
-
is_namespace ||
|
|
475
|
-
declaration.is_a?(Rubydex::Constant) ||
|
|
476
|
-
declaration.is_a?(Rubydex::ConstantAlias)
|
|
477
|
-
end
|
|
478
|
-
|
|
479
|
-
def model_eligible_declaration?(declaration, name = declaration.name)
|
|
480
|
-
return false if name.nil? || name.empty? || name.include?("<anonymous>")
|
|
481
|
-
return false if declaration.is_a?(Rubydex::Todo)
|
|
482
|
-
return true unless declaration.is_a?(Rubydex::SingletonClass)
|
|
483
|
-
|
|
484
|
-
attached = declaration.attached_class
|
|
485
|
-
!attached.is_a?(Rubydex::SingletonClass) && !attached.is_a?(Rubydex::Todo)
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
def canonical_namespace_definition?(declaration, definition)
|
|
489
|
-
(declaration.is_a?(Rubydex::Class) && definition.is_a?(Rubydex::ClassDefinition)) ||
|
|
490
|
-
(declaration.is_a?(Rubydex::Module) && definition.is_a?(Rubydex::ModuleDefinition))
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
def namespace_kind(declaration)
|
|
494
|
-
case declaration
|
|
495
|
-
when Rubydex::Class then 0
|
|
496
|
-
when Rubydex::Module then 1
|
|
497
|
-
else 2
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
def ruby_construct_index(declaration)
|
|
502
|
-
case declaration
|
|
503
|
-
when Rubydex::SingletonClass then nil
|
|
504
|
-
when Rubydex::Class then 0
|
|
505
|
-
when Rubydex::Module then 1
|
|
506
|
-
when Rubydex::Method then 2
|
|
507
|
-
when Rubydex::Constant, Rubydex::ConstantAlias then 3
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def source_path(uri_string)
|
|
512
|
-
@source_path_cache.fetch(uri_string) do
|
|
513
|
-
@source_path_cache[uri_string] = SourcePath.from_file_uri(uri_string)
|
|
514
|
-
end
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
def safe_length(object, method)
|
|
518
|
-
records = object.public_send(method)
|
|
519
|
-
size = records.size
|
|
520
|
-
size.nil? ? safe_count(records) : size
|
|
521
|
-
rescue StandardError
|
|
522
|
-
safe_count(records)
|
|
523
|
-
end
|
|
524
|
-
|
|
525
|
-
def safe_count(records)
|
|
526
|
-
records ? records.count : 0
|
|
527
|
-
rescue StandardError
|
|
528
|
-
0
|
|
529
|
-
end
|
|
530
|
-
|
|
190
|
+
#: () -> String
|
|
531
191
|
def project_name
|
|
532
192
|
basename = @manifest.root.basename.to_s
|
|
533
193
|
return "IRB" if basename.casecmp("irb").zero?
|
|
@@ -17,10 +17,12 @@ module RubyLens
|
|
|
17
17
|
local_index = @rows[package_index].length
|
|
18
18
|
@ruby_counts[package_index][construct_index] += 1 if construct_index
|
|
19
19
|
maxima = @signal_maxima
|
|
20
|
-
SIGNAL_COLUMNS.
|
|
20
|
+
column = SIGNAL_COLUMNS.begin
|
|
21
|
+
while column <= SIGNAL_COLUMNS.end
|
|
21
22
|
value = row[column]
|
|
22
23
|
index = column - 1
|
|
23
24
|
maxima[index] = value if value > maxima[index]
|
|
25
|
+
column += 1
|
|
24
26
|
end
|
|
25
27
|
@rows[package_index] << (row.frozen? ? row : row.dup.freeze)
|
|
26
28
|
local_index
|
data/lib/rubylens/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubylens
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RubyLens contributors
|
|
@@ -69,6 +69,10 @@ files:
|
|
|
69
69
|
- lib/rubylens/errors.rb
|
|
70
70
|
- lib/rubylens/generator.rb
|
|
71
71
|
- lib/rubylens/git_repository.rb
|
|
72
|
+
- lib/rubylens/index/constant_reference_collector.rb
|
|
73
|
+
- lib/rubylens/index/declaration_collector.rb
|
|
74
|
+
- lib/rubylens/index/git_package_source.rb
|
|
75
|
+
- lib/rubylens/index/location_index.rb
|
|
72
76
|
- lib/rubylens/index/manifest.rb
|
|
73
77
|
- lib/rubylens/index/rspec_extractor.rb
|
|
74
78
|
- lib/rubylens/index/rubydex_adapter.rb
|
|
@@ -82,12 +86,12 @@ files:
|
|
|
82
86
|
- lib/rubylens/showcase_model.rb
|
|
83
87
|
- lib/rubylens/showcase_writer.rb
|
|
84
88
|
- lib/rubylens/version.rb
|
|
85
|
-
homepage: https://st0012.dev/
|
|
89
|
+
homepage: https://st0012.dev/ruby-galaxies/
|
|
86
90
|
licenses:
|
|
87
91
|
- MIT
|
|
88
92
|
metadata:
|
|
89
93
|
allowed_push_host: https://rubygems.org
|
|
90
|
-
homepage_uri: https://st0012.dev/
|
|
94
|
+
homepage_uri: https://st0012.dev/ruby-galaxies/
|
|
91
95
|
rubygems_mfa_required: 'true'
|
|
92
96
|
rdoc_options: []
|
|
93
97
|
require_paths:
|