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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/holistic/application.rb +12 -4
  4. data/lib/holistic/database/migrations.rb +20 -0
  5. data/lib/holistic/database/node.rb +29 -0
  6. data/lib/holistic/database/table.rb +53 -53
  7. data/lib/holistic/document/file/record.rb +10 -0
  8. data/lib/holistic/document/file/repository.rb +24 -0
  9. data/lib/holistic/document/file/store.rb +13 -0
  10. data/lib/holistic/document/location.rb +4 -6
  11. data/lib/holistic/document/unsaved/record.rb +0 -4
  12. data/lib/holistic/extensions/ruby/stdlib.rb +6 -6
  13. data/lib/holistic/language_server/requests/lifecycle/initialize.rb +5 -10
  14. data/lib/holistic/language_server/requests/text_document/completion.rb +5 -4
  15. data/lib/holistic/language_server/requests/text_document/did_close.rb +5 -9
  16. data/lib/holistic/language_server/requests/text_document/did_open.rb +1 -0
  17. data/lib/holistic/language_server/requests/text_document/did_save.rb +5 -9
  18. data/lib/holistic/language_server/requests/text_document/find_references.rb +7 -4
  19. data/lib/holistic/language_server/requests/text_document/go_to_definition.rb +3 -2
  20. data/lib/holistic/ruby/autocompletion/suggest.rb +40 -20
  21. data/lib/holistic/ruby/parser/constant_resolution.rb +8 -8
  22. data/lib/holistic/ruby/parser/live_editing/process_file_changed.rb +21 -21
  23. data/lib/holistic/ruby/parser/program_visitor.rb +21 -24
  24. data/lib/holistic/ruby/parser.rb +8 -11
  25. data/lib/holistic/ruby/reference/delete.rb +18 -0
  26. data/lib/holistic/ruby/reference/find_referenced_scope.rb +2 -2
  27. data/lib/holistic/ruby/reference/record.rb +7 -8
  28. data/lib/holistic/ruby/reference/repository.rb +19 -41
  29. data/lib/holistic/ruby/reference/store.rb +18 -0
  30. data/lib/holistic/ruby/scope/delete.rb +29 -0
  31. data/lib/holistic/ruby/scope/lexical.rb +11 -0
  32. data/lib/holistic/ruby/scope/list_references.rb +2 -2
  33. data/lib/holistic/ruby/scope/location.rb +9 -9
  34. data/lib/holistic/ruby/scope/outline.rb +8 -8
  35. data/lib/holistic/ruby/scope/record.rb +15 -51
  36. data/lib/holistic/ruby/scope/repository.rb +24 -25
  37. data/lib/holistic/ruby/scope/store.rb +45 -0
  38. data/lib/holistic/ruby/type_inference/processing_queue.rb +19 -0
  39. data/lib/holistic/ruby/type_inference/solve.rb +16 -25
  40. data/lib/holistic/ruby/type_inference/solve_pending_references.rb +3 -1
  41. data/lib/holistic/version.rb +1 -1
  42. metadata +13 -9
  43. data/lib/holistic/document/file.rb +0 -36
  44. data/lib/holistic/ruby/parser/table_of_contents.rb +0 -17
  45. data/lib/holistic/ruby/reference/register.rb +0 -15
  46. data/lib/holistic/ruby/reference/unregister.rb +0 -11
  47. data/lib/holistic/ruby/scope/register.rb +0 -31
  48. data/lib/holistic/ruby/scope/unregister.rb +0 -27
  49. 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:, root_scope:)
12
+ def initialize(scope_repository:)
13
13
  @scope_repository = scope_repository
14
- @scope = root_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::Register.call(
29
- repository: @scope_repository,
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::Register.call(
56
- repository: @scope_repository,
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::Register.call(
83
- repository: @scope_repository,
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:, file:)
7
+ def call(application:, file_path:, content:)
8
8
  # TODO: do not build the AST twice
9
- return unless HasValidSyntax[file:]
9
+ return unless HasValidSyntax[content]
10
10
 
11
- references_to_recalculate = identify_references_to_recalculate(application:, file:)
11
+ references_to_recalculate = identify_references_to_recalculate(application:, file_path:)
12
12
 
13
- unregister_scopes_in_file(application:, file:)
14
- unregsiter_references_in_file(application:, file:)
13
+ delete_scopes_in_file(application:, file_path:)
14
+ delete_references_in_file(application:, file_path:)
15
15
 
16
- parse_again(application:, file:)
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:, file:)
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: file.path)
29
- .reject { _1.location.file_path == file.path }
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 unregister_scopes_in_file(application:, file:)
33
- application.scopes.list_scopes_in_file(file.path).each do |scope|
34
- ::Holistic::Ruby::Scope::Unregister.call(
35
- repository: application.scopes,
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: file.path
37
+ file_path:
38
38
  )
39
39
  end
40
40
  end
41
41
 
42
- def unregsiter_references_in_file(application:, file:)
43
- application.references.list_references_in_file(file.path).each do |reference|
44
- ::Holistic::Ruby::Reference::Unregister.call(
45
- repository: application.references,
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:, file:)
52
- ParseFile.call(application:, file:)
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.conclusion = ::Holistic::Ruby::TypeInference::Conclusion.pending
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, body_node = node.child_nodes
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(body_node)
23
+ visit(body_statements_node)
24
24
  end
25
25
  end
26
26
 
27
27
  def visit_class(node)
28
- declaration_node, superclass_node, body_node = node.child_nodes
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(body_node)
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, body_node = node.child_nodes
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(body_node)
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::Register.call(
84
- repository: @application.references,
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::Register.call(
118
- repository: @application.references,
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::Register.call(
155
- repository: @application.scopes,
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::Register.call(
189
- repository: @application.references,
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
- # syntax_tree seems to have a bug with the bodystmt node.
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
@@ -1,21 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Holistic::Ruby::Parser
4
- HasValidSyntax = ->(file:) do
5
- ::SyntaxTree.parse(file.read)
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:, file:) do
13
- program = ::SyntaxTree.parse(file.read)
12
+ ParseFile = ->(application:, file_path:, content:) do
13
+ program = ::SyntaxTree.parse(content)
14
14
 
15
- constant_resolution = ConstantResolution.new(
16
- scope_repository: application.scopes,
17
- root_scope: application.root_scope
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
- file = ::Holistic::Document::File.new(path: file_path)
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.conclusion.dependency_identifier.nil?
11
+ return :could_not_find_referenced_scope if reference.referenced_scope.nil?
12
12
 
13
- referenced_scope = application.scopes.find_by_fully_qualified_name(reference.conclusion.dependency_identifier)
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 = ::Struct.new(
5
- :scope,
6
- :clues,
7
- :conclusion,
8
- :location,
9
- keyword_init: true
10
- ) do
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 :table
5
+ attr_reader :database
6
6
 
7
- INDICES = [
8
- :file_path,
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
- table.filter(:file_path, cursor.file_path).map { _1[:reference] }.each do |reference|
29
- return reference if reference.location.contains?(cursor)
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
- table.filter(:file_path, file_path).map { _1[:reference] }
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
- scopes.list_scopes_in_file(file_path).flat_map do |scope|
45
- table.filter(:referenced_scope_fully_qualified_name, scope.fully_qualified_name).map { _1[:reference] }
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
- def delete(identifier)
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
- table.all.map { _1[:reference] }.find do |reference|
60
- reference.conclusion.dependency_identifier == scope_name || reference.clues.find { _1.to_s == scope_name }
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
- table.all.map { _1[:reference] }.find do |reference|
66
- reference.clues.find { _1.to_s == code_content }
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 = application.references.list_references_to(scope.fully_qualified_name)
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.file_path.include?("_spec.rb") || reference.location.file_path.include?("_test.rb")
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 :scope, :items
6
+ attr_reader :scope_name, :items
7
7
 
8
- def initialize(scope, location)
9
- raise ::ArgumentError if location.present? && !location.is_a?(Location)
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 :reject!, to: :items
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 = scope.name.underscore
28
+ scope_name_in_snake_case = scope_name.underscore
29
29
 
30
30
  items.find do |location|
31
- ::File.basename(location.declaration.file_path) == "#{scope_name_in_snake_case}.rb"
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 = application.scopes.find_by_fully_qualified_name(reference.conclusion.dependency_identifier)
21
+ scope = reference.referenced_scope
22
22
 
23
- scope.eql?(outlined_scope) || scope.descendant?(outlined_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.file_path)
31
- .filter { |reference| reference.scope == scope }
32
- .filter { |reference| reference.conclusion.dependency_identifier.present? }
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 { _1.conclusion.dependency_identifier }
43
+ dependencies = QueryDependenciesRecursively.call(application, scope, scope).uniq
44
44
 
45
- references = application.references.list_references_to(scope.fully_qualified_name)
45
+ references = scope.referenced_by
46
46
 
47
- dependants = references.map { |reference| reference.scope }.uniq
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
- attr_reader :kind, :name, :parent, :children, :locations
6
-
7
- def initialize(kind:, name:, parent:, location: nil)
8
- @kind = kind
9
- @name = name
10
- @parent = parent
11
- @locations = Location::Collection.new(self, location)
12
- @children = []
13
- end
14
-
15
- def fully_qualified_name
16
- return "" if root?
17
-
18
- separator =
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