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
@@ -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