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
@@ -2,48 +2,47 @@
2
2
 
3
3
  module Holistic::Ruby::Scope
4
4
  class Repository
5
- attr_reader :table
6
-
7
- def initialize
8
- @table = ::Holistic::Database::Table.new(primary_attribute: :fully_qualified_name, indices: [:file_paths])
9
- end
10
-
11
- def register_scope(scope)
12
- table.update({
13
- fully_qualified_name: scope.fully_qualified_name,
14
- file_paths: scope.locations.map { |scope_location| scope_location.declaration.file_path },
15
- scope:
16
- })
5
+ attr_reader :database, :root
6
+
7
+ def initialize(database:)
8
+ @root = database.store(
9
+ "root_scope",
10
+ Record.new("root_scope", {
11
+ fully_qualified_name: "::",
12
+ name: "::",
13
+ kind: Kind::ROOT
14
+ })
15
+ )
16
+
17
+ @database = database
17
18
  end
18
19
 
19
- def find_by_fully_qualified_name(fully_qualified_name)
20
- table.find(fully_qualified_name).try(:dig, :scope)
20
+ def find(fully_qualified_name)
21
+ database.find(fully_qualified_name)
21
22
  end
22
23
 
23
24
  def find_by_cursor(cursor)
24
- table.filter(:file_paths, cursor.file_path).map { _1[:scope] }.each do |scope|
25
- return scope if scope.locations.any? { _1.declaration.contains?(cursor) }
25
+ database.find(cursor.file_path)&.then do |file|
26
+ file.defines_scopes.find do |scope|
27
+ scope.locations.any? { _1.declaration.contains?(cursor) }
28
+ end
26
29
  end
27
-
28
- nil
29
30
  end
30
31
 
31
32
  def find_inner_most_scope_by_cursor(cursor)
32
- scopes = table.filter(:file_paths, cursor.file_path).map { _1[:scope] }
33
+ file = database.find(cursor.file_path)
33
34
 
34
- matching_scopes = scopes.filter do |scope|
35
+ return nil if file.nil?
36
+
37
+ matching_scopes = file.defines_scopes.filter do |scope|
35
38
  scope.locations.any? { _1.body.contains?(cursor) }
36
39
  end
37
40
 
38
41
  matching_scopes.last
39
42
  end
40
43
 
41
- def delete_by_fully_qualified_name(fully_qualified_name)
42
- table.delete(fully_qualified_name)
43
- end
44
-
45
44
  def list_scopes_in_file(file_path)
46
- table.filter(:file_paths, file_path).map { _1[:scope] }
45
+ database.find(file_path)&.defines_scopes || []
47
46
  end
48
47
  end
49
48
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Holistic::Ruby::Scope
4
+ module Store
5
+ extend self
6
+
7
+ def call(database:, parent:, kind:, name:, location:)
8
+ fully_qualified_name = build_fully_qualified_name(parent:, kind:, name:)
9
+
10
+ child_scope = database.find(fully_qualified_name)
11
+
12
+ if child_scope.nil?
13
+ record = Record.new(fully_qualified_name, { fully_qualified_name:, name:, kind:, locations: Location::Collection.new(name) })
14
+
15
+ child_scope = database.store(fully_qualified_name, record)
16
+ end
17
+
18
+ child_scope.locations << location
19
+
20
+ database.connect(source: parent, target: child_scope, name: :children, inverse_of: :parent)
21
+ database.connect(source: location.declaration.file, target: child_scope, name: :defines_scopes, inverse_of: :scope_defined_in_file)
22
+
23
+ child_scope
24
+ end
25
+
26
+ private
27
+
28
+ def build_fully_qualified_name(parent:, kind:, name:)
29
+ parent_fully_qualified_name =
30
+ case parent.kind
31
+ when Kind::ROOT then ""
32
+ else parent.fully_qualified_name
33
+ end
34
+
35
+ separator =
36
+ case kind
37
+ when Kind::INSTANCE_METHOD then "#"
38
+ when Kind::CLASS_METHOD then "."
39
+ else "::"
40
+ end
41
+
42
+ "#{parent_fully_qualified_name}#{separator}#{name}"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Holistic::Ruby::TypeInference::ProcessingQueue
4
+ def initialize
5
+ @queue = ::Queue.new
6
+ end
7
+
8
+ def push(reference)
9
+ @queue.push(reference)
10
+ end
11
+
12
+ def pop
13
+ @queue.pop(true)
14
+ end
15
+
16
+ def empty?
17
+ @queue.empty?
18
+ end
19
+ end
@@ -5,12 +5,13 @@ module Holistic::Ruby::TypeInference
5
5
  extend self
6
6
 
7
7
  def call(application:, reference:)
8
- reference.conclusion =
8
+ referenced_scope =
9
9
  solve_scope_reference(application:, reference:) ||
10
- solve_method_call(application:, reference:) ||
11
- Conclusion.unresolved
10
+ solve_method_call(application:, reference:)
12
11
 
13
- application.references.register_reference(reference)
12
+ if referenced_scope
13
+ application.database.connect(source: reference, target: referenced_scope, name: :referenced_scope, inverse_of: :referenced_by)
14
+ end
14
15
  end
15
16
 
16
17
  private
@@ -23,30 +24,21 @@ module Holistic::Ruby::TypeInference
23
24
 
24
25
  scope_reference_clue = reference.clues.first
25
26
 
26
- referenced_scope = resolve_scope(
27
+ resolve_scope(
27
28
  application:,
28
29
  nesting: scope_reference_clue.nesting,
29
30
  resolution_possibilities: scope_reference_clue.resolution_possibilities
30
31
  )
31
-
32
- if referenced_scope.present?
33
- return Conclusion.done(referenced_scope.fully_qualified_name)
34
- end
35
-
36
- nil
37
32
  end
38
33
 
39
34
  SolveMethodCallInCurrentScope = ->(application:, reference:, method_call_clue:) do
40
- referenced_method =
41
- if reference.scope.class_method?
42
- resolve_class_method(application:, scope: reference.scope.parent, method_name: method_call_clue.method_name)
43
- elsif reference.scope.instance_method? && reference.scope.parent.present?
44
- resolve_instance_method(application:, scope: reference.scope.parent, method_name: method_call_clue.method_name)
45
- end
35
+ scope = reference.located_in_scope
46
36
 
47
- return if referenced_method.nil?
48
-
49
- Conclusion.done(referenced_method.fully_qualified_name)
37
+ if scope.class_method?
38
+ resolve_class_method(application:, scope: scope.parent, method_name: method_call_clue.method_name)
39
+ elsif scope.instance_method? && scope.parent.present?
40
+ resolve_instance_method(application:, scope: scope.parent, method_name: method_call_clue.method_name)
41
+ end
50
42
  end
51
43
 
52
44
  SolveMethodCallInSpecifiedScope = ->(application:, reference:, method_call_clue:) do
@@ -59,9 +51,8 @@ module Holistic::Ruby::TypeInference
59
51
  return if referenced_scope.nil?
60
52
 
61
53
  referenced_method = application.extensions.dispatch(:resolve_method_call_known_scope, { reference:, referenced_scope:, method_call_clue: })
62
- referenced_method ||= resolve_class_method(application:, scope: referenced_scope, method_name: method_call_clue.method_name)
63
54
 
64
- Conclusion.done(referenced_method.fully_qualified_name) if referenced_method.present?
55
+ referenced_method || resolve_class_method(application:, scope: referenced_scope, method_name: method_call_clue.method_name)
65
56
  end
66
57
 
67
58
  SolveMethodCallInLocalVariable = ->(application:, reference:, method_call_clue:) do
@@ -98,7 +89,7 @@ module Holistic::Ruby::TypeInference
98
89
  "#{resolution_candidate}::#{nesting.to_s}"
99
90
  end
100
91
 
101
- scope = application.scopes.find_by_fully_qualified_name(fully_qualified_scope_name)
92
+ scope = application.scopes.find(fully_qualified_scope_name)
102
93
 
103
94
  return scope if scope.present?
104
95
  end
@@ -109,13 +100,13 @@ module Holistic::Ruby::TypeInference
109
100
  def resolve_instance_method(application:, scope:, method_name:)
110
101
  method_fully_qualified_name = "#{scope.fully_qualified_name}##{method_name}"
111
102
 
112
- application.scopes.find_by_fully_qualified_name(method_fully_qualified_name)
103
+ application.scopes.find(method_fully_qualified_name)
113
104
  end
114
105
 
115
106
  def resolve_class_method(application:, scope:, method_name:)
116
107
  method_fully_qualified_name = "#{scope.fully_qualified_name}.#{method_name}"
117
108
 
118
- application.scopes.find_by_fully_qualified_name(method_fully_qualified_name)
109
+ application.scopes.find(method_fully_qualified_name)
119
110
  end
120
111
  end
121
112
  end
@@ -5,7 +5,9 @@ module Holistic::Ruby::TypeInference
5
5
  extend self
6
6
 
7
7
  def call(application:)
8
- application.references.list_references_pending_type_inference_conclusion.each do |reference|
8
+ until application.type_inference_processing_queue.empty?
9
+ reference = application.type_inference_processing_queue.pop
10
+
9
11
  Solve.call(application:, reference:)
10
12
  end
11
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Holistic
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.6"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: holistic-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luiz Vasconcellos
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-20 00:00:00.000000000 Z
11
+ date: 2023-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: syntax_tree
@@ -73,9 +73,13 @@ files:
73
73
  - lib/holistic.rb
74
74
  - lib/holistic/application.rb
75
75
  - lib/holistic/background_process.rb
76
+ - lib/holistic/database/migrations.rb
77
+ - lib/holistic/database/node.rb
76
78
  - lib/holistic/database/table.rb
77
79
  - lib/holistic/document/cursor.rb
78
- - lib/holistic/document/file.rb
80
+ - lib/holistic/document/file/record.rb
81
+ - lib/holistic/document/file/repository.rb
82
+ - lib/holistic/document/file/store.rb
79
83
  - lib/holistic/document/location.rb
80
84
  - lib/holistic/document/unsaved/change.rb
81
85
  - lib/holistic/document/unsaved/collection.rb
@@ -110,23 +114,23 @@ files:
110
114
  - lib/holistic/ruby/parser/live_editing/process_file_changed.rb
111
115
  - lib/holistic/ruby/parser/nesting_syntax.rb
112
116
  - lib/holistic/ruby/parser/program_visitor.rb
113
- - lib/holistic/ruby/parser/table_of_contents.rb
117
+ - lib/holistic/ruby/reference/delete.rb
114
118
  - lib/holistic/ruby/reference/find_referenced_scope.rb
115
119
  - lib/holistic/ruby/reference/record.rb
116
- - lib/holistic/ruby/reference/register.rb
117
120
  - lib/holistic/ruby/reference/repository.rb
118
- - lib/holistic/ruby/reference/unregister.rb
121
+ - lib/holistic/ruby/reference/store.rb
122
+ - lib/holistic/ruby/scope/delete.rb
119
123
  - lib/holistic/ruby/scope/kind.rb
124
+ - lib/holistic/ruby/scope/lexical.rb
120
125
  - lib/holistic/ruby/scope/list_references.rb
121
126
  - lib/holistic/ruby/scope/location.rb
122
127
  - lib/holistic/ruby/scope/outline.rb
123
128
  - lib/holistic/ruby/scope/record.rb
124
- - lib/holistic/ruby/scope/register.rb
125
129
  - lib/holistic/ruby/scope/repository.rb
126
- - lib/holistic/ruby/scope/unregister.rb
130
+ - lib/holistic/ruby/scope/store.rb
127
131
  - lib/holistic/ruby/type_inference/clue/method_call.rb
128
132
  - lib/holistic/ruby/type_inference/clue/scope_reference.rb
129
- - lib/holistic/ruby/type_inference/conclusion.rb
133
+ - lib/holistic/ruby/type_inference/processing_queue.rb
130
134
  - lib/holistic/ruby/type_inference/solve.rb
131
135
  - lib/holistic/ruby/type_inference/solve_pending_references.rb
132
136
  - lib/holistic/version.rb
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Document
4
- class File
5
- attr_reader :path
6
-
7
- def initialize(path:)
8
- @path = path
9
- end
10
-
11
- def read
12
- ::File.read(path)
13
- end
14
-
15
- def write(content)
16
- ::File.write(path, content)
17
- end
18
-
19
- class Fake
20
- attr_reader :path
21
-
22
- def initialize(path:, content:)
23
- @path = path
24
- @content = content
25
- end
26
-
27
- def read
28
- @content
29
- end
30
-
31
- def write(content)
32
- @content = content
33
- end
34
- end
35
- end
36
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::Parser
4
- # TODO: move to an attibute of the scope
5
- class TableOfContents
6
- attr_reader :records
7
-
8
- def initialize
9
- @records = Hash.new { |hash, key| hash[key] = {} }
10
- end
11
-
12
- def register(scope:, name:, clue:)
13
- @records[scope.fully_qualified_name][name] ||= []
14
- @records[scope.fully_qualified_name][name] << clue
15
- end
16
- end
17
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::Reference
4
- module Register
5
- extend self
6
-
7
- def call(repository:, scope:, clues:, location:)
8
- conclusion = ::Holistic::Ruby::TypeInference::Conclusion.pending
9
-
10
- reference = ::Holistic::Ruby::Reference::Record.new(scope:, clues:, location:, conclusion:)
11
-
12
- repository.register_reference(reference)
13
- end
14
- end
15
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::Reference
4
- module Unregister
5
- extend self
6
-
7
- def call(repository:, reference:)
8
- repository.delete(reference.identifier)
9
- end
10
- end
11
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::Scope
4
- module Register
5
- extend self
6
-
7
- def call(repository:, parent:, kind:, name:, location:)
8
- child_scope = append_location_to_existing_scope(scope: parent, name:, location:) || add_new_scope(parent:, kind:, name:, location:)
9
-
10
- repository.register_scope(child_scope)
11
-
12
- child_scope
13
- end
14
-
15
- private
16
-
17
- def append_location_to_existing_scope(scope:, name:, location:)
18
- child_scope = scope.children.find { _1.name == name }
19
-
20
- return if child_scope.nil?
21
-
22
- child_scope.tap { _1.locations << location }
23
- end
24
-
25
- def add_new_scope(parent:, kind:, name:, location:)
26
- child_scope = Record.new(kind:, name:, parent:, location:)
27
-
28
- child_scope.tap { parent.children << _1 }
29
- end
30
- end
31
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::Scope
4
- module Unregister
5
- extend self
6
-
7
- def call(repository:, fully_qualified_name:, file_path:)
8
- scope = repository.find_by_fully_qualified_name(fully_qualified_name)
9
-
10
- return :scope_not_found if scope.nil?
11
-
12
- updated_locations = scope.locations.reject! { |scope_location| scope_location.declaration.file_path == file_path }
13
-
14
- return :scope_not_defined_in_speciefied_file if updated_locations.nil?
15
-
16
- if updated_locations.empty?
17
- scope.parent.children.delete(scope)
18
-
19
- repository.delete_by_fully_qualified_name(fully_qualified_name)
20
- else
21
- repository.register_scope(scope)
22
- end
23
-
24
- :definition_unregistered
25
- end
26
- end
27
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Holistic::Ruby::TypeInference
4
- STATUS_PENDING = :pending
5
- STATUS_DONE = :done
6
-
7
- Conclusion = ::Data.define(:status, :dependency_identifier) do
8
- def self.pending
9
- new(status: STATUS_PENDING, dependency_identifier: nil)
10
- end
11
-
12
- def self.unresolved
13
- new(status: STATUS_DONE, dependency_identifier: nil)
14
- end
15
-
16
- def self.done(dependency_identifier)
17
- new(status: STATUS_DONE, dependency_identifier:)
18
- end
19
- end
20
- end