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.
- 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
@@ -2,48 +2,47 @@
|
|
2
2
|
|
3
3
|
module Holistic::Ruby::Scope
|
4
4
|
class Repository
|
5
|
-
attr_reader :
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
20
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
33
|
+
file = database.find(cursor.file_path)
|
33
34
|
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
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.
|
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.
|
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.
|
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.
|
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
|
data/lib/holistic/version.rb
CHANGED
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
|
+
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-
|
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/
|
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/
|
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/
|
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/
|
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,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
|