holistic-ruby 0.1.1 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +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/events.rb +9 -1
- data/lib/holistic/extensions/ruby/stdlib.rb +26 -12
- data/lib/holistic/language_server/requests/lifecycle/initialize.rb +5 -10
- data/lib/holistic/language_server/requests/text_document/completion.rb +11 -8
- data/lib/holistic/language_server/requests/text_document/did_close.rb +6 -12
- 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 +65 -15
- data/lib/holistic/ruby/parser/constant_resolution.rb +60 -9
- data/lib/holistic/ruby/parser/live_editing/process_file_changed.rb +24 -21
- data/lib/holistic/ruby/parser/nesting_syntax.rb +1 -0
- data/lib/holistic/ruby/parser/program_visitor.rb +57 -44
- data/lib/holistic/ruby/parser.rb +14 -9
- 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/kind.rb +6 -5
- 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 -47
- 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 +23 -21
- 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
@@ -6,6 +6,12 @@ module Holistic::Ruby::Autocompletion
|
|
6
6
|
|
7
7
|
Suggestion = ::Data.define(:code, :kind)
|
8
8
|
|
9
|
+
StartsWithLowerCaseLetter = ->(code) do
|
10
|
+
return false if [".", ":", "@"].include?(code[0])
|
11
|
+
|
12
|
+
code[0] == code[0].downcase
|
13
|
+
end
|
14
|
+
|
9
15
|
def call(code:, scope:)
|
10
16
|
lookup_scope = scope
|
11
17
|
|
@@ -13,12 +19,66 @@ module Holistic::Ruby::Autocompletion
|
|
13
19
|
lookup_scope = lookup_scope.parent until lookup_scope.root?
|
14
20
|
end
|
15
21
|
|
16
|
-
|
22
|
+
if StartsWithLowerCaseLetter[code]
|
23
|
+
suggest_local_methods_from_current_scope(code:, scope: lookup_scope)
|
24
|
+
elsif code.include?(".")
|
25
|
+
suggest_methods_from_scope(code:, scope: lookup_scope)
|
26
|
+
else
|
27
|
+
suggest_namespaces_from_scope(code:, scope: lookup_scope)
|
28
|
+
end
|
17
29
|
end
|
18
30
|
|
19
31
|
private
|
20
32
|
|
21
|
-
|
33
|
+
def suggest_local_methods_from_current_scope(code:, scope:)
|
34
|
+
suggestions = []
|
35
|
+
|
36
|
+
method_to_autocomplete = code
|
37
|
+
|
38
|
+
if scope.instance_method?
|
39
|
+
sibling_methods = scope.parent.children.filter { _1.instance_method? }
|
40
|
+
|
41
|
+
sibling_methods.each do |method_scope|
|
42
|
+
if method_scope.name.start_with?(method_to_autocomplete)
|
43
|
+
suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
elsif scope.class_method?
|
47
|
+
sibling_methods = scope.parent.children.filter { _1.class_method? }
|
48
|
+
|
49
|
+
sibling_methods.each do |method_scope|
|
50
|
+
if method_scope.name.start_with?(method_to_autocomplete)
|
51
|
+
suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
suggestions
|
57
|
+
end
|
58
|
+
|
59
|
+
def suggest_methods_from_scope(code:, scope:)
|
60
|
+
suggestions = []
|
61
|
+
|
62
|
+
partial_namespaces = code.split(/(::|\.)/).compact_blank
|
63
|
+
method_to_autocomplete = partial_namespaces.pop.then { _1 == "." ? "" : _1 }
|
64
|
+
namespaces_to_resolve = partial_namespaces.reject { _1 == "::" || _1 == "." }
|
65
|
+
|
66
|
+
namespaces_to_resolve.each do |namespace_name|
|
67
|
+
scope = resolve_scope(name: namespace_name, from_scope: scope)
|
68
|
+
|
69
|
+
return suggestions if scope.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
class_methods = scope.children.filter { _1.class_method? }
|
73
|
+
|
74
|
+
class_methods.each do |method_scope|
|
75
|
+
if method_scope.name.start_with?(method_to_autocomplete)
|
76
|
+
suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
suggestions
|
81
|
+
end
|
22
82
|
|
23
83
|
def suggest_namespaces_from_scope(code:, scope:)
|
24
84
|
suggestions = []
|
@@ -33,22 +93,12 @@ module Holistic::Ruby::Autocompletion
|
|
33
93
|
return suggestions if scope.nil?
|
34
94
|
end
|
35
95
|
|
36
|
-
# special case when user did not type :: at the end but the current word
|
37
|
-
# is matches an existing namespace. In this case, suggestions will start with ::.
|
38
|
-
# For example:
|
39
|
-
#
|
40
|
-
# \/ cursor here
|
41
|
-
# typing: "::MyApp::Payments"
|
42
|
-
# suggestions: ["::Record", "::SendReminder"]
|
43
|
-
resolve_scope(name: namespace_to_autocomplete, from_scope: scope)&.then do |fully_typed_scope|
|
44
|
-
scope = fully_typed_scope
|
45
|
-
namespace_to_autocomplete = ""
|
46
|
-
end
|
47
|
-
|
48
96
|
should_search_upwards = namespaces_to_resolve.empty?
|
49
97
|
|
50
98
|
search = ->(scope) do
|
51
|
-
scope.children.
|
99
|
+
scope.children.each do |child_scope|
|
100
|
+
next if child_scope.method?
|
101
|
+
|
52
102
|
if child_scope.name.start_with?(namespace_to_autocomplete)
|
53
103
|
suggestions << Suggestion.new(code: child_scope.name, kind: child_scope.kind)
|
54
104
|
end
|
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
module Holistic::Ruby::Parser
|
4
4
|
class ConstantResolution
|
5
|
-
|
5
|
+
module MethodRegistrationMode
|
6
|
+
INSTANCE_METHODS = :instance_methods
|
7
|
+
CLASS_METHODS = :class_methods
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :scope_repository, :scope, :method_registration_mode
|
6
11
|
|
7
|
-
def initialize(scope_repository
|
12
|
+
def initialize(scope_repository:)
|
8
13
|
@scope_repository = scope_repository
|
9
|
-
@scope =
|
14
|
+
@scope = scope_repository.root
|
10
15
|
@constant_resolution_possibilities = ["::"]
|
16
|
+
@method_registration_mode = MethodRegistrationMode::INSTANCE_METHODS
|
11
17
|
end
|
12
18
|
|
13
19
|
def current
|
@@ -19,8 +25,8 @@ module Holistic::Ruby::Parser
|
|
19
25
|
|
20
26
|
nesting.each do |name|
|
21
27
|
@scope =
|
22
|
-
::Holistic::Ruby::Scope::
|
23
|
-
|
28
|
+
::Holistic::Ruby::Scope::Store.call(
|
29
|
+
database: @scope_repository.database,
|
24
30
|
parent: @scope,
|
25
31
|
kind: ::Holistic::Ruby::Scope::Kind::MODULE,
|
26
32
|
name:,
|
@@ -28,12 +34,17 @@ module Holistic::Ruby::Parser
|
|
28
34
|
)
|
29
35
|
end
|
30
36
|
|
37
|
+
registered_module_scope = @scope
|
38
|
+
|
31
39
|
@constant_resolution_possibilities.unshift(@scope.fully_qualified_name)
|
32
40
|
|
33
41
|
block.call
|
34
42
|
|
35
|
-
|
43
|
+
change_method_registration_mode_to_instance_methods!
|
36
44
|
@constant_resolution_possibilities.shift
|
45
|
+
@scope = starting_scope
|
46
|
+
|
47
|
+
registered_module_scope
|
37
48
|
end
|
38
49
|
|
39
50
|
def register_child_class(nesting:, location:, &block)
|
@@ -41,8 +52,8 @@ module Holistic::Ruby::Parser
|
|
41
52
|
|
42
53
|
nesting.each do |name|
|
43
54
|
@scope =
|
44
|
-
::Holistic::Ruby::Scope::
|
45
|
-
|
55
|
+
::Holistic::Ruby::Scope::Store.call(
|
56
|
+
database: @scope_repository.database,
|
46
57
|
parent: @scope,
|
47
58
|
kind: ::Holistic::Ruby::Scope::Kind::CLASS,
|
48
59
|
name:,
|
@@ -50,12 +61,52 @@ module Holistic::Ruby::Parser
|
|
50
61
|
)
|
51
62
|
end
|
52
63
|
|
64
|
+
registered_class_scope = @scope
|
65
|
+
|
53
66
|
@constant_resolution_possibilities.unshift(@scope.fully_qualified_name)
|
54
67
|
|
55
68
|
block.call
|
56
69
|
|
57
|
-
|
70
|
+
change_method_registration_mode_to_instance_methods!
|
58
71
|
@constant_resolution_possibilities.shift
|
72
|
+
@scope = starting_scope
|
73
|
+
|
74
|
+
registered_class_scope
|
75
|
+
end
|
76
|
+
|
77
|
+
def register_child_method(nesting:, location:, kind:, &block)
|
78
|
+
starting_scope = @scope
|
79
|
+
|
80
|
+
nesting.each do |name|
|
81
|
+
@scope =
|
82
|
+
::Holistic::Ruby::Scope::Store.call(
|
83
|
+
database: @scope_repository.database,
|
84
|
+
parent: @scope,
|
85
|
+
kind:,
|
86
|
+
name:,
|
87
|
+
location:
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
registered_method_scope = @scope
|
92
|
+
|
93
|
+
block.call
|
94
|
+
|
95
|
+
@scope = starting_scope
|
96
|
+
|
97
|
+
registered_method_scope
|
98
|
+
end
|
99
|
+
|
100
|
+
def method_registration_class_methods?
|
101
|
+
method_registration_mode == MethodRegistrationMode::CLASS_METHODS
|
102
|
+
end
|
103
|
+
|
104
|
+
def change_method_registration_mode_to_class_methods!
|
105
|
+
@method_registration_mode = MethodRegistrationMode::CLASS_METHODS
|
106
|
+
end
|
107
|
+
|
108
|
+
def change_method_registration_mode_to_instance_methods!
|
109
|
+
@method_registration_mode = MethodRegistrationMode::INSTANCE_METHODS
|
59
110
|
end
|
60
111
|
end
|
61
112
|
end
|
@@ -4,56 +4,59 @@ module Holistic::Ruby::Parser
|
|
4
4
|
module LiveEditing::ProcessFileChanged
|
5
5
|
extend self
|
6
6
|
|
7
|
-
def call(application:,
|
8
|
-
|
7
|
+
def call(application:, file_path:, content:)
|
8
|
+
# TODO: do not build the AST twice
|
9
|
+
return unless HasValidSyntax[content]
|
9
10
|
|
10
|
-
|
11
|
-
unregsiter_references_in_file(application:, file:)
|
11
|
+
references_to_recalculate = identify_references_to_recalculate(application:, file_path:)
|
12
12
|
|
13
|
-
|
13
|
+
delete_scopes_in_file(application:, file_path:)
|
14
|
+
delete_references_in_file(application:, file_path:)
|
15
|
+
|
16
|
+
parse_again(application:, file_path:, content:)
|
14
17
|
|
15
18
|
recalculate_type_inference_for_references(application:, references: references_to_recalculate)
|
16
19
|
end
|
17
20
|
|
18
21
|
private
|
19
22
|
|
20
|
-
def
|
23
|
+
def identify_references_to_recalculate(application:, file_path:)
|
21
24
|
# we need to reject references declared in the same because they're already going to be
|
22
|
-
#
|
25
|
+
# reparsed. If we don't do that, we'll end up with duplicated reference records.
|
23
26
|
|
24
27
|
application.references
|
25
|
-
.list_references_to_scopes_in_file(scopes: application.scopes, file_path:
|
26
|
-
.reject { _1.location.
|
28
|
+
.list_references_to_scopes_in_file(scopes: application.scopes, file_path: file_path)
|
29
|
+
.reject { _1.location.file.path == file_path }
|
27
30
|
end
|
28
31
|
|
29
|
-
def
|
30
|
-
application.scopes.list_scopes_in_file(
|
31
|
-
::Holistic::Ruby::Scope::
|
32
|
-
|
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,
|
33
36
|
fully_qualified_name: scope.fully_qualified_name,
|
34
|
-
file_path:
|
37
|
+
file_path:
|
35
38
|
)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
|
-
def
|
40
|
-
application.references.list_references_in_file(
|
41
|
-
::Holistic::Ruby::Reference::
|
42
|
-
|
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,
|
43
46
|
reference: reference
|
44
47
|
)
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
48
|
-
def parse_again(application:,
|
49
|
-
ParseFile.call(application:,
|
51
|
+
def parse_again(application:, file_path:, content:)
|
52
|
+
ParseFile.call(application:, file_path:, content:)
|
50
53
|
|
51
54
|
::Holistic::Ruby::TypeInference::SolvePendingReferences.call(application:)
|
52
55
|
end
|
53
56
|
|
54
57
|
def recalculate_type_inference_for_references(application:, references:)
|
55
58
|
references.each do |reference|
|
56
|
-
reference.
|
59
|
+
application.database.disconnect(source: reference.referenced_scope, target: reference, name: :referenced_by, inverse_of: :referenced_scope)
|
57
60
|
|
58
61
|
::Holistic::Ruby::TypeInference::Solve.call(application:, reference:)
|
59
62
|
end
|
@@ -14,6 +14,7 @@ module Holistic::Ruby::Parser
|
|
14
14
|
when ::SyntaxTree::Const then nesting_syntax << node.value
|
15
15
|
when ::SyntaxTree::VCall then append.(node.child_nodes.first)
|
16
16
|
when ::SyntaxTree::Ident then nesting_syntax << node.value
|
17
|
+
when ::SyntaxTree::Kw then nesting_syntax << node.value
|
17
18
|
when ::SyntaxTree::IVar then nesting_syntax << node.value
|
18
19
|
when ::SyntaxTree::Period then nesting_syntax << "."
|
19
20
|
when ::SyntaxTree::Paren then append.(node.child_nodes[1]) # node.child_nodes[0] is ::SyntaxTree::LParen
|
@@ -14,52 +14,63 @@ module Holistic::Ruby::Parser
|
|
14
14
|
|
15
15
|
visit_methods do
|
16
16
|
def visit_module(node)
|
17
|
-
declaration_node,
|
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(
|
23
|
+
visit(body_statements_node)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def visit_class(node)
|
28
|
-
declaration_node, superclass_node,
|
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
|
-
@constant_resolution.register_child_class(nesting:, location:) do
|
38
|
-
visit(
|
37
|
+
class_scope = @constant_resolution.register_child_class(nesting:, location:) do
|
38
|
+
visit(body_statements_node)
|
39
|
+
end
|
40
|
+
|
41
|
+
@application.extensions.dispatch(:class_scope_registered, { class_scope:, location: })
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_command(node)
|
45
|
+
command_name_node, args_node = node.child_nodes
|
46
|
+
|
47
|
+
if command_name_node.value == "extend"
|
48
|
+
is_extending_self = args_node.child_nodes.size == 1 && NestingSyntax.build(args_node.child_nodes.first).to_s == "self"
|
49
|
+
|
50
|
+
@constant_resolution.change_method_registration_mode_to_class_methods! if is_extending_self
|
39
51
|
end
|
52
|
+
|
53
|
+
visit(args_node)
|
40
54
|
end
|
41
55
|
|
42
56
|
def visit_def(node)
|
43
|
-
instance_node, period_node, method_name_node, _params,
|
57
|
+
instance_node, period_node, method_name_node, _params, body_statements_node = node.child_nodes
|
58
|
+
|
59
|
+
nesting = NestingSyntax.new(method_name_node.value)
|
60
|
+
location = build_scope_location(declaration_node: method_name_node, body_node: node)
|
44
61
|
|
45
|
-
|
46
|
-
if instance_node.present? &&
|
47
|
-
|
62
|
+
kind =
|
63
|
+
if instance_node.present? && instance_node.child_nodes.first.value == "self"
|
64
|
+
::Holistic::Ruby::Scope::Kind::CLASS_METHOD
|
65
|
+
elsif @constant_resolution.method_registration_class_methods?
|
66
|
+
::Holistic::Ruby::Scope::Kind::CLASS_METHOD
|
48
67
|
else
|
49
|
-
|
68
|
+
::Holistic::Ruby::Scope::Kind::INSTANCE_METHOD
|
50
69
|
end
|
51
70
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
repository: @application.scopes,
|
56
|
-
parent: @constant_resolution.scope,
|
57
|
-
kind: ::Holistic::Ruby::Scope::Kind::METHOD,
|
58
|
-
name: method_name,
|
59
|
-
location:
|
60
|
-
)
|
61
|
-
|
62
|
-
visit(body_node)
|
71
|
+
@constant_resolution.register_child_method(nesting:, location:, kind:) do
|
72
|
+
visit(body_statements_node)
|
73
|
+
end
|
63
74
|
end
|
64
75
|
|
65
76
|
def visit_vcall(node)
|
@@ -69,8 +80,9 @@ module Holistic::Ruby::Parser
|
|
69
80
|
resolution_possibilities: @constant_resolution.current
|
70
81
|
)
|
71
82
|
|
72
|
-
::Holistic::Ruby::Reference::
|
73
|
-
|
83
|
+
::Holistic::Ruby::Reference::Store.call(
|
84
|
+
database: @application.database,
|
85
|
+
processing_queue: @application.type_inference_processing_queue,
|
74
86
|
scope: @constant_resolution.scope,
|
75
87
|
clues: [method_call_clue],
|
76
88
|
location: build_location(node)
|
@@ -103,8 +115,9 @@ module Holistic::Ruby::Parser
|
|
103
115
|
resolution_possibilities: @constant_resolution.current
|
104
116
|
)
|
105
117
|
|
106
|
-
::Holistic::Ruby::Reference::
|
107
|
-
|
118
|
+
::Holistic::Ruby::Reference::Store.call(
|
119
|
+
database: @application.database,
|
120
|
+
processing_queue: @application.type_inference_processing_queue,
|
108
121
|
scope: @constant_resolution.scope,
|
109
122
|
clues: [method_call_clue],
|
110
123
|
location: build_location(method_name_node || instance_node)
|
@@ -128,22 +141,27 @@ module Holistic::Ruby::Parser
|
|
128
141
|
nesting = NestingSyntax.build(assign_node)
|
129
142
|
location = build_scope_location(declaration_node: assign_node, body_node: block_node)
|
130
143
|
|
131
|
-
@constant_resolution.register_child_class(nesting:, location:) do
|
144
|
+
class_scope = @constant_resolution.register_child_class(nesting:, location:) do
|
132
145
|
visit(block_node)
|
133
146
|
end
|
134
147
|
|
148
|
+
@application.extensions.dispatch(:class_scope_registered, { class_scope:, location: })
|
149
|
+
|
135
150
|
return
|
136
151
|
end
|
137
152
|
|
138
153
|
location = build_scope_location(declaration_node: assign_node, body_node:)
|
139
154
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
155
|
+
lambda_scope =
|
156
|
+
::Holistic::Ruby::Scope::Store.call(
|
157
|
+
database: @application.database,
|
158
|
+
parent: @constant_resolution.scope,
|
159
|
+
kind: ::Holistic::Ruby::Scope::Kind::LAMBDA,
|
160
|
+
name: assign_node.child_nodes.first.value,
|
161
|
+
location:
|
162
|
+
)
|
163
|
+
|
164
|
+
@application.extensions.dispatch(:lambda_scope_registered, { lambda_scope:, location: })
|
147
165
|
|
148
166
|
visit(body_node)
|
149
167
|
end
|
@@ -169,8 +187,9 @@ module Holistic::Ruby::Parser
|
|
169
187
|
resolution_possibilities: @constant_resolution.current
|
170
188
|
)
|
171
189
|
|
172
|
-
::Holistic::Ruby::Reference::
|
173
|
-
|
190
|
+
::Holistic::Ruby::Reference::Store.call(
|
191
|
+
database: @application.database,
|
192
|
+
processing_queue: @application.type_inference_processing_queue,
|
174
193
|
scope: @constant_resolution.scope,
|
175
194
|
clues: [clue],
|
176
195
|
location:
|
@@ -193,13 +212,7 @@ module Holistic::Ruby::Parser
|
|
193
212
|
start_column = node.location.start_column
|
194
213
|
end_column = node.location.end_column
|
195
214
|
|
196
|
-
|
197
|
-
# It sets the end_column lower than the start_column.
|
198
|
-
if start_line == end_line && start_column > end_column
|
199
|
-
start_column, end_column = end_column, start_column
|
200
|
-
end
|
201
|
-
|
202
|
-
::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:)
|
203
216
|
end
|
204
217
|
end
|
205
218
|
end
|
data/lib/holistic/ruby/parser.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Holistic::Ruby::Parser
|
4
|
-
|
5
|
-
|
4
|
+
HasValidSyntax = ->(content) do
|
5
|
+
::SyntaxTree.parse(content)
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
true
|
8
|
+
rescue ::SyntaxTree::Parser::ParseError
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
ParseFile = ->(application:, file_path:, content:) do
|
13
|
+
program = ::SyntaxTree.parse(content)
|
14
|
+
|
15
|
+
constant_resolution = ConstantResolution.new(scope_repository: application.scopes)
|
16
|
+
|
17
|
+
file = ::Holistic::Document::File::Store.call(database: application.database, file_path:)
|
11
18
|
|
12
19
|
visitor = ProgramVisitor.new(application:, constant_resolution:, file:)
|
13
20
|
|
@@ -18,9 +25,7 @@ module Holistic::Ruby::Parser
|
|
18
25
|
|
19
26
|
ParseDirectory = ->(application:, directory_path:) do
|
20
27
|
::Dir.glob("#{directory_path}/**/*.rb").map do |file_path|
|
21
|
-
|
22
|
-
|
23
|
-
ParseFile[application:, file:]
|
28
|
+
ParseFile.call(application:, file_path:, content: ::File.read(file_path))
|
24
29
|
end
|
25
30
|
end
|
26
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.
|
11
|
+
return :could_not_find_referenced_scope if reference.referenced_scope.nil?
|
12
12
|
|
13
|
-
referenced_scope =
|
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
|
5
|
-
:
|
6
|
-
:clues
|
7
|
-
:
|
8
|
-
|
9
|
-
|
10
|
-
|
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 :
|
5
|
+
attr_reader :database
|
6
6
|
|
7
|
-
|
8
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
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
|
-
|
60
|
-
|
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
|
-
|
66
|
-
|
43
|
+
all.find do |node|
|
44
|
+
node.clues&.find { _1.to_s == code_content }
|
67
45
|
end
|
68
46
|
end
|
69
47
|
end
|