yoda-language-server 0.4.0
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 +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +78 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/client/atom/main.js +27 -0
- data/client/vscode/.gitignore +4 -0
- data/client/vscode/.vscode/launch.json +28 -0
- data/client/vscode/.vscode/settings.json +9 -0
- data/client/vscode/.vscode/tasks.json +20 -0
- data/client/vscode/.vscodeignore +8 -0
- data/client/vscode/CHANGELOG.md +7 -0
- data/client/vscode/README.md +65 -0
- data/client/vscode/package-lock.json +2688 -0
- data/client/vscode/package.json +39 -0
- data/client/vscode/src/extension.ts +42 -0
- data/client/vscode/src/test/extension.test.ts +22 -0
- data/client/vscode/src/test/index.ts +22 -0
- data/client/vscode/tsconfig.json +16 -0
- data/client/vscode/vsc-extension-quickstart.md +33 -0
- data/exe/yoda +27 -0
- data/lib/yoda.rb +11 -0
- data/lib/yoda/evaluation.rb +9 -0
- data/lib/yoda/evaluation/code_completion.rb +65 -0
- data/lib/yoda/evaluation/code_completion/base_provider.rb +57 -0
- data/lib/yoda/evaluation/code_completion/const_provider.rb +90 -0
- data/lib/yoda/evaluation/code_completion/method_provider.rb +82 -0
- data/lib/yoda/evaluation/code_completion/variable_provider.rb +18 -0
- data/lib/yoda/evaluation/comment_completion.rb +70 -0
- data/lib/yoda/evaluation/comment_completion/base_provider.rb +64 -0
- data/lib/yoda/evaluation/comment_completion/param_provider.rb +18 -0
- data/lib/yoda/evaluation/comment_completion/tag_provider.rb +41 -0
- data/lib/yoda/evaluation/comment_completion/type_provider.rb +58 -0
- data/lib/yoda/evaluation/current_node_explain.rb +70 -0
- data/lib/yoda/evaluation/evaluator.rb +103 -0
- data/lib/yoda/evaluation/signature_discovery.rb +83 -0
- data/lib/yoda/model.rb +12 -0
- data/lib/yoda/model/completion_item.rb +56 -0
- data/lib/yoda/model/descriptions.rb +10 -0
- data/lib/yoda/model/descriptions/base.rb +26 -0
- data/lib/yoda/model/descriptions/function_description.rb +40 -0
- data/lib/yoda/model/descriptions/value_description.rb +33 -0
- data/lib/yoda/model/descriptions/word_description.rb +32 -0
- data/lib/yoda/model/function_signatures.rb +13 -0
- data/lib/yoda/model/function_signatures/base.rb +68 -0
- data/lib/yoda/model/function_signatures/constructor.rb +70 -0
- data/lib/yoda/model/function_signatures/formatter.rb +82 -0
- data/lib/yoda/model/function_signatures/method.rb +67 -0
- data/lib/yoda/model/function_signatures/overload.rb +79 -0
- data/lib/yoda/model/function_signatures/parameter_list.rb +108 -0
- data/lib/yoda/model/function_signatures/type_builder.rb +101 -0
- data/lib/yoda/model/node_signature.rb +28 -0
- data/lib/yoda/model/path.rb +96 -0
- data/lib/yoda/model/scoped_path.rb +44 -0
- data/lib/yoda/model/types.rb +84 -0
- data/lib/yoda/model/types/any_type.rb +32 -0
- data/lib/yoda/model/types/base.rb +37 -0
- data/lib/yoda/model/types/duck_type.rb +41 -0
- data/lib/yoda/model/types/function_type.rb +174 -0
- data/lib/yoda/model/types/generic_type.rb +66 -0
- data/lib/yoda/model/types/instance_type.rb +42 -0
- data/lib/yoda/model/types/module_type.rb +42 -0
- data/lib/yoda/model/types/sequence_type.rb +53 -0
- data/lib/yoda/model/types/union_type.rb +56 -0
- data/lib/yoda/model/types/unknown_type.rb +40 -0
- data/lib/yoda/model/types/value_type.rb +58 -0
- data/lib/yoda/model/values.rb +9 -0
- data/lib/yoda/model/values/base.rb +32 -0
- data/lib/yoda/model/values/instance_value.rb +65 -0
- data/lib/yoda/model/values/module_value.rb +72 -0
- data/lib/yoda/parsing.rb +15 -0
- data/lib/yoda/parsing/ast_traversable.rb +18 -0
- data/lib/yoda/parsing/comment_tokenizer.rb +59 -0
- data/lib/yoda/parsing/location.rb +101 -0
- data/lib/yoda/parsing/node_objects.rb +10 -0
- data/lib/yoda/parsing/node_objects/const_node.rb +52 -0
- data/lib/yoda/parsing/node_objects/method_definition.rb +46 -0
- data/lib/yoda/parsing/node_objects/namespace.rb +104 -0
- data/lib/yoda/parsing/node_objects/send_node.rb +72 -0
- data/lib/yoda/parsing/parser.rb +27 -0
- data/lib/yoda/parsing/query.rb +11 -0
- data/lib/yoda/parsing/query/current_comment_query.rb +80 -0
- data/lib/yoda/parsing/query/current_comment_token_query.rb +153 -0
- data/lib/yoda/parsing/query/current_commenting_node_query.rb +68 -0
- data/lib/yoda/parsing/query/current_location_node_query.rb +51 -0
- data/lib/yoda/parsing/query/current_node_comment_query.rb +40 -0
- data/lib/yoda/parsing/range.rb +41 -0
- data/lib/yoda/parsing/scopes.rb +15 -0
- data/lib/yoda/parsing/scopes/base.rb +78 -0
- data/lib/yoda/parsing/scopes/builder.rb +60 -0
- data/lib/yoda/parsing/scopes/class_definition.rb +47 -0
- data/lib/yoda/parsing/scopes/meta_class_definition.rb +44 -0
- data/lib/yoda/parsing/scopes/meta_method_definition.rb +70 -0
- data/lib/yoda/parsing/scopes/method_definition.rb +69 -0
- data/lib/yoda/parsing/scopes/module_definition.rb +36 -0
- data/lib/yoda/parsing/scopes/root.rb +25 -0
- data/lib/yoda/parsing/source_analyzer.rb +59 -0
- data/lib/yoda/parsing/source_cutter.rb +231 -0
- data/lib/yoda/parsing/type_parser.rb +141 -0
- data/lib/yoda/runner.rb +6 -0
- data/lib/yoda/runner/infer.rb +50 -0
- data/lib/yoda/runner/setup.rb +26 -0
- data/lib/yoda/server.rb +191 -0
- data/lib/yoda/server/client_info.rb +98 -0
- data/lib/yoda/server/completion_provider.rb +78 -0
- data/lib/yoda/server/definition_provider.rb +36 -0
- data/lib/yoda/server/deserializer.rb +27 -0
- data/lib/yoda/server/hover_provider.rb +38 -0
- data/lib/yoda/server/signature_provider.rb +46 -0
- data/lib/yoda/store.rb +13 -0
- data/lib/yoda/store/actions.rb +10 -0
- data/lib/yoda/store/actions/import_core_library.rb +30 -0
- data/lib/yoda/store/actions/import_gems.rb +91 -0
- data/lib/yoda/store/actions/read_file.rb +36 -0
- data/lib/yoda/store/actions/read_project_files.rb +29 -0
- data/lib/yoda/store/adapters.rb +14 -0
- data/lib/yoda/store/adapters/base.rb +58 -0
- data/lib/yoda/store/adapters/leveldb_adapter.rb +80 -0
- data/lib/yoda/store/adapters/lmdb_adapter.rb +113 -0
- data/lib/yoda/store/objects.rb +46 -0
- data/lib/yoda/store/objects/addressable.rb +25 -0
- data/lib/yoda/store/objects/base.rb +116 -0
- data/lib/yoda/store/objects/class_object.rb +51 -0
- data/lib/yoda/store/objects/merger.rb +94 -0
- data/lib/yoda/store/objects/meta_class_object.rb +41 -0
- data/lib/yoda/store/objects/method_object.rb +94 -0
- data/lib/yoda/store/objects/module_object.rb +11 -0
- data/lib/yoda/store/objects/namespace_object.rb +67 -0
- data/lib/yoda/store/objects/overload.rb +51 -0
- data/lib/yoda/store/objects/patch.rb +46 -0
- data/lib/yoda/store/objects/patch_set.rb +80 -0
- data/lib/yoda/store/objects/tag.rb +62 -0
- data/lib/yoda/store/objects/value_object.rb +45 -0
- data/lib/yoda/store/project.rb +159 -0
- data/lib/yoda/store/query.rb +12 -0
- data/lib/yoda/store/query/associators.rb +10 -0
- data/lib/yoda/store/query/associators/associate_ancestors.rb +103 -0
- data/lib/yoda/store/query/associators/associate_methods.rb +38 -0
- data/lib/yoda/store/query/base.rb +16 -0
- data/lib/yoda/store/query/find_constant.rb +150 -0
- data/lib/yoda/store/query/find_meta_class.rb +18 -0
- data/lib/yoda/store/query/find_method.rb +74 -0
- data/lib/yoda/store/query/find_signature.rb +43 -0
- data/lib/yoda/store/registry.rb +67 -0
- data/lib/yoda/store/yard_importer.rb +260 -0
- data/lib/yoda/typing.rb +10 -0
- data/lib/yoda/typing/context.rb +96 -0
- data/lib/yoda/typing/environment.rb +35 -0
- data/lib/yoda/typing/evaluator.rb +256 -0
- data/lib/yoda/typing/lexical_scope.rb +26 -0
- data/lib/yoda/typing/relation.rb +15 -0
- data/lib/yoda/typing/traces.rb +9 -0
- data/lib/yoda/typing/traces/base.rb +26 -0
- data/lib/yoda/typing/traces/normal.rb +22 -0
- data/lib/yoda/typing/traces/send.rb +26 -0
- data/lib/yoda/version.rb +3 -0
- data/lib/yoda/yard_extensions.rb +11 -0
- data/lib/yoda/yard_extensions/sig_directive.rb +40 -0
- data/lib/yoda/yard_extensions/type_tag.rb +10 -0
- data/package.json +76 -0
- data/scripts/benchmark.rb +6 -0
- data/scripts/build_core_index.sh +16 -0
- data/yarn.lock +13 -0
- data/yoda-language-server.gemspec +40 -0
- metadata +424 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Model
|
|
3
|
+
module Values
|
|
4
|
+
class ModuleValue < Base
|
|
5
|
+
attr_reader :registry, :namespace_object
|
|
6
|
+
|
|
7
|
+
# @param registry [Registry]
|
|
8
|
+
# @param namespace_object [::YARD::CodeObjects::NamespaceObject, ::YARD::CodeObjects::Proxy]
|
|
9
|
+
def initialize(registry, namespace_object)
|
|
10
|
+
fail ArgumentError, registry unless registry.is_a?(Registry)
|
|
11
|
+
fail ArgumentError, namespace_object unless namespace_object.is_a?(::YARD::CodeObjects::NamespaceObject) || namespace_object.is_a?(::YARD::CodeObjects::Proxy)
|
|
12
|
+
@registry = registry
|
|
13
|
+
@namespace_object = namespace_object
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Array<Functions::Base>]
|
|
17
|
+
def methods(visibility: nil)
|
|
18
|
+
return [] if namespace_object.type == :proxy
|
|
19
|
+
@methods ||= begin
|
|
20
|
+
opts = { scope: :class, visibility: visibility }.compact
|
|
21
|
+
class_methods = namespace_object.meths(opts).map { |meth| Functions::Method.new(meth) } + constructors
|
|
22
|
+
class_method_names = Set.new(class_methods.map(&:name))
|
|
23
|
+
parent_meths = parent_methods(visibility: visibility).reject { |m| class_method_names.include?(m.name) }
|
|
24
|
+
class_methods + parent_meths
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [String]
|
|
29
|
+
def path
|
|
30
|
+
"#{namespace.path}.#{namespace.type == :class ? 'class' : 'module'}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def namespace
|
|
34
|
+
namespace_object
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param [String]
|
|
38
|
+
def docstring
|
|
39
|
+
namespace.docstring
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Array<[String, Integer]>]
|
|
43
|
+
def defined_files
|
|
44
|
+
namespace.files
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# @return [Array<Functions::Constructor>]
|
|
50
|
+
def constructors
|
|
51
|
+
[] unless namespace_object.type == :class
|
|
52
|
+
[] if namespace.child(name: :new, scope: :class)
|
|
53
|
+
[namespace.child(name: :initialize, scope: :instance)].map do |method_object|
|
|
54
|
+
Functions::Constructor.new(method_object)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Array<Functions::Base>]
|
|
59
|
+
def parent_methods(visibility: nil)
|
|
60
|
+
case namespace_object.type
|
|
61
|
+
when :class
|
|
62
|
+
InstanceValue.new(registry, registry.find_or_proxy('::Class')).methods(visibility: visibility)
|
|
63
|
+
when :module
|
|
64
|
+
InstanceValue.new(registry, registry.find_or_proxy('::Module')).methods(visibility: visibility)
|
|
65
|
+
else
|
|
66
|
+
[]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/yoda/parsing.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
require 'yoda/parsing/ast_traversable'
|
|
4
|
+
require 'yoda/parsing/comment_tokenizer'
|
|
5
|
+
require 'yoda/parsing/parser'
|
|
6
|
+
require 'yoda/parsing/source_analyzer'
|
|
7
|
+
require 'yoda/parsing/node_objects'
|
|
8
|
+
require 'yoda/parsing/location'
|
|
9
|
+
require 'yoda/parsing/scopes'
|
|
10
|
+
require 'yoda/parsing/source_cutter'
|
|
11
|
+
require 'yoda/parsing/range'
|
|
12
|
+
require 'yoda/parsing/query'
|
|
13
|
+
require 'yoda/parsing/type_parser'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
module AstTraversable
|
|
4
|
+
# @param root_node [Array<::Parser::AST::Node>]
|
|
5
|
+
# @param current_location [Parser::Source::Map]
|
|
6
|
+
# @return [Array<::Parser::AST::Node>]
|
|
7
|
+
def calc_nodes_to_current_location(root_node, current_location)
|
|
8
|
+
nodes = [root_node]
|
|
9
|
+
node = root_node
|
|
10
|
+
while node && !node.children.empty?
|
|
11
|
+
node = node.children.find { |n| n.respond_to?(:location) && current_location.included?(n.location) }
|
|
12
|
+
nodes << node if node && node.is_a?(::Parser::AST::Node)
|
|
13
|
+
end
|
|
14
|
+
nodes
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'parslet'
|
|
2
|
+
|
|
3
|
+
module Yoda
|
|
4
|
+
module Parsing
|
|
5
|
+
class CommentTokenizer
|
|
6
|
+
# @return [Sequence]
|
|
7
|
+
def parse(str)
|
|
8
|
+
Generator.new.apply(Tokenizer.new.parse(str))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Tokenizer < Parslet::Parser
|
|
12
|
+
rule(:space) { match('\s').repeat(1) }
|
|
13
|
+
rule(:space?) { space.maybe }
|
|
14
|
+
|
|
15
|
+
rule(:comment_begin) { str('#') }
|
|
16
|
+
rule(:tag) { str('@') >> (str('!').maybe >> match('[a-zA-Z0-9_-]').repeat) }
|
|
17
|
+
|
|
18
|
+
rule(:name) { (sign.absent? >> match['[:graph:]']).repeat(1) }
|
|
19
|
+
rule(:sign) { match['\[\]<>,{}\(\)'] }
|
|
20
|
+
|
|
21
|
+
rule(:comment_token) { sign | name }
|
|
22
|
+
|
|
23
|
+
rule(:base) { comment_begin.maybe >> space? >> tag.maybe.as(:tag) >> space? >> (comment_token.as(:token) >> space?).repeat.as(:tokens) }
|
|
24
|
+
root :base
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Generator < Parslet::Transform
|
|
28
|
+
rule(token: simple(:token)) { token }
|
|
29
|
+
rule(tag: simple(:tag), tokens: sequence(:tokens)) { Sequence.new(tag: tag, tokens: tokens) }
|
|
30
|
+
rule(tag: simple(:tag), tokens: simple(:token)) { Sequence.new(tag: tag, tokens: [token]) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Sequence
|
|
34
|
+
# @type Parslet::Slice | nil
|
|
35
|
+
attr_reader :tag
|
|
36
|
+
|
|
37
|
+
# @param tag [Parslet::Slice, nil]
|
|
38
|
+
# @param tokens [Array<Parslet::Slice>]
|
|
39
|
+
def initialize(tag: nil, tokens: [])
|
|
40
|
+
fail ArgumentError, tag if tag && !tag.is_a?(Parslet::Slice)
|
|
41
|
+
fail ArgumentError, tokens unless tokens.all? { |token| token.is_a?(Parslet::Slice) }
|
|
42
|
+
|
|
43
|
+
@tag = tag
|
|
44
|
+
@tokens = tokens
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Array<Parslet::Slice>]
|
|
48
|
+
def all_tokens
|
|
49
|
+
@all_tokens ||= [@tag, *parameter_tokens].compact
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Array<Parslet::Slice>]
|
|
53
|
+
def parameter_tokens
|
|
54
|
+
@tokens
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
class Location
|
|
4
|
+
include Comparable
|
|
5
|
+
|
|
6
|
+
# @todo Make this 0-indexed.
|
|
7
|
+
# @return [Integer] 0-indexed column number.
|
|
8
|
+
attr_reader :row
|
|
9
|
+
|
|
10
|
+
# @return [Integer] 0-indexed column number.
|
|
11
|
+
attr_reader :column
|
|
12
|
+
|
|
13
|
+
# @param row [Integer] 1-indexed row number.
|
|
14
|
+
# @param column [Integer] 0-indexed column number.
|
|
15
|
+
def initialize(row:, column:)
|
|
16
|
+
@row = row
|
|
17
|
+
@column = column
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param ast_location [Parser::Source::Map, Parser::Source::Range]
|
|
21
|
+
# @return [Location, nil]
|
|
22
|
+
def self.of_ast_location(ast_location)
|
|
23
|
+
return nil unless valid_location?(ast_location)
|
|
24
|
+
Location.new(row: ast_location.line, column: ast_location.column)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param line [Integer]
|
|
28
|
+
# @param character [Integer]
|
|
29
|
+
# @return [Location]
|
|
30
|
+
def self.of_language_server_protocol_position(line:, character:)
|
|
31
|
+
new(row: line + 1, column: character)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param location [Parser::Source::Range, Parser::Source::Map, Object]
|
|
35
|
+
def self.valid_location?(location)
|
|
36
|
+
return false if !location.is_a?(::Parser::Source::Range) && !location.is_a?(::Parser::Source::Map)
|
|
37
|
+
return false if location.is_a?(::Parser::Source::Map) && !location.expression
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
42
|
+
def index_of(source)
|
|
43
|
+
(source.split("\n").slice(0, row - 1) || []).map(&:length).reduce(0, &:+) + column
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
47
|
+
def included?(location)
|
|
48
|
+
return false unless self.class.valid_location?(location)
|
|
49
|
+
after_begin(location) && before_last(location)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
53
|
+
def later_than?(location)
|
|
54
|
+
move(row: 0, column: -1).after_begin(location)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
58
|
+
def after_begin(location)
|
|
59
|
+
return false unless self.class.valid_location?(location)
|
|
60
|
+
(location.line == row && location.column <= column) || location.line < row
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
64
|
+
def before_last(location)
|
|
65
|
+
return false unless self.class.valid_location?(location)
|
|
66
|
+
(location.last_line == row && column <= location.last_column ) || row < location.last_line
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @param location [Parser::Source::Range, Parser::Source::Map]
|
|
70
|
+
# @return [{Symbol => Numerical}]
|
|
71
|
+
def offset_from_begin(location)
|
|
72
|
+
fail ArgumentError, location unless self.class.valid_location?(location)
|
|
73
|
+
{ line: row - location.line, column: column - location.column }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @param row [Integer]
|
|
77
|
+
# @param column [Integer]
|
|
78
|
+
# @return [Location]
|
|
79
|
+
def move(row:, column:)
|
|
80
|
+
self.class.new(row: @row + row, column: @column + column)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @return [{Symbol => Integer}]
|
|
84
|
+
def to_language_server_protocol_range
|
|
85
|
+
{ line: row - 1, character: column }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def to_s
|
|
89
|
+
"(#{row}, #{column})"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @param another [Location]
|
|
93
|
+
# @return [Integer]
|
|
94
|
+
def <=>(another)
|
|
95
|
+
return 0 if row == another.row && column == another.column
|
|
96
|
+
return 1 if (row == another.row && column >= another.column) || row > another.row
|
|
97
|
+
-1
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
module NodeObjects
|
|
4
|
+
require 'yoda/parsing/node_objects/const_node'
|
|
5
|
+
require 'yoda/parsing/node_objects/send_node'
|
|
6
|
+
require 'yoda/parsing/node_objects/method_definition'
|
|
7
|
+
require 'yoda/parsing/node_objects/namespace'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
module NodeObjects
|
|
4
|
+
class ConstNode
|
|
5
|
+
# @param node [::AST::Node]
|
|
6
|
+
attr_reader :node
|
|
7
|
+
|
|
8
|
+
# @param node [::AST::Node]
|
|
9
|
+
def initialize(node)
|
|
10
|
+
fail ArgumentError, node unless node.is_a?(::AST::Node) && node.type == :const
|
|
11
|
+
@node = node
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [ConstNode, nil]
|
|
15
|
+
def parent_const
|
|
16
|
+
node.children.first && node.children.first.type == :const ? ConstNode.new(node.children.first) : nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [true, false]
|
|
20
|
+
def absolute?
|
|
21
|
+
node.children.first == :cbase
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param location [Location]
|
|
25
|
+
# @return [true, false]
|
|
26
|
+
def just_after_separator?(location)
|
|
27
|
+
return false unless node.location.double_colon
|
|
28
|
+
location == Location.of_ast_location(node.location.double_colon.end)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Model::Path]
|
|
32
|
+
def to_path
|
|
33
|
+
Model::Path.new(to_s)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param base [String, Symbol, nil]
|
|
37
|
+
# @return [String]
|
|
38
|
+
def to_s(base = nil)
|
|
39
|
+
fail ArgumentError, base unless !base || base.is_a?(String) || base.is_a?(Symbol)
|
|
40
|
+
paths = []
|
|
41
|
+
looking_node = node
|
|
42
|
+
while true
|
|
43
|
+
return (base ? base.to_s + '::' : '') + paths.join('::') unless looking_node
|
|
44
|
+
return '::' + paths.join('::') if looking_node.type == :cbase
|
|
45
|
+
paths.unshift(looking_node.children[1])
|
|
46
|
+
looking_node = looking_node.children[0]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
module NodeObjects
|
|
4
|
+
class MethodDefinition
|
|
5
|
+
|
|
6
|
+
# @return [::Parser::AST::Node]
|
|
7
|
+
attr_reader :node
|
|
8
|
+
|
|
9
|
+
# @return [Namespace]
|
|
10
|
+
attr_reader :namespace
|
|
11
|
+
|
|
12
|
+
# @param node [::Parser::AST::Node]
|
|
13
|
+
# @param namespace [Namespace]
|
|
14
|
+
def initialize(node, namespace)
|
|
15
|
+
fail ArgumentError, node unless node.is_a?(::Parser::AST::Node)
|
|
16
|
+
fail ArgumentError, namespace unless namespace.is_a?(Namespace)
|
|
17
|
+
@node = node
|
|
18
|
+
@namespace = namespace
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Symbol]
|
|
22
|
+
def name
|
|
23
|
+
node.children[-3]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def arg_node
|
|
27
|
+
node.children[-2]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def body_node
|
|
31
|
+
node.children[-1]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [String]
|
|
35
|
+
def full_name
|
|
36
|
+
"#{namespace.full_name}##{name}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [String]
|
|
40
|
+
def namespace_name
|
|
41
|
+
namespace.full_name
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module Yoda
|
|
2
|
+
module Parsing
|
|
3
|
+
module NodeObjects
|
|
4
|
+
class Namespace
|
|
5
|
+
include AstTraversable
|
|
6
|
+
|
|
7
|
+
# @return [::Parser::AST::Node]
|
|
8
|
+
attr_reader :node
|
|
9
|
+
|
|
10
|
+
# @return [Namespace, nil]
|
|
11
|
+
attr_reader :parent
|
|
12
|
+
|
|
13
|
+
# @param node [::Parser::AST::Node]
|
|
14
|
+
# @param parent [Namespace, nil]
|
|
15
|
+
def initialize(node, parent = nil)
|
|
16
|
+
fail ArgumentError, node unless node.is_a?(::Parser::AST::Node)
|
|
17
|
+
fail ArgumentError, parent unless !parent || parent.is_a?(Namespace)
|
|
18
|
+
@node = node
|
|
19
|
+
@parent = parent
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [::Parser::AST::Node]
|
|
23
|
+
def body_node
|
|
24
|
+
return node if type == :root
|
|
25
|
+
return node.children[2] if type == :class
|
|
26
|
+
node.children[1]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [::Parser::AST::Node, nil]
|
|
30
|
+
def const_node
|
|
31
|
+
%i(root sclass).include?(type) ? nil : node.children[0]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Namespace]
|
|
35
|
+
def child_namespaces
|
|
36
|
+
@child_namespaces ||= child_nodes_of(body_node).select { |node| %i(module class sclass).include?(node.type) }.map { |node| self.class.new(node, self) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Wrappers::MethodNodeWrapper]
|
|
40
|
+
def child_methods
|
|
41
|
+
@child_methods ||= child_nodes_of(body_node).select { |node| %i(def defs).include?(node.type) }.map { |node| MethodDefinition.new(node, self) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def type
|
|
45
|
+
@type ||= begin
|
|
46
|
+
return node.type if %i(module class sclass).include?(node.type)
|
|
47
|
+
:root
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [String]
|
|
52
|
+
def path
|
|
53
|
+
name = full_name
|
|
54
|
+
name == :root ? '' : name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [true, false]
|
|
58
|
+
def root?
|
|
59
|
+
type == :root
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [String, Symbol]
|
|
63
|
+
def full_name
|
|
64
|
+
return :root if type == :root
|
|
65
|
+
parent_name = parent && !parent.root? ? parent.full_name : ''
|
|
66
|
+
const_node ? ConstNode.new(const_node).to_s(parent_name) : parent_name
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Array<String>]
|
|
70
|
+
def paths_from_root
|
|
71
|
+
if root?
|
|
72
|
+
[path]
|
|
73
|
+
else
|
|
74
|
+
parent ? parent.paths_from_root + [path] : ['', path]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param location [Location]
|
|
79
|
+
# @return [Namespace, nil]
|
|
80
|
+
def calc_current_location_namespace(location)
|
|
81
|
+
return nil unless location.included?(node.location)
|
|
82
|
+
including_child_namespace = child_namespaces.find { |namespace| location.included?(namespace.node.location) }
|
|
83
|
+
including_child_namespace ? including_child_namespace.calc_current_location_namespace(location) : self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @param location [Location]
|
|
87
|
+
# @return [MethodNodeWrapper, nil]
|
|
88
|
+
def calc_current_location_method(location)
|
|
89
|
+
namespace = calc_current_location_namespace(location)
|
|
90
|
+
namespace && namespace.child_methods.find { |method| location.included?(method.node.location) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def child_nodes_of(node)
|
|
96
|
+
# @todo evaluate nodes in the namespace
|
|
97
|
+
return [] unless node
|
|
98
|
+
return node.children.map { |child| child_nodes_of(child) }.flatten.compact if node.type == :begin
|
|
99
|
+
[node]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|