rucoa 0.6.0 → 0.8.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +4 -1
  4. data/data/definitions_ruby_3_1 +0 -0
  5. data/lib/rucoa/configuration.rb +10 -0
  6. data/lib/rucoa/definition_store.rb +113 -6
  7. data/lib/rucoa/definitions/class_definition.rb +16 -0
  8. data/lib/rucoa/definitions/method_definition.rb +14 -2
  9. data/lib/rucoa/definitions/module_definition.rb +8 -0
  10. data/lib/rucoa/definitions.rb +2 -0
  11. data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +14 -2
  12. data/lib/rucoa/handlers/initialize_handler.rb +1 -0
  13. data/lib/rucoa/handlers/initialized_handler.rb +1 -1
  14. data/lib/rucoa/handlers/text_document_completion_handler.rb +9 -2
  15. data/lib/rucoa/handlers/text_document_definition_handler.rb +155 -0
  16. data/lib/rucoa/handlers/text_document_did_close_handler.rb +20 -0
  17. data/lib/rucoa/handlers/text_document_formatting_handler.rb +2 -2
  18. data/lib/rucoa/handlers/text_document_hover_handler.rb +3 -4
  19. data/lib/rucoa/handlers/text_document_range_formatting_handler.rb +2 -2
  20. data/lib/rucoa/handlers.rb +2 -0
  21. data/lib/rucoa/node_inspector.rb +48 -15
  22. data/lib/rucoa/nodes/base.rb +2 -2
  23. data/lib/rucoa/nodes/const_node.rb +31 -0
  24. data/lib/rucoa/position.rb +1 -1
  25. data/lib/rucoa/rbs/class_definition_mapper.rb +46 -0
  26. data/lib/rucoa/{definition_builders/rbs_constant_definition_builder.rb → rbs/constant_definition_mapper.rb} +4 -8
  27. data/lib/rucoa/{definition_builders/rbs_method_definition_builder.rb → rbs/method_definition_mapper.rb} +4 -7
  28. data/lib/rucoa/rbs/module_definition_mapper.rb +40 -0
  29. data/lib/rucoa/rbs/ruby_definitions_loader.rb +82 -0
  30. data/lib/rucoa/rbs.rb +11 -0
  31. data/lib/rucoa/rubocop/autocorrector.rb +51 -0
  32. data/lib/rucoa/rubocop/configuration_checker.rb +44 -0
  33. data/lib/rucoa/rubocop/investigator.rb +59 -0
  34. data/lib/rucoa/rubocop.rb +9 -0
  35. data/lib/rucoa/server.rb +2 -0
  36. data/lib/rucoa/source.rb +11 -3
  37. data/lib/rucoa/version.rb +1 -1
  38. data/lib/rucoa/yard/definitions_loader.rb +85 -0
  39. data/lib/rucoa/{definition_builders/yard_method_definition_builder.rb → yard/method_definition_mapper.rb} +22 -25
  40. data/lib/rucoa/yard.rb +8 -0
  41. data/lib/rucoa.rb +3 -7
  42. metadata +19 -12
  43. data/lib/rucoa/definition_builders.rb +0 -9
  44. data/lib/rucoa/rbs_document_loader.rb +0 -43
  45. data/lib/rucoa/rubocop_autocorrector.rb +0 -38
  46. data/lib/rucoa/rubocop_configuration_checker.rb +0 -42
  47. data/lib/rucoa/rubocop_investigator.rb +0 -48
  48. data/lib/rucoa/yard_glob_document_loader.rb +0 -47
  49. data/lib/rucoa/yard_string_document_loader.rb +0 -70
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module Rucoa
6
4
  class NodeInspector
7
5
  # @param definition_store [Rucoa::DefinitionStore]
@@ -11,10 +9,24 @@ module Rucoa
11
9
  @node = node
12
10
  end
13
11
 
14
- # @return [Array<String>]
12
+ # @return [Array<Rucoa::Definitions::Base>]
13
+ def definitions
14
+ case @node
15
+ when Nodes::ConstNode
16
+ [constant_definition]
17
+ when Nodes::SendNode
18
+ method_definitions
19
+ else
20
+ []
21
+ end
22
+ end
23
+
24
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
15
25
  def method_definitions
16
- method_full_qualified_names.flat_map do |full_qualified_name|
17
- @definition_store.select_by_full_qualified_name(full_qualified_name)
26
+ method_receiver_types.flat_map do |type|
27
+ @definition_store.instance_method_definitions_of(type)
28
+ end.select do |method_definition|
29
+ method_definition.method_name == @node.name
18
30
  end
19
31
  end
20
32
 
@@ -36,7 +48,7 @@ module Rucoa
36
48
  def return_types
37
49
  case @node.type
38
50
  when :const
39
- [@node.name]
51
+ ["singleton<#{@node.name}>"]
40
52
  when :lvar
41
53
  return_types_for_lvar
42
54
  when :send
@@ -74,16 +86,33 @@ module Rucoa
74
86
 
75
87
  private
76
88
 
77
- # @return [Array<String>]
78
- def method_full_qualified_names
79
- method_receiver_types.map do |type|
89
+ # @return [Rucoa::Definitions::ConstantDefinition, nil]
90
+ def constant_definition
91
+ return unless @node.is_a?(Nodes::ConstNode)
92
+
93
+ module_nesting_full_qualified_names = @node.each_ancestor(:class, :module).map(&:full_qualified_name)
94
+ candidate_full_qualified_names = module_nesting_full_qualified_names.flat_map do |module_nesting_full_qualified_name|
80
95
  [
81
- type,
82
- @node.name
83
- ].join('#')
96
+ module_nesting_full_qualified_name
97
+ # TODO: *ancestors_of(module_nesting_full_qualified_name)
98
+ ].map do |full_qualified_name|
99
+ [
100
+ full_qualified_name,
101
+ @node.chained_name
102
+ ].join('::')
103
+ end
104
+ end + [@node.chained_name]
105
+ candidate_full_qualified_names.find do |candidate_full_qualified_name|
106
+ definition = @definition_store.select_by_full_qualified_name(candidate_full_qualified_name).first
107
+ break definition if definition
84
108
  end
85
109
  end
86
110
 
111
+ # @return [Boolean]
112
+ def singleton_method_call?
113
+ @node.is_a?(Nodes::ConstNode)
114
+ end
115
+
87
116
  # @return [String, nil]
88
117
  def nearest_def_full_qualified_name
89
118
  @node.each_ancestor(:def).first&.full_qualified_name
@@ -103,9 +132,13 @@ module Rucoa
103
132
 
104
133
  # @return [Array<String>]
105
134
  def return_types_for_send
106
- method_full_qualified_names.flat_map do |full_qualified_name|
107
- @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map(&:return_types)
108
- end.uniq
135
+ method_receiver_types.flat_map do |type|
136
+ @definition_store.find_method_definition_by(
137
+ method_name: @node.name,
138
+ namespace: type,
139
+ singleton: singleton_method_call?
140
+ )&.return_types
141
+ end.compact
109
142
  end
110
143
  end
111
144
  end
@@ -121,8 +121,8 @@ module Rucoa
121
121
  # @param types [Array<Symbol>]
122
122
  # @return [void]
123
123
  def visit_descendants(types, &block)
124
- each_child_node(*types) do |child|
125
- yield(child)
124
+ each_child_node do |child|
125
+ yield(child) if types.empty? || types.include?(child.type)
126
126
  child.visit_descendants(types, &block)
127
127
  end
128
128
  end
@@ -4,9 +4,40 @@ module Rucoa
4
4
  module Nodes
5
5
  class ConstNode < Base
6
6
  # @return [String]
7
+ # @example returns "A" for "A"
8
+ # node = Rucoa::Parser.call('A')
9
+ # expect(node.name).to eq('A')
10
+ # @example returns "B" for "A::B"
11
+ # node = Rucoa::Parser.call('A::B')
12
+ # expect(node.name).to eq('B')
7
13
  def name
8
14
  children[1].to_s
9
15
  end
16
+
17
+ # @return [String]
18
+ # @example returns "A" for "A"
19
+ # node = Rucoa::Parser.call('A')
20
+ # expect(node.chained_name).to eq('A')
21
+ # @example returns "A::B" for "A::B"
22
+ # node = Rucoa::Parser.call('A::B')
23
+ # expect(node.chained_name).to eq('A::B')
24
+ def chained_name
25
+ if receiver.is_a?(ConstNode)
26
+ [
27
+ receiver.chained_name,
28
+ name
29
+ ].join('::')
30
+ else
31
+ name
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # @return [Rucoa::Nodes::Base, nil]
38
+ def receiver
39
+ children[0]
40
+ end
10
41
  end
11
42
  end
12
43
  end
@@ -39,7 +39,7 @@ module Rucoa
39
39
 
40
40
  # @param column [Integer] 0-origin column number
41
41
  # @param line [Integer] 1-origin line number
42
- def initialize(column:, line:)
42
+ def initialize(column: 0, line: 1)
43
43
  @column = column
44
44
  @line = line
45
45
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rbs
5
+ class ClassDefinitionMapper
6
+ class << self
7
+ # @param declaration [RBS::AST::Declarations::Class]
8
+ # @return [Rucoa::Definitions::ClassDefinition]
9
+ def call(declaration:)
10
+ new(declaration: declaration).call
11
+ end
12
+ end
13
+
14
+ # @param declaration [RBS::AST::Declarations::Class]
15
+ def initialize(declaration:)
16
+ @declaration = declaration
17
+ end
18
+
19
+ # @return [Rucoa::Definitions::ClassDefinition]
20
+ def call
21
+ Definitions::ClassDefinition.new(
22
+ full_qualified_name: full_qualified_name,
23
+ source_path: source_path,
24
+ super_class_name: super_class_name
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ # @return [String]
31
+ def full_qualified_name
32
+ @declaration.name.to_s.delete_prefix('::')
33
+ end
34
+
35
+ # @return [String]
36
+ def source_path
37
+ @declaration.location.name
38
+ end
39
+
40
+ # @return [String, nil]
41
+ def super_class_name
42
+ @declaration.super_class&.name&.to_s&.delete_prefix('::')
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,28 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rucoa
4
- module DefinitionBuilders
5
- # Build Rucoa contant definition from RBS constant declaration.
6
- class RbsConstantDefinitionBuilder
4
+ module Rbs
5
+ class ConstantDefinitionMapper
7
6
  class << self
8
7
  # @param declaration [RBS::AST::Declarations::Constant]
9
8
  # @return [Rucoa::Definitions::ConstantDefinition]
10
9
  def call(declaration:)
11
- new(
12
- declaration: declaration
13
- ).call
10
+ new(declaration: declaration).call
14
11
  end
15
12
  end
16
13
 
17
14
  # @param declaration [RBS::AST::Declarations::Constant]
18
- # @return [Rucoa::Definitions::ConstantDefinition]
19
15
  def initialize(declaration:)
20
16
  @declaration = declaration
21
17
  end
22
18
 
23
19
  # @return [Rucoa::Definitions::ConstantDefinition]
24
20
  def call
25
- ::Rucoa::Definitions::ConstantDefinition.new(
21
+ Definitions::ConstantDefinition.new(
26
22
  full_qualified_name: full_qualified_name,
27
23
  source_path: source_path
28
24
  )
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rbs'
4
-
5
3
  module Rucoa
6
- module DefinitionBuilders
7
- # Build Rucoa method definition from RBS method definition.
8
- class RbsMethodDefinitionBuilder
4
+ module Rbs
5
+ class MethodDefinitionMapper
9
6
  class << self
10
7
  # @param declaration [RBS::AST::Declarations::Class, RBS::AST::Declarations::Module]
11
8
  # @param method_definition [RBS::AST::Members::MethodDefinition]
@@ -42,7 +39,7 @@ module Rucoa
42
39
  # @return [Array<Rucoa::Types::MethodType>]
43
40
  def types
44
41
  @method_definition.types.map do |method_type|
45
- RbsMethodTypeToRucoaMethodTypeMapper.call(
42
+ MethodTypeMapper.call(
46
43
  method_type: method_type
47
44
  )
48
45
  end
@@ -73,7 +70,7 @@ module Rucoa
73
70
  @declaration.location.name
74
71
  end
75
72
 
76
- class RbsMethodTypeToRucoaMethodTypeMapper
73
+ class MethodTypeMapper
77
74
  class << self
78
75
  # @param method_type [RBS::Types::MethodType]
79
76
  # @return [Rucoa::Types::MethodType]
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rbs
5
+ class ModuleDefinitionMapper
6
+ class << self
7
+ # @param declaration [RBS::AST::Declarations::Module]
8
+ # @return [Rucoa::Definitions::ModuleDefinition]
9
+ def call(declaration:)
10
+ new(declaration: declaration).call
11
+ end
12
+ end
13
+
14
+ # @param declaration [RBS::AST::Declarations::Module]
15
+ def initialize(declaration:)
16
+ @declaration = declaration
17
+ end
18
+
19
+ # @return [Rucoa::Definitions::ModuleDefinition]
20
+ def call
21
+ Definitions::ModuleDefinition.new(
22
+ full_qualified_name: full_qualified_name,
23
+ source_path: source_path
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ # @return [String]
30
+ def full_qualified_name
31
+ @declaration.name.to_s.delete_prefix('::')
32
+ end
33
+
34
+ # @return [String]
35
+ def source_path
36
+ @declaration.location.name
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbs'
4
+
5
+ module Rucoa
6
+ module Rbs
7
+ # Load definitions for Ruby core and standard libraries.
8
+ class RubyDefinitionsLoader
9
+ class << self
10
+ # @return [Array<Rucoa::Definitions::Base>]
11
+ def call
12
+ new.call
13
+ end
14
+ end
15
+
16
+ # @return [Array<Rucoa::Definitions::Base>]
17
+ def call
18
+ declarations.flat_map do |declaration|
19
+ case declaration
20
+ when ::RBS::AST::Declarations::Constant
21
+ [ConstantDefinitionMapper.call(declaration: declaration)]
22
+ when ::RBS::AST::Declarations::Class
23
+ [ClassDefinitionMapper.call(declaration: declaration)] +
24
+ declaration.members.grep(::RBS::AST::Members::MethodDefinition).map do |method_definition|
25
+ MethodDefinitionMapper.call(
26
+ declaration: declaration,
27
+ method_definition: method_definition
28
+ )
29
+ end
30
+ when ::RBS::AST::Declarations::Module
31
+ [ModuleDefinitionMapper.call(declaration: declaration)] +
32
+ declaration.members.grep(::RBS::AST::Members::MethodDefinition).map do |method_definition|
33
+ MethodDefinitionMapper.call(
34
+ declaration: declaration,
35
+ method_definition: method_definition
36
+ )
37
+ end
38
+ else
39
+ []
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # @return [Array<RBS::AST::Declarations::Base>]
47
+ def declarations
48
+ class_declarations + constant_declarations
49
+ end
50
+
51
+ # @return [Array<RBS::AST::Declarations::Class>]
52
+ def class_declarations
53
+ environment.class_decls.values.flat_map do |multi_entry|
54
+ multi_entry.decls.map(&:decl)
55
+ end
56
+ end
57
+
58
+ # @return [Array<RBS::AST::Declarations::Constant>]
59
+ def constant_declarations
60
+ environment.constant_decls.values.map(&:decl)
61
+ end
62
+
63
+ # @return [RBS::Environment]
64
+ def environment
65
+ @environment ||= ::RBS::Environment.from_loader(
66
+ environment_loader
67
+ ).resolve_type_names
68
+ end
69
+
70
+ # @return [RBS::EnvironmentLoader]
71
+ def environment_loader
72
+ loader = ::RBS::EnvironmentLoader.new
73
+ ::RBS::Repository::DEFAULT_STDLIB_ROOT.children.sort.each do |pathname|
74
+ loader.add(
75
+ library: pathname.basename.to_s
76
+ )
77
+ end
78
+ loader
79
+ end
80
+ end
81
+ end
82
+ end
data/lib/rucoa/rbs.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rbs
5
+ autoload :ClassDefinitionMapper, 'rucoa/rbs/class_definition_mapper'
6
+ autoload :ConstantDefinitionMapper, 'rucoa/rbs/constant_definition_mapper'
7
+ autoload :MethodDefinitionMapper, 'rucoa/rbs/method_definition_mapper'
8
+ autoload :ModuleDefinitionMapper, 'rucoa/rbs/module_definition_mapper'
9
+ autoload :RubyDefinitionsLoader, 'rucoa/rbs/ruby_definitions_loader'
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Rucoa
6
+ module Rubocop
7
+ class Autocorrector < ::RuboCop::Runner
8
+ class << self
9
+ # @param source [Rucoa::Source]
10
+ # @return [String]
11
+ def call(source:)
12
+ new(source: source).call
13
+ end
14
+ end
15
+
16
+ # @param source [Rucoa::Source]
17
+ def initialize(source:)
18
+ @source = source
19
+ super(
20
+ ::RuboCop::Options.new.parse(
21
+ %w[
22
+ --stderr
23
+ --force-exclusion
24
+ --format RuboCop::Formatter::BaseFormatter
25
+ -A
26
+ ]
27
+ ).first,
28
+ ::RuboCop::ConfigStore.new
29
+ )
30
+ end
31
+
32
+ # @return [String]
33
+ def call
34
+ @options[:stdin] = @source.content
35
+ run([path])
36
+ @options[:stdin]
37
+ end
38
+
39
+ private
40
+
41
+ # @return [String]
42
+ def path
43
+ if @source.untitled? || @source.path.nil?
44
+ 'untitled'
45
+ else
46
+ @source.path
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module Rucoa
6
+ module Rubocop
7
+ class ConfigurationChecker
8
+ class << self
9
+ # @return [Boolean]
10
+ def call
11
+ new.call
12
+ end
13
+ end
14
+
15
+ # @return [Boolean]
16
+ def call
17
+ rubocop_configured_for_current_directory?
18
+ end
19
+
20
+ private
21
+
22
+ # @return [Boolean]
23
+ def rubocop_configured_for_current_directory?
24
+ each_current_and_ancestor_pathname.any? do |pathname|
25
+ pathname.join('.rubocop.yml').exist?
26
+ end
27
+ end
28
+
29
+ # @return [Enumerable<Pathname>]
30
+ def each_current_and_ancestor_pathname
31
+ return to_enum(__method__) unless block_given?
32
+
33
+ pathname = ::Pathname.pwd
34
+ loop do
35
+ yield pathname
36
+ break if pathname.root?
37
+
38
+ pathname = pathname.parent
39
+ end
40
+ self
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Rucoa
6
+ module Rubocop
7
+ class Investigator < ::RuboCop::Runner
8
+ class << self
9
+ # @param source [Rucoa::Source]
10
+ # @return [Array<RuboCop::Cop::Offense>]
11
+ def call(source:)
12
+ new(source: source).call
13
+ end
14
+ end
15
+
16
+ # @param source [Rucoa::Source]
17
+ def initialize(source:)
18
+ @source = source
19
+ @offenses = []
20
+ super(
21
+ ::RuboCop::Options.new.parse(
22
+ %w[
23
+ --stderr
24
+ --force-exclusion
25
+ --format RuboCop::Formatter::BaseFormatter
26
+ ]
27
+ ).first,
28
+ ::RuboCop::ConfigStore.new
29
+ )
30
+ end
31
+
32
+ # @return [Array<RuboCop::Cop::Offense>]
33
+ def call
34
+ @options[:stdin] = @source.content
35
+ run([path])
36
+ @offenses
37
+ end
38
+
39
+ private
40
+
41
+ # @param file [String]
42
+ # @param offenses [Array<RuboCop::Cop::Offense>]
43
+ # @return [void]
44
+ def file_finished(file, offenses)
45
+ @offenses = offenses
46
+ super
47
+ end
48
+
49
+ # @return [String]
50
+ def path
51
+ if @source.untitled? || @source.path.nil?
52
+ 'untitled'
53
+ else
54
+ @source.path
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rubocop
5
+ autoload :Autocorrector, 'rucoa/rubocop/autocorrector'
6
+ autoload :ConfigurationChecker, 'rucoa/rubocop/configuration_checker'
7
+ autoload :Investigator, 'rucoa/rubocop/investigator'
8
+ end
9
+ end
data/lib/rucoa/server.rb CHANGED
@@ -13,7 +13,9 @@ module Rucoa
13
13
  'shutdown' => Handlers::ShutdownHandler,
14
14
  'textDocument/codeAction' => Handlers::TextDocumentCodeActionHandler,
15
15
  'textDocument/completion' => Handlers::TextDocumentCompletionHandler,
16
+ 'textDocument/definition' => Handlers::TextDocumentDefinitionHandler,
16
17
  'textDocument/didChange' => Handlers::TextDocumentDidChangeHandler,
18
+ 'textDocument/didClose' => Handlers::TextDocumentDidCloseHandler,
17
19
  'textDocument/didOpen' => Handlers::TextDocumentDidOpenHandler,
18
20
  'textDocument/documentSymbol' => Handlers::TextDocumentDocumentSymbolHandler,
19
21
  'textDocument/formatting' => Handlers::TextDocumentFormattingHandler,
data/lib/rucoa/source.rb CHANGED
@@ -29,11 +29,12 @@ module Rucoa
29
29
  # )
30
30
  # expect(source.definitions).to match(
31
31
  # [
32
+ # a_kind_of(Rucoa::Definitions::ClassDefinition),
32
33
  # a_kind_of(Rucoa::Definitions::MethodDefinition)
33
34
  # ]
34
35
  # )
35
36
  def definitions
36
- @definitions ||= YardStringDocumentLoader.call(
37
+ @definitions ||= Yard::DefinitionsLoader.load_string(
37
38
  content: @content,
38
39
  path: path
39
40
  )
@@ -46,15 +47,17 @@ module Rucoa
46
47
  # uri: 'file:///path/to/foo.rb'
47
48
  # )
48
49
  # expect(source.path).to eq('/path/to/foo.rb')
49
- # @example returns nil for untitled URI
50
+ # @example returns name for untitled URI
50
51
  # source = Rucoa::Source.new(
51
52
  # content: '',
52
53
  # uri: 'untitled:Untitled-1'
53
54
  # )
54
- # expect(source.path).to be_nil
55
+ # expect(source.path).to eq('Untitled-1')
55
56
  def path
56
57
  return unless @uri
57
58
 
59
+ return @uri.split(':', 2).last if untitled?
60
+
58
61
  path = ::URI.parse(@uri).path
59
62
  return unless path
60
63
 
@@ -83,6 +86,11 @@ module Rucoa
83
86
  root_node.nil?
84
87
  end
85
88
 
89
+ # @return [Boolean]
90
+ def untitled?
91
+ @uri&.start_with?('untitled:')
92
+ end
93
+
86
94
  private
87
95
 
88
96
  # @return [Array<Rucoa::Nodes::Base>]
data/lib/rucoa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rucoa
4
- VERSION = '0.6.0'
4
+ VERSION = '0.8.0'
5
5
  end