rucoa 0.4.0 → 0.6.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +12 -2
  5. data/README.md +47 -17
  6. data/data/definitions_ruby_3_1 +0 -0
  7. data/images/diagnostics.gif +0 -0
  8. data/images/document-formatting.gif +0 -0
  9. data/images/document-symbol.gif +0 -0
  10. data/images/selection-ranges.gif +0 -0
  11. data/lib/rucoa/cli.rb +3 -2
  12. data/lib/rucoa/configuration.rb +111 -32
  13. data/lib/rucoa/definition_archiver.rb +29 -0
  14. data/lib/rucoa/definition_builders/rbs_constant_definition_builder.rb +44 -0
  15. data/lib/rucoa/definition_builders/rbs_method_definition_builder.rb +106 -0
  16. data/lib/rucoa/definition_builders/yard_method_definition_builder.rb +218 -0
  17. data/lib/rucoa/definition_builders.rb +9 -0
  18. data/lib/rucoa/definition_store.rb +63 -0
  19. data/lib/rucoa/definitions/base.rb +12 -0
  20. data/lib/rucoa/definitions/constant_definition.rb +51 -0
  21. data/lib/rucoa/definitions/method_definition.rb +126 -0
  22. data/lib/rucoa/definitions/method_parameter_definition.rb +30 -0
  23. data/lib/rucoa/definitions.rb +9 -0
  24. data/lib/rucoa/handlers/base.rb +8 -0
  25. data/lib/rucoa/handlers/exit_handler.rb +11 -0
  26. data/lib/rucoa/handlers/initialize_handler.rb +13 -0
  27. data/lib/rucoa/handlers/initialized_handler.rb +10 -0
  28. data/lib/rucoa/handlers/shutdown_handler.rb +12 -0
  29. data/lib/rucoa/handlers/text_document_completion_handler.rb +216 -0
  30. data/lib/rucoa/handlers/text_document_did_change_handler.rb +1 -20
  31. data/lib/rucoa/handlers/text_document_did_open_handler.rb +11 -4
  32. data/lib/rucoa/handlers/text_document_document_symbol_handler.rb +5 -4
  33. data/lib/rucoa/handlers/text_document_hover_handler.rb +76 -0
  34. data/lib/rucoa/handlers/text_document_range_formatting_handler.rb +1 -1
  35. data/lib/rucoa/handlers/text_document_selection_range_handler.rb +3 -3
  36. data/lib/rucoa/handlers/text_document_signature_help_handler.rb +68 -0
  37. data/lib/rucoa/handlers.rb +5 -0
  38. data/lib/rucoa/node_inspector.rb +111 -0
  39. data/lib/rucoa/nodes/base.rb +30 -0
  40. data/lib/rucoa/nodes/def_node.rb +59 -0
  41. data/lib/rucoa/nodes/lvar_node.rb +25 -0
  42. data/lib/rucoa/nodes/send_node.rb +43 -0
  43. data/lib/rucoa/nodes.rb +1 -0
  44. data/lib/rucoa/parser_builder.rb +1 -0
  45. data/lib/rucoa/position.rb +14 -3
  46. data/lib/rucoa/range.rb +64 -14
  47. data/lib/rucoa/rbs_document_loader.rb +43 -0
  48. data/lib/rucoa/rubocop_autocorrector.rb +1 -1
  49. data/lib/rucoa/rubocop_investigator.rb +1 -1
  50. data/lib/rucoa/server.rb +63 -13
  51. data/lib/rucoa/source.rb +59 -4
  52. data/lib/rucoa/source_store.rb +6 -10
  53. data/lib/rucoa/types/method_type.rb +23 -0
  54. data/lib/rucoa/types.rb +7 -0
  55. data/lib/rucoa/version.rb +1 -1
  56. data/lib/rucoa/yard_glob_document_loader.rb +47 -0
  57. data/lib/rucoa/yard_string_document_loader.rb +70 -0
  58. data/lib/rucoa.rb +9 -0
  59. data/rucoa.gemspec +2 -0
  60. metadata +58 -2
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class TextDocumentCompletionHandler < Base
6
+ COMPLETION_ITEM_KIND_FOR_TEXT = 1
7
+ COMPLETION_ITEM_KIND_FOR_METHOD = 2
8
+ COMPLETION_ITEM_KIND_FOR_FUNCTION = 3
9
+ COMPLETION_ITEM_KIND_FOR_CONSTRUCTOR = 4
10
+ COMPLETION_ITEM_KIND_FOR_FIELD = 5
11
+ COMPLETION_ITEM_KIND_FOR_VARIABLE = 6
12
+ COMPLETION_ITEM_KIND_FOR_CLASS = 7
13
+ COMPLETION_ITEM_KIND_FOR_INTERFACE = 8
14
+ COMPLETION_ITEM_KIND_FOR_MODULE = 9
15
+ COMPLETION_ITEM_KIND_FOR_PROPERTY = 10
16
+ COMPLETION_ITEM_KIND_FOR_UNIT = 11
17
+ COMPLETION_ITEM_KIND_FOR_VALUE = 12
18
+ COMPLETION_ITEM_KIND_FOR_ENUM = 13
19
+ COMPLETION_ITEM_KIND_FOR_KEYWORD = 14
20
+ COMPLETION_ITEM_KIND_FOR_SNIPPET = 15
21
+ COMPLETION_ITEM_KIND_FOR_COLOR = 16
22
+ COMPLETION_ITEM_KIND_FOR_FILE = 17
23
+ COMPLETION_ITEM_KIND_FOR_REFERENCE = 18
24
+ COMPLETION_ITEM_KIND_FOR_FOLDER = 19
25
+ COMPLETION_ITEM_KIND_FOR_ENUM_MEMBER = 20
26
+ COMPLETION_ITEM_KIND_FOR_CONSTANT = 21
27
+ COMPLETION_ITEM_KIND_FOR_STRUCT = 22
28
+ COMPLETION_ITEM_KIND_FOR_EVENT = 23
29
+ COMPLETION_ITEM_KIND_FOR_OPERATOR = 24
30
+ COMPLETION_ITEM_KIND_FOR_TYPE_PARAMETER = 25
31
+
32
+ EXAMPLE_IDENTIFIER = 'a'
33
+ private_constant :EXAMPLE_IDENTIFIER
34
+
35
+ def call
36
+ respond(completion_items)
37
+ end
38
+
39
+ private
40
+
41
+ # @return [Array<Hash>, nil]
42
+ def completion_items
43
+ return unless responsible?
44
+
45
+ case node
46
+ when Nodes::ConstNode
47
+ completion_items_for_constant
48
+ when Nodes::SendNode
49
+ if node.location.dot&.is?('::')
50
+ completion_items_for_constant
51
+ else
52
+ completion_items_for_method
53
+ end
54
+ else
55
+ []
56
+ end
57
+ end
58
+
59
+ # @return [Boolean]
60
+ def responsible?
61
+ configuration.enables_completion? &&
62
+ !source.nil?
63
+ end
64
+
65
+ # @return [Rucoa::Source, nil]
66
+ def source
67
+ @source ||= source_store.get(uri)
68
+ end
69
+
70
+ # @return [Rucoa::Position]
71
+ def position
72
+ @position ||= Position.from_vscode_position(
73
+ request.dig('params', 'position')
74
+ )
75
+ end
76
+
77
+ # @return [String]
78
+ def uri
79
+ request.dig('params', 'textDocument', 'uri')
80
+ end
81
+
82
+ # @return [Array<Hash>]
83
+ def completion_items_for_method
84
+ completable_method_names.map do |method_name|
85
+ {
86
+ kind: COMPLETION_ITEM_KIND_FOR_METHOD,
87
+ label: method_name,
88
+ textEdit: {
89
+ newText: method_name,
90
+ range: range.to_vscode_range
91
+ }
92
+ }
93
+ end
94
+ end
95
+
96
+ # @return [Array<Hash>]
97
+ def completion_items_for_constant
98
+ completable_constant_names.map do |constant_name|
99
+ {
100
+ kind: COMPLETION_ITEM_KIND_FOR_CONSTANT,
101
+ label: constant_name,
102
+ textEdit: {
103
+ newText: constant_name,
104
+ range: range.to_vscode_range
105
+ }
106
+ }
107
+ end
108
+ end
109
+
110
+ # @return [Array<String>]
111
+ def completable_constant_names
112
+ referrable_constant_names.select do |constant_name|
113
+ constant_name.start_with?(completion_head)
114
+ end.sort
115
+ end
116
+
117
+ # @return [String] e.g. "SE" to `File::SE|`, "ba" to `foo.ba|`
118
+ def completion_head
119
+ @completion_head ||=
120
+ if @repaired
121
+ ''
122
+ else
123
+ node.name
124
+ end
125
+ end
126
+
127
+ def referrable_constant_names
128
+ definition_store.constant_definitions_under(constant_namespace).map(&:name).uniq
129
+ end
130
+
131
+ # @return [String] e.g. "Foo::Bar" to `Foo::Bar.baz|`.
132
+ def constant_namespace
133
+ node.each_child_node(:const).map(&:name).reverse.join('::')
134
+ end
135
+
136
+ # @return [Array<String>]
137
+ def completable_method_names
138
+ callable_method_names.select do |method_name|
139
+ method_name.start_with?(completion_head)
140
+ end.sort
141
+ end
142
+
143
+ # @return [Array<String>]
144
+ def callable_method_names
145
+ callable_method_definitions.map(&:method_name).uniq
146
+ end
147
+
148
+ # @return [Array<String>]
149
+ def callable_method_definitions
150
+ receiver_types.flat_map do |type|
151
+ definition_store.method_definitions_of(type)
152
+ end
153
+ end
154
+
155
+ # @return [Array<String>]
156
+ def receiver_types
157
+ NodeInspector.new(
158
+ definition_store: definition_store,
159
+ node: node
160
+ ).method_receiver_types
161
+ end
162
+
163
+ # @return [Rucoa::Node, nil]
164
+ def node
165
+ @node ||=
166
+ if source.syntax_error?
167
+ repair
168
+ repaired_node
169
+ else
170
+ normal_node
171
+ end
172
+ end
173
+
174
+ # @return [Rucoa::Node, nil]
175
+ def normal_node
176
+ source.node_at(position)
177
+ end
178
+
179
+ # @return [Rucoa::Node, nil]
180
+ def repaired_node
181
+ repaired_source.node_at(position)
182
+ end
183
+
184
+ # @return [void]
185
+ def repair
186
+ @repaired = true
187
+ end
188
+
189
+ # @return [String]
190
+ def repaired_content
191
+ source.content.dup.insert(
192
+ position.to_index_of(source.content),
193
+ EXAMPLE_IDENTIFIER
194
+ )
195
+ end
196
+
197
+ # @return [Rucoa::Source]
198
+ def repaired_source
199
+ Source.new(
200
+ content: repaired_content,
201
+ uri: source.uri
202
+ )
203
+ end
204
+
205
+ # @return [Rucoa::Range]
206
+ def range
207
+ @range ||=
208
+ if @repaired
209
+ position.to_range
210
+ else
211
+ Range.from_parser_range(node.location.expression)
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -2,30 +2,11 @@
2
2
 
3
3
  module Rucoa
4
4
  module Handlers
5
- class TextDocumentDidChangeHandler < Base
6
- include HandlerConcerns::DiagnosticsPublishable
7
-
8
- def call
9
- update_source
10
- publish_diagnostics_on(uri)
11
- end
12
-
13
- private
14
-
15
- # @return [void]
16
- def update_source
17
- source_store.set(uri, text)
18
- end
19
-
5
+ class TextDocumentDidChangeHandler < TextDocumentDidOpenHandler
20
6
  # @return [String]
21
7
  def text
22
8
  request.dig('params', 'contentChanges')[0]['text']
23
9
  end
24
-
25
- # @return [String]
26
- def uri
27
- @uri ||= request.dig('params', 'textDocument', 'uri')
28
- end
29
10
  end
30
11
  end
31
12
  end
@@ -6,15 +6,22 @@ module Rucoa
6
6
  include HandlerConcerns::DiagnosticsPublishable
7
7
 
8
8
  def call
9
- update_source
9
+ source_store.update(source)
10
+ definition_store.update_definitions_defined_in(
11
+ source.path,
12
+ definitions: source.definitions
13
+ )
10
14
  publish_diagnostics_on(uri)
11
15
  end
12
16
 
13
17
  private
14
18
 
15
- # @return [void]
16
- def update_source
17
- source_store.set(uri, text)
19
+ # @return [Rucoa::Source]
20
+ def source
21
+ @source ||= Source.new(
22
+ content: text,
23
+ uri: uri
24
+ )
18
25
  end
19
26
 
20
27
  # @return [String]
@@ -40,7 +40,7 @@ module Rucoa
40
40
  ]
41
41
 
42
42
  def call
43
- return unless respondable?
43
+ return unless responsible?
44
44
 
45
45
  respond(document_symbols)
46
46
  end
@@ -206,13 +206,14 @@ module Rucoa
206
206
  end
207
207
 
208
208
  # @return [Boolean]
209
- def respondable?
210
- configuration.enables_document_symbol? && source
209
+ def responsible?
210
+ configuration.enables_document_symbol? &&
211
+ !source.nil?
211
212
  end
212
213
 
213
214
  # @return [Rucoa::Source]
214
215
  def source
215
- @source ||= @server.source_store.get(uri)
216
+ @source ||= source_store.get(uri)
216
217
  end
217
218
 
218
219
  # @return [String]
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class TextDocumentHoverHandler < Base
6
+ def call
7
+ respond(hover)
8
+ end
9
+
10
+ private
11
+
12
+ # @return [Hash, nil]
13
+ def hover
14
+ return unless responsible?
15
+
16
+ {
17
+ contents: contents,
18
+ range: range.to_vscode_range
19
+ }
20
+ end
21
+
22
+ # @return [Boolean]
23
+ def responsible?
24
+ configuration.enables_hover? &&
25
+ !source.nil? &&
26
+ !node.nil?
27
+ end
28
+
29
+ # @return [String, nil]
30
+ def contents
31
+ method_definition = method_definitions.first
32
+ return unless method_definition
33
+
34
+ [
35
+ method_definition.signatures.join("\n"),
36
+ method_definition.description
37
+ ].join("\n\n")
38
+ end
39
+
40
+ # @return [Rucoa::Range]
41
+ def range
42
+ Range.from_parser_range(node.location.expression)
43
+ end
44
+
45
+ # @return [Rucoa::Nodes::Base, nil]
46
+ def node
47
+ @node ||= source.node_at(position)
48
+ end
49
+
50
+ # @return [Rucoa::Source, nil]
51
+ def source
52
+ @source ||= source_store.get(uri)
53
+ end
54
+
55
+ # @return [String]
56
+ def uri
57
+ request.dig('params', 'textDocument', 'uri')
58
+ end
59
+
60
+ # @return [Rucoa::Position]
61
+ def position
62
+ Position.from_vscode_position(
63
+ request.dig('params', 'position')
64
+ )
65
+ end
66
+
67
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
68
+ def method_definitions
69
+ NodeInspector.new(
70
+ definition_store: definition_store,
71
+ node: node
72
+ ).method_definitions
73
+ end
74
+ end
75
+ end
76
+ end
@@ -53,7 +53,7 @@ module Rucoa
53
53
  # @return [Array(Rucoa::Range, String)]
54
54
  def correctable_replacements
55
55
  replacements.select do |replacement_range, _|
56
- range.contains?(replacement_range)
56
+ range.contain?(replacement_range)
57
57
  end
58
58
  end
59
59
 
@@ -4,7 +4,7 @@ module Rucoa
4
4
  module Handlers
5
5
  class TextDocumentSelectionRangeHandler < Base
6
6
  def call
7
- return unless respondable?
7
+ return unless responsible?
8
8
 
9
9
  respond(
10
10
  positions.filter_map do |position|
@@ -19,9 +19,9 @@ module Rucoa
19
19
  private
20
20
 
21
21
  # @return [Boolean]
22
- def respondable?
22
+ def responsible?
23
23
  configuration.enables_selection_range? &&
24
- source
24
+ !source.nil?
25
25
  end
26
26
 
27
27
  # @return [Rucoa::Source]
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class TextDocumentSignatureHelpHandler < Base
6
+ def call
7
+ respond(signature_help)
8
+ end
9
+
10
+ private
11
+
12
+ # @return [Hash]
13
+ def signature_help
14
+ return unless responsible?
15
+
16
+ {
17
+ signatures: signature_informations
18
+ }
19
+ end
20
+
21
+ # @return [Boolean]
22
+ def responsible?
23
+ configuration.enables_signature_help? &&
24
+ node.is_a?(Nodes::SendNode)
25
+ end
26
+
27
+ # @return [Array<Hash>]
28
+ def signature_informations
29
+ method_definitions.map do |method_definition|
30
+ {
31
+ documentation: method_definition.description,
32
+ label: method_definition.signatures.join("\n")
33
+ }
34
+ end
35
+ end
36
+
37
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
38
+ def method_definitions
39
+ NodeInspector.new(
40
+ definition_store: definition_store,
41
+ node: node
42
+ ).method_definitions
43
+ end
44
+
45
+ # @return [Rucoa::Nodes::Base, nil]
46
+ def node
47
+ @node ||= source.node_at(position)
48
+ end
49
+
50
+ # @return [Rucoa::Position]
51
+ def position
52
+ Position.from_vscode_position(
53
+ request.dig('params', 'position')
54
+ )
55
+ end
56
+
57
+ # @return [Rucoa::Source]
58
+ def source
59
+ source_store.get(uri)
60
+ end
61
+
62
+ # @return [String]
63
+ def uri
64
+ request.dig('params', 'textDocument', 'uri')
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,15 +3,20 @@
3
3
  module Rucoa
4
4
  module Handlers
5
5
  autoload :Base, 'rucoa/handlers/base'
6
+ autoload :ExitHandler, 'rucoa/handlers/exit_handler'
6
7
  autoload :InitializeHandler, 'rucoa/handlers/initialize_handler'
7
8
  autoload :InitializedHandler, 'rucoa/handlers/initialized_handler'
9
+ autoload :ShutdownHandler, 'rucoa/handlers/shutdown_handler'
8
10
  autoload :TextDocumentCodeActionHandler, 'rucoa/handlers/text_document_code_action_handler'
11
+ autoload :TextDocumentCompletionHandler, 'rucoa/handlers/text_document_completion_handler'
9
12
  autoload :TextDocumentDidChangeHandler, 'rucoa/handlers/text_document_did_change_handler'
10
13
  autoload :TextDocumentDidOpenHandler, 'rucoa/handlers/text_document_did_open_handler'
11
14
  autoload :TextDocumentDocumentSymbolHandler, 'rucoa/handlers/text_document_document_symbol_handler'
12
15
  autoload :TextDocumentFormattingHandler, 'rucoa/handlers/text_document_formatting_handler'
16
+ autoload :TextDocumentHoverHandler, 'rucoa/handlers/text_document_hover_handler'
13
17
  autoload :TextDocumentRangeFormattingHandler, 'rucoa/handlers/text_document_range_formatting_handler'
14
18
  autoload :TextDocumentSelectionRangeHandler, 'rucoa/handlers/text_document_selection_range_handler'
19
+ autoload :TextDocumentSignatureHelpHandler, 'rucoa/handlers/text_document_signature_help_handler'
15
20
  autoload :WorkspaceDidChangeConfigurationHandler, 'rucoa/handlers/workspace_did_change_configuration_handler'
16
21
  end
17
22
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Rucoa
6
+ class NodeInspector
7
+ # @param definition_store [Rucoa::DefinitionStore]
8
+ # @param node [Rucoa::Node]
9
+ def initialize(definition_store:, node:)
10
+ @definition_store = definition_store
11
+ @node = node
12
+ end
13
+
14
+ # @return [Array<String>]
15
+ 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)
18
+ end
19
+ end
20
+
21
+ # @return [Array<String>]
22
+ def method_receiver_types
23
+ return [] unless @node.is_a?(Nodes::SendNode)
24
+
25
+ if @node.receiver
26
+ self.class.new(
27
+ definition_store: @definition_store,
28
+ node: @node.receiver
29
+ ).return_types
30
+ else
31
+ [@node.namespace]
32
+ end
33
+ end
34
+
35
+ # @return [Array<String>]
36
+ def return_types
37
+ case @node.type
38
+ when :const
39
+ [@node.name]
40
+ when :lvar
41
+ return_types_for_lvar
42
+ when :send
43
+ return_types_for_send
44
+ when :array
45
+ %w[Array]
46
+ when :class, :module, :nil
47
+ %w[NilClass]
48
+ when :complex
49
+ %w[Complex]
50
+ when :def, :sym
51
+ %w[Symbol]
52
+ when :dstr, :str, :xstr
53
+ %w[String]
54
+ when :erange, :irange
55
+ %w[Range]
56
+ when false
57
+ %w[FalseClass]
58
+ when :float
59
+ %w[Float]
60
+ when :hash, :pair
61
+ %w[Hash]
62
+ when :int
63
+ %w[Integer]
64
+ when :rational
65
+ %w[Rational]
66
+ when :regexp, :regopt
67
+ %w[Regexp]
68
+ when true
69
+ %w[TrueClass]
70
+ else
71
+ []
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ # @return [Array<String>]
78
+ def method_full_qualified_names
79
+ method_receiver_types.map do |type|
80
+ [
81
+ type,
82
+ @node.name
83
+ ].join('#')
84
+ end
85
+ end
86
+
87
+ # @return [String, nil]
88
+ def nearest_def_full_qualified_name
89
+ @node.each_ancestor(:def).first&.full_qualified_name
90
+ end
91
+
92
+ # @return [Array<String>]
93
+ def return_types_for_lvar
94
+ full_qualified_name = nearest_def_full_qualified_name
95
+ return [] unless full_qualified_name
96
+
97
+ @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map do |definition|
98
+ definition.parameters.select do |parameter|
99
+ parameter.name == @node.name
100
+ end.flat_map(&:types)
101
+ end
102
+ end
103
+
104
+ # @return [Array<String>]
105
+ 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
109
+ end
110
+ end
111
+ end
@@ -85,6 +85,36 @@ module Rucoa
85
85
  Range.from_parser_range(location.expression).include?(position)
86
86
  end
87
87
 
88
+ # @note namespace is a String representation of `Module.nesting`.
89
+ # @return [String]
90
+ # @example returns namespace
91
+ # node = Rucoa::Source.new(
92
+ # content: <<~RUBY
93
+ # module Foo
94
+ # class Bar
95
+ # def baz
96
+ # end
97
+ # end
98
+ # end
99
+ # RUBY
100
+ # ).node_at(
101
+ # Rucoa::Position.new(
102
+ # column: 4,
103
+ # line: 3
104
+ # )
105
+ # )
106
+ # expect(node.namespace).to eq('Foo::Bar')
107
+ # @example returns "Object" when the node is not in a namespace
108
+ # node = Rucoa::Parser.call(
109
+ # <<~RUBY
110
+ # foo
111
+ # RUBY
112
+ # )
113
+ # expect(node.namespace).to eq('Object')
114
+ def namespace
115
+ each_ancestor(:module, :class).first&.full_qualified_name || 'Object'
116
+ end
117
+
88
118
  protected
89
119
 
90
120
  # Visit all descendants.