holistic-ruby 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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