holistic-ruby 0.1.4 → 0.1.6
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/Gemfile.lock +1 -1
- data/lib/holistic/application.rb +12 -4
- data/lib/holistic/database/migrations.rb +20 -0
- data/lib/holistic/database/node.rb +29 -0
- data/lib/holistic/database/table.rb +53 -53
- data/lib/holistic/document/file/record.rb +10 -0
- data/lib/holistic/document/file/repository.rb +24 -0
- data/lib/holistic/document/file/store.rb +13 -0
- data/lib/holistic/document/location.rb +4 -6
- data/lib/holistic/document/unsaved/record.rb +0 -4
- data/lib/holistic/extensions/ruby/stdlib.rb +6 -6
- data/lib/holistic/language_server/requests/lifecycle/initialize.rb +5 -10
- data/lib/holistic/language_server/requests/text_document/completion.rb +5 -4
- data/lib/holistic/language_server/requests/text_document/did_close.rb +5 -9
- data/lib/holistic/language_server/requests/text_document/did_open.rb +1 -0
- data/lib/holistic/language_server/requests/text_document/did_save.rb +5 -9
- data/lib/holistic/language_server/requests/text_document/find_references.rb +7 -4
- data/lib/holistic/language_server/requests/text_document/go_to_definition.rb +3 -2
- data/lib/holistic/ruby/autocompletion/suggest.rb +40 -20
- data/lib/holistic/ruby/parser/constant_resolution.rb +8 -8
- data/lib/holistic/ruby/parser/live_editing/process_file_changed.rb +21 -21
- data/lib/holistic/ruby/parser/program_visitor.rb +21 -24
- data/lib/holistic/ruby/parser.rb +8 -11
- data/lib/holistic/ruby/reference/delete.rb +18 -0
- data/lib/holistic/ruby/reference/find_referenced_scope.rb +2 -2
- data/lib/holistic/ruby/reference/record.rb +7 -8
- data/lib/holistic/ruby/reference/repository.rb +19 -41
- data/lib/holistic/ruby/reference/store.rb +18 -0
- data/lib/holistic/ruby/scope/delete.rb +29 -0
- data/lib/holistic/ruby/scope/lexical.rb +11 -0
- data/lib/holistic/ruby/scope/list_references.rb +2 -2
- data/lib/holistic/ruby/scope/location.rb +9 -9
- data/lib/holistic/ruby/scope/outline.rb +8 -8
- data/lib/holistic/ruby/scope/record.rb +15 -51
- data/lib/holistic/ruby/scope/repository.rb +24 -25
- data/lib/holistic/ruby/scope/store.rb +45 -0
- data/lib/holistic/ruby/type_inference/processing_queue.rb +19 -0
- data/lib/holistic/ruby/type_inference/solve.rb +16 -25
- data/lib/holistic/ruby/type_inference/solve_pending_references.rb +3 -1
- data/lib/holistic/version.rb +1 -1
- metadata +13 -9
- data/lib/holistic/document/file.rb +0 -36
- data/lib/holistic/ruby/parser/table_of_contents.rb +0 -17
- data/lib/holistic/ruby/reference/register.rb +0 -15
- data/lib/holistic/ruby/reference/unregister.rb +0 -11
- data/lib/holistic/ruby/scope/register.rb +0 -31
- data/lib/holistic/ruby/scope/unregister.rb +0 -27
- data/lib/holistic/ruby/type_inference/conclusion.rb +0 -20
@@ -9,9 +9,9 @@ module Holistic::Ruby::Parser
|
|
9
9
|
|
10
10
|
attr_reader :scope_repository, :scope, :method_registration_mode
|
11
11
|
|
12
|
-
def initialize(scope_repository
|
12
|
+
def initialize(scope_repository:)
|
13
13
|
@scope_repository = scope_repository
|
14
|
-
@scope =
|
14
|
+
@scope = scope_repository.root
|
15
15
|
@constant_resolution_possibilities = ["::"]
|
16
16
|
@method_registration_mode = MethodRegistrationMode::INSTANCE_METHODS
|
17
17
|
end
|
@@ -25,8 +25,8 @@ module Holistic::Ruby::Parser
|
|
25
25
|
|
26
26
|
nesting.each do |name|
|
27
27
|
@scope =
|
28
|
-
::Holistic::Ruby::Scope::
|
29
|
-
|
28
|
+
::Holistic::Ruby::Scope::Store.call(
|
29
|
+
database: @scope_repository.database,
|
30
30
|
parent: @scope,
|
31
31
|
kind: ::Holistic::Ruby::Scope::Kind::MODULE,
|
32
32
|
name:,
|
@@ -52,8 +52,8 @@ module Holistic::Ruby::Parser
|
|
52
52
|
|
53
53
|
nesting.each do |name|
|
54
54
|
@scope =
|
55
|
-
::Holistic::Ruby::Scope::
|
56
|
-
|
55
|
+
::Holistic::Ruby::Scope::Store.call(
|
56
|
+
database: @scope_repository.database,
|
57
57
|
parent: @scope,
|
58
58
|
kind: ::Holistic::Ruby::Scope::Kind::CLASS,
|
59
59
|
name:,
|
@@ -79,8 +79,8 @@ module Holistic::Ruby::Parser
|
|
79
79
|
|
80
80
|
nesting.each do |name|
|
81
81
|
@scope =
|
82
|
-
::Holistic::Ruby::Scope::
|
83
|
-
|
82
|
+
::Holistic::Ruby::Scope::Store.call(
|
83
|
+
database: @scope_repository.database,
|
84
84
|
parent: @scope,
|
85
85
|
kind:,
|
86
86
|
name:,
|
@@ -4,59 +4,59 @@ module Holistic::Ruby::Parser
|
|
4
4
|
module LiveEditing::ProcessFileChanged
|
5
5
|
extend self
|
6
6
|
|
7
|
-
def call(application:,
|
7
|
+
def call(application:, file_path:, content:)
|
8
8
|
# TODO: do not build the AST twice
|
9
|
-
return unless HasValidSyntax[
|
9
|
+
return unless HasValidSyntax[content]
|
10
10
|
|
11
|
-
references_to_recalculate = identify_references_to_recalculate(application:,
|
11
|
+
references_to_recalculate = identify_references_to_recalculate(application:, file_path:)
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
delete_scopes_in_file(application:, file_path:)
|
14
|
+
delete_references_in_file(application:, file_path:)
|
15
15
|
|
16
|
-
parse_again(application:,
|
16
|
+
parse_again(application:, file_path:, content:)
|
17
17
|
|
18
18
|
recalculate_type_inference_for_references(application:, references: references_to_recalculate)
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
def identify_references_to_recalculate(application:,
|
23
|
+
def identify_references_to_recalculate(application:, file_path:)
|
24
24
|
# we need to reject references declared in the same because they're already going to be
|
25
25
|
# reparsed. If we don't do that, we'll end up with duplicated reference records.
|
26
26
|
|
27
27
|
application.references
|
28
|
-
.list_references_to_scopes_in_file(scopes: application.scopes, file_path:
|
29
|
-
.reject { _1.location.
|
28
|
+
.list_references_to_scopes_in_file(scopes: application.scopes, file_path: file_path)
|
29
|
+
.reject { _1.location.file.path == file_path }
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
application.scopes.list_scopes_in_file(
|
34
|
-
::Holistic::Ruby::Scope::
|
35
|
-
|
32
|
+
def delete_scopes_in_file(application:, file_path:)
|
33
|
+
application.scopes.list_scopes_in_file(file_path).each do |scope|
|
34
|
+
::Holistic::Ruby::Scope::Delete.call(
|
35
|
+
database: application.database,
|
36
36
|
fully_qualified_name: scope.fully_qualified_name,
|
37
|
-
file_path:
|
37
|
+
file_path:
|
38
38
|
)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
application.references.list_references_in_file(
|
44
|
-
::Holistic::Ruby::Reference::
|
45
|
-
|
42
|
+
def delete_references_in_file(application:, file_path:)
|
43
|
+
application.references.list_references_in_file(file_path).each do |reference|
|
44
|
+
::Holistic::Ruby::Reference::Delete.call(
|
45
|
+
database: application.database,
|
46
46
|
reference: reference
|
47
47
|
)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
def parse_again(application:,
|
52
|
-
ParseFile.call(application:,
|
51
|
+
def parse_again(application:, file_path:, content:)
|
52
|
+
ParseFile.call(application:, file_path:, content:)
|
53
53
|
|
54
54
|
::Holistic::Ruby::TypeInference::SolvePendingReferences.call(application:)
|
55
55
|
end
|
56
56
|
|
57
57
|
def recalculate_type_inference_for_references(application:, references:)
|
58
58
|
references.each do |reference|
|
59
|
-
reference.
|
59
|
+
application.database.disconnect(source: reference.referenced_scope, target: reference, name: :referenced_by, inverse_of: :referenced_scope)
|
60
60
|
|
61
61
|
::Holistic::Ruby::TypeInference::Solve.call(application:, reference:)
|
62
62
|
end
|
@@ -14,28 +14,28 @@ module Holistic::Ruby::Parser
|
|
14
14
|
|
15
15
|
visit_methods do
|
16
16
|
def visit_module(node)
|
17
|
-
declaration_node,
|
17
|
+
declaration_node, body_statements_node = node.child_nodes
|
18
18
|
|
19
19
|
nesting = NestingSyntax.build(declaration_node)
|
20
|
-
location = build_scope_location(declaration_node:, body_node:)
|
20
|
+
location = build_scope_location(declaration_node:, body_node: node)
|
21
21
|
|
22
22
|
@constant_resolution.register_child_module(nesting:, location:) do
|
23
|
-
visit(
|
23
|
+
visit(body_statements_node)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def visit_class(node)
|
28
|
-
declaration_node, superclass_node,
|
28
|
+
declaration_node, superclass_node, body_statements_node = node.child_nodes
|
29
29
|
|
30
30
|
if superclass_node
|
31
31
|
register_reference(nesting: NestingSyntax.build(superclass_node), location: build_location(superclass_node))
|
32
32
|
end
|
33
33
|
|
34
34
|
nesting = NestingSyntax.build(declaration_node)
|
35
|
-
location = build_scope_location(declaration_node:, body_node:)
|
35
|
+
location = build_scope_location(declaration_node:, body_node: node)
|
36
36
|
|
37
37
|
class_scope = @constant_resolution.register_child_class(nesting:, location:) do
|
38
|
-
visit(
|
38
|
+
visit(body_statements_node)
|
39
39
|
end
|
40
40
|
|
41
41
|
@application.extensions.dispatch(:class_scope_registered, { class_scope:, location: })
|
@@ -54,10 +54,10 @@ module Holistic::Ruby::Parser
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def visit_def(node)
|
57
|
-
instance_node, period_node, method_name_node, _params,
|
57
|
+
instance_node, period_node, method_name_node, _params, body_statements_node = node.child_nodes
|
58
58
|
|
59
59
|
nesting = NestingSyntax.new(method_name_node.value)
|
60
|
-
location = build_scope_location(declaration_node: method_name_node, body_node:)
|
60
|
+
location = build_scope_location(declaration_node: method_name_node, body_node: node)
|
61
61
|
|
62
62
|
kind =
|
63
63
|
if instance_node.present? && instance_node.child_nodes.first.value == "self"
|
@@ -69,7 +69,7 @@ module Holistic::Ruby::Parser
|
|
69
69
|
end
|
70
70
|
|
71
71
|
@constant_resolution.register_child_method(nesting:, location:, kind:) do
|
72
|
-
visit(
|
72
|
+
visit(body_statements_node)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
@@ -80,8 +80,9 @@ module Holistic::Ruby::Parser
|
|
80
80
|
resolution_possibilities: @constant_resolution.current
|
81
81
|
)
|
82
82
|
|
83
|
-
::Holistic::Ruby::Reference::
|
84
|
-
|
83
|
+
::Holistic::Ruby::Reference::Store.call(
|
84
|
+
database: @application.database,
|
85
|
+
processing_queue: @application.type_inference_processing_queue,
|
85
86
|
scope: @constant_resolution.scope,
|
86
87
|
clues: [method_call_clue],
|
87
88
|
location: build_location(node)
|
@@ -114,8 +115,9 @@ module Holistic::Ruby::Parser
|
|
114
115
|
resolution_possibilities: @constant_resolution.current
|
115
116
|
)
|
116
117
|
|
117
|
-
::Holistic::Ruby::Reference::
|
118
|
-
|
118
|
+
::Holistic::Ruby::Reference::Store.call(
|
119
|
+
database: @application.database,
|
120
|
+
processing_queue: @application.type_inference_processing_queue,
|
119
121
|
scope: @constant_resolution.scope,
|
120
122
|
clues: [method_call_clue],
|
121
123
|
location: build_location(method_name_node || instance_node)
|
@@ -151,8 +153,8 @@ module Holistic::Ruby::Parser
|
|
151
153
|
location = build_scope_location(declaration_node: assign_node, body_node:)
|
152
154
|
|
153
155
|
lambda_scope =
|
154
|
-
::Holistic::Ruby::Scope::
|
155
|
-
|
156
|
+
::Holistic::Ruby::Scope::Store.call(
|
157
|
+
database: @application.database,
|
156
158
|
parent: @constant_resolution.scope,
|
157
159
|
kind: ::Holistic::Ruby::Scope::Kind::LAMBDA,
|
158
160
|
name: assign_node.child_nodes.first.value,
|
@@ -185,8 +187,9 @@ module Holistic::Ruby::Parser
|
|
185
187
|
resolution_possibilities: @constant_resolution.current
|
186
188
|
)
|
187
189
|
|
188
|
-
::Holistic::Ruby::Reference::
|
189
|
-
|
190
|
+
::Holistic::Ruby::Reference::Store.call(
|
191
|
+
database: @application.database,
|
192
|
+
processing_queue: @application.type_inference_processing_queue,
|
190
193
|
scope: @constant_resolution.scope,
|
191
194
|
clues: [clue],
|
192
195
|
location:
|
@@ -209,13 +212,7 @@ module Holistic::Ruby::Parser
|
|
209
212
|
start_column = node.location.start_column
|
210
213
|
end_column = node.location.end_column
|
211
214
|
|
212
|
-
|
213
|
-
# It sets the end_column lower than the start_column.
|
214
|
-
if start_line == end_line && start_column > end_column
|
215
|
-
start_column, end_column = end_column, start_column
|
216
|
-
end
|
217
|
-
|
218
|
-
::Holistic::Document::Location.new(file_path: file.path, start_line:, start_column:, end_line:, end_column:)
|
215
|
+
::Holistic::Document::Location.new(file:, start_line:, start_column:, end_line:, end_column:)
|
219
216
|
end
|
220
217
|
end
|
221
218
|
end
|
data/lib/holistic/ruby/parser.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Holistic::Ruby::Parser
|
4
|
-
HasValidSyntax = ->(
|
5
|
-
::SyntaxTree.parse(
|
4
|
+
HasValidSyntax = ->(content) do
|
5
|
+
::SyntaxTree.parse(content)
|
6
6
|
|
7
7
|
true
|
8
8
|
rescue ::SyntaxTree::Parser::ParseError
|
9
9
|
false
|
10
10
|
end
|
11
11
|
|
12
|
-
ParseFile = ->(application:,
|
13
|
-
program = ::SyntaxTree.parse(
|
12
|
+
ParseFile = ->(application:, file_path:, content:) do
|
13
|
+
program = ::SyntaxTree.parse(content)
|
14
14
|
|
15
|
-
constant_resolution = ConstantResolution.new(
|
16
|
-
|
17
|
-
|
18
|
-
)
|
15
|
+
constant_resolution = ConstantResolution.new(scope_repository: application.scopes)
|
16
|
+
|
17
|
+
file = ::Holistic::Document::File::Store.call(database: application.database, file_path:)
|
19
18
|
|
20
19
|
visitor = ProgramVisitor.new(application:, constant_resolution:, file:)
|
21
20
|
|
@@ -26,9 +25,7 @@ module Holistic::Ruby::Parser
|
|
26
25
|
|
27
26
|
ParseDirectory = ->(application:, directory_path:) do
|
28
27
|
::Dir.glob("#{directory_path}/**/*.rb").map do |file_path|
|
29
|
-
|
30
|
-
|
31
|
-
ParseFile[application:, file:]
|
28
|
+
ParseFile.call(application:, file_path:, content: ::File.read(file_path))
|
32
29
|
end
|
33
30
|
end
|
34
31
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Reference
|
4
|
+
module Delete
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, reference:)
|
8
|
+
database.delete(reference.identifier)
|
9
|
+
|
10
|
+
database.disconnect(source: reference.location.file, target: reference, name: :defines_references, inverse_of: :reference_defined_in_file)
|
11
|
+
database.disconnect(source: reference.located_in_scope, target: reference, name: :contains_many_references, inverse_of: :located_in_scope)
|
12
|
+
|
13
|
+
if reference.referenced_scope
|
14
|
+
database.disconnect(source: reference.referenced_scope, target: reference, name: :referenced_by, inverse_of: :referenced_scope)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -8,9 +8,9 @@ module Holistic::Ruby::Reference
|
|
8
8
|
reference = application.references.find_by_cursor(cursor)
|
9
9
|
|
10
10
|
return :not_found if reference.nil?
|
11
|
-
return :could_not_find_referenced_scope if reference.
|
11
|
+
return :could_not_find_referenced_scope if reference.referenced_scope.nil?
|
12
12
|
|
13
|
-
referenced_scope =
|
13
|
+
referenced_scope = reference.referenced_scope
|
14
14
|
|
15
15
|
[:referenced_scope_found, {reference:, referenced_scope:}]
|
16
16
|
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Holistic::Ruby::Reference
|
4
|
-
Record
|
5
|
-
:
|
6
|
-
:clues
|
7
|
-
:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def identifier = location.identifier
|
4
|
+
class Record < ::Holistic::Database::Node
|
5
|
+
def identifier = attr(:identifier)
|
6
|
+
def clues = attr(:clues)
|
7
|
+
def location = attr(:location)
|
8
|
+
|
9
|
+
def referenced_scope = has_one(:referenced_scope)
|
10
|
+
def located_in_scope = has_one(:located_in_scope)
|
12
11
|
end
|
13
12
|
end
|
@@ -2,68 +2,46 @@
|
|
2
2
|
|
3
3
|
module Holistic::Ruby::Reference
|
4
4
|
class Repository
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :database
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
:type_inference_status,
|
10
|
-
:referenced_scope_fully_qualified_name
|
11
|
-
].freeze
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@table = ::Holistic::Database::Table.new(primary_attribute: :identifier, indices: INDICES)
|
15
|
-
end
|
16
|
-
|
17
|
-
def register_reference(reference)
|
18
|
-
table.update({
|
19
|
-
reference:,
|
20
|
-
identifier: reference.identifier,
|
21
|
-
file_path: reference.location.file_path,
|
22
|
-
type_inference_status: reference.conclusion.status,
|
23
|
-
referenced_scope_fully_qualified_name: reference.conclusion.dependency_identifier
|
24
|
-
})
|
7
|
+
def initialize(database:)
|
8
|
+
@database = database
|
25
9
|
end
|
26
10
|
|
27
11
|
def find_by_cursor(cursor)
|
28
|
-
|
29
|
-
|
12
|
+
@database.find(cursor.file_path)&.then do |file|
|
13
|
+
file.defines_references.find do |reference|
|
14
|
+
reference.location.contains?(cursor)
|
15
|
+
end
|
30
16
|
end
|
31
|
-
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def list_references_to(fully_qualified_scope_name)
|
36
|
-
table.filter(:referenced_scope_fully_qualified_name, fully_qualified_scope_name).map { _1[:reference] }
|
37
17
|
end
|
38
18
|
|
39
19
|
def list_references_in_file(file_path)
|
40
|
-
|
20
|
+
@database.find(file_path)&.defines_references || []
|
41
21
|
end
|
42
22
|
|
43
23
|
def list_references_to_scopes_in_file(scopes:, file_path:)
|
44
|
-
|
45
|
-
|
24
|
+
references = @database.find(file_path)&.defines_scopes&.flat_map do |scope|
|
25
|
+
scope.referenced_by
|
46
26
|
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def list_references_pending_type_inference_conclusion
|
50
|
-
table.filter(:type_inference_status, ::Holistic::Ruby::TypeInference::STATUS_PENDING).map { _1[:reference] }
|
51
|
-
end
|
52
27
|
|
53
|
-
|
54
|
-
table.delete(identifier)
|
28
|
+
references || []
|
55
29
|
end
|
56
30
|
|
57
31
|
concerning :TestHelpers do
|
32
|
+
def all
|
33
|
+
@database.all.filter { _1.is_a?(Record) }
|
34
|
+
end
|
35
|
+
|
58
36
|
def find_reference_to(scope_name)
|
59
|
-
|
60
|
-
|
37
|
+
all.find do |node|
|
38
|
+
node.referenced_scope&.fully_qualified_name == scope_name || node.clues&.find { _1.to_s == scope_name }
|
61
39
|
end
|
62
40
|
end
|
63
41
|
|
64
42
|
def find_by_code_content(code_content)
|
65
|
-
|
66
|
-
|
43
|
+
all.find do |node|
|
44
|
+
node.clues&.find { _1.to_s == code_content }
|
67
45
|
end
|
68
46
|
end
|
69
47
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Reference
|
4
|
+
module Store
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, processing_queue:, scope:, clues:, location:)
|
8
|
+
record = Record.new(location.identifier, { identifier: location.identifier, clues:, location: })
|
9
|
+
|
10
|
+
reference = database.store(location.identifier, record)
|
11
|
+
|
12
|
+
database.connect(source: scope, target: reference, name: :contains_many_references, inverse_of: :located_in_scope)
|
13
|
+
database.connect(source: location.file, target: reference, name: :defines_references, inverse_of: :reference_defined_in_file)
|
14
|
+
|
15
|
+
processing_queue.push(reference)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope
|
4
|
+
module Delete
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(database:, fully_qualified_name:, file_path:)
|
8
|
+
scope = database.find(fully_qualified_name)
|
9
|
+
|
10
|
+
return :scope_not_found if scope.nil?
|
11
|
+
|
12
|
+
location_to_remove = scope.locations.find { |scope_location| scope_location.declaration.file.path == file_path }
|
13
|
+
|
14
|
+
return :scope_not_defined_in_speciefied_file if location_to_remove.nil?
|
15
|
+
|
16
|
+
scope.locations.delete(location_to_remove)
|
17
|
+
|
18
|
+
database.disconnect(source: location_to_remove.declaration.file, target: scope, name: :defines_scopes, inverse_of: :scope_defined_in_file)
|
19
|
+
|
20
|
+
if scope.locations.empty?
|
21
|
+
database.disconnect(source: scope.parent, target: scope, name: :children, inverse_of: :parent)
|
22
|
+
|
23
|
+
database.delete(fully_qualified_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
:definition_unregistered
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Holistic::Ruby::Scope::Lexical
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def descendant?(child:, parent:)
|
7
|
+
child_parent = child.parent
|
8
|
+
|
9
|
+
child_parent.present? && (child_parent == parent || descendant?(child: child_parent, parent:))
|
10
|
+
end
|
11
|
+
end
|
@@ -5,7 +5,7 @@ module Holistic::Ruby::Scope
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
QueryReferencesRecursively = ->(application, scope) do
|
8
|
-
references_to_scope =
|
8
|
+
references_to_scope = scope.referenced_by
|
9
9
|
|
10
10
|
references_to_child_scopes = scope.children.flat_map { QueryReferencesRecursively.call(application, _1) }
|
11
11
|
|
@@ -14,7 +14,7 @@ module Holistic::Ruby::Scope
|
|
14
14
|
|
15
15
|
Relevance = ->(reference) do
|
16
16
|
# TODO: should the location answer the kind of file it is? application code, config, spec, etc. Not sure.
|
17
|
-
looks_like_a_spec = reference.location.
|
17
|
+
looks_like_a_spec = reference.location.file.path.include?("_spec.rb") || reference.location.file.path.include?("_test.rb")
|
18
18
|
|
19
19
|
looks_like_a_spec ? 1 : 0
|
20
20
|
end
|
@@ -3,13 +3,11 @@
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
4
|
class Location
|
5
5
|
class Collection
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :scope_name, :items
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
11
|
-
@scope = scope
|
12
|
-
@items = location.nil? ? [] : [location]
|
8
|
+
def initialize(scope_name)
|
9
|
+
@scope_name = scope_name
|
10
|
+
@items = []
|
13
11
|
end
|
14
12
|
|
15
13
|
def main
|
@@ -18,17 +16,19 @@ module Holistic::Ruby::Scope
|
|
18
16
|
|
19
17
|
delegate :<<, to: :items
|
20
18
|
delegate :each, to: :items
|
19
|
+
delegate :find, to: :items
|
21
20
|
delegate :map, to: :items
|
22
|
-
delegate :
|
21
|
+
delegate :delete, to: :items
|
23
22
|
delegate :any?, to: :items
|
23
|
+
delegate :empty?, to: :items
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def location_matching_scope_name
|
28
|
-
scope_name_in_snake_case =
|
28
|
+
scope_name_in_snake_case = scope_name.underscore
|
29
29
|
|
30
30
|
items.find do |location|
|
31
|
-
::File.basename(location.declaration.
|
31
|
+
::File.basename(location.declaration.file.path) == "#{scope_name_in_snake_case}.rb"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -18,18 +18,18 @@ module Holistic::Ruby::Scope
|
|
18
18
|
|
19
19
|
QueryDependenciesRecursively = ->(application, outlined_scope, scope) do
|
20
20
|
is_local_dependency = ->(reference) do
|
21
|
-
scope =
|
21
|
+
scope = reference.referenced_scope
|
22
22
|
|
23
|
-
scope
|
23
|
+
scope == outlined_scope || Lexical.descendant?(child: scope, parent: outlined_scope)
|
24
24
|
end
|
25
25
|
|
26
26
|
dependencies = []
|
27
27
|
|
28
28
|
scope.locations.each do |scope_location|
|
29
29
|
application.references
|
30
|
-
.list_references_in_file(scope_location.declaration.
|
31
|
-
.filter { |reference| reference.
|
32
|
-
.filter { |reference| reference.
|
30
|
+
.list_references_in_file(scope_location.declaration.file.path)
|
31
|
+
.filter { |reference| reference.located_in_scope == scope }
|
32
|
+
.filter { |reference| reference.referenced_scope.present? }
|
33
33
|
.reject(&is_local_dependency)
|
34
34
|
.tap { dependencies.concat(_1) }
|
35
35
|
end
|
@@ -40,11 +40,11 @@ module Holistic::Ruby::Scope
|
|
40
40
|
def call(application:, scope:)
|
41
41
|
declarations = QueryChildScopesRecursively.call(application, scope).sort_by { _1.fully_qualified_name }
|
42
42
|
|
43
|
-
dependencies = QueryDependenciesRecursively.call(application, scope, scope).uniq
|
43
|
+
dependencies = QueryDependenciesRecursively.call(application, scope, scope).uniq
|
44
44
|
|
45
|
-
references =
|
45
|
+
references = scope.referenced_by
|
46
46
|
|
47
|
-
dependants = references.map { |reference| reference.
|
47
|
+
dependants = references.map { |reference| reference.located_in_scope }.uniq
|
48
48
|
|
49
49
|
Result.new(declarations:, dependencies:, references:, dependants:)
|
50
50
|
end
|
@@ -1,56 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
|
-
class Record
|
5
|
-
|
6
|
-
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
case kind
|
20
|
-
when Kind::INSTANCE_METHOD then "#"
|
21
|
-
when Kind::CLASS_METHOD then "."
|
22
|
-
else "::"
|
23
|
-
end
|
24
|
-
|
25
|
-
"#{parent.fully_qualified_name}#{separator}#{name}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def root?
|
29
|
-
parent.nil?
|
30
|
-
end
|
31
|
-
|
32
|
-
def lambda?
|
33
|
-
kind == Kind::LAMBDA
|
34
|
-
end
|
35
|
-
|
36
|
-
def class?
|
37
|
-
kind == Kind::CLASS
|
38
|
-
end
|
39
|
-
|
40
|
-
def module?
|
41
|
-
kind == Kind::MODULE
|
42
|
-
end
|
43
|
-
|
44
|
-
def instance_method?
|
45
|
-
kind == Kind::INSTANCE_METHOD
|
46
|
-
end
|
47
|
-
|
48
|
-
def class_method?
|
49
|
-
kind == Kind::CLASS_METHOD
|
50
|
-
end
|
51
|
-
|
52
|
-
def descendant?(other)
|
53
|
-
parent.present? && (parent == other || parent.descendant?(other))
|
54
|
-
end
|
4
|
+
class Record < ::Holistic::Database::Node
|
5
|
+
def fully_qualified_name = attr(:fully_qualified_name)
|
6
|
+
def locations = attr(:locations)
|
7
|
+
def name = attr(:name)
|
8
|
+
def kind = attr(:kind)
|
9
|
+
|
10
|
+
def parent = has_one(:parent)
|
11
|
+
def children = has_many(:children)
|
12
|
+
def referenced_by = has_many(:referenced_by)
|
13
|
+
|
14
|
+
def root? = kind == Kind::ROOT
|
15
|
+
def class? = kind == Kind::CLASS
|
16
|
+
def class_method? = kind == Kind::CLASS_METHOD
|
17
|
+
def instance_method? = kind == Kind::INSTANCE_METHOD
|
18
|
+
def method? = class_method? || instance_method?
|
55
19
|
end
|
56
20
|
end
|