rucoa 0.6.0 → 0.8.0

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