samovar 2.4.1 → 2.5.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/completion.md +206 -0
- data/context/index.yaml +4 -0
- data/lib/samovar/command.rb +24 -9
- data/lib/samovar/completion/context.rb +115 -0
- data/lib/samovar/completion/provider.rb +75 -0
- data/lib/samovar/completion/result.rb +54 -0
- data/lib/samovar/completion/suggestion.rb +94 -0
- data/lib/samovar/completion.rb +24 -0
- data/lib/samovar/failure.rb +1 -1
- data/lib/samovar/flags.rb +32 -0
- data/lib/samovar/many.rb +33 -2
- data/lib/samovar/nested.rb +46 -3
- data/lib/samovar/one.rb +28 -2
- data/lib/samovar/option.rb +63 -5
- data/lib/samovar/options.rb +90 -6
- data/lib/samovar/output/columns.rb +1 -1
- data/lib/samovar/output/header.rb +1 -1
- data/lib/samovar/output/usage_formatter.rb +41 -35
- data/lib/samovar/split.rb +45 -1
- data/lib/samovar/version.rb +1 -1
- data/license.md +2 -1
- data/readme.md +26 -8
- data/releases.md +7 -1
- data.tar.gz.sig +0 -0
- metadata +10 -17
- metadata.gz.sig +0 -0
data/lib/samovar/flags.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Samovar
|
|
|
8
8
|
#
|
|
9
9
|
# Flags parse text like `-f/--flag <value>` into individual flag parsers.
|
|
10
10
|
class Flags
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
11
13
|
# Initialize a new flags parser.
|
|
12
14
|
#
|
|
13
15
|
# @parameter text [String] The flags specification string (e.g., `-f/--flag <value>`).
|
|
@@ -24,6 +26,21 @@ module Samovar
|
|
|
24
26
|
@ordered.each(&block)
|
|
25
27
|
end
|
|
26
28
|
|
|
29
|
+
# Find the flag that matches the given token.
|
|
30
|
+
#
|
|
31
|
+
# @parameter token [String] The token to match.
|
|
32
|
+
# @returns [Flag | Nil] The matching flag.
|
|
33
|
+
def flag_for(token)
|
|
34
|
+
@ordered.find{|flag| flag.prefix?(token)}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The possible flag prefixes for completion.
|
|
38
|
+
#
|
|
39
|
+
# @returns [Array(String)] The flag prefixes and alternatives.
|
|
40
|
+
def completions
|
|
41
|
+
@ordered.flat_map(&:completions)
|
|
42
|
+
end
|
|
43
|
+
|
|
27
44
|
# Get the first flag.
|
|
28
45
|
#
|
|
29
46
|
# @returns [Flag] The first flag.
|
|
@@ -132,6 +149,21 @@ module Samovar
|
|
|
132
149
|
def boolean?
|
|
133
150
|
false
|
|
134
151
|
end
|
|
152
|
+
|
|
153
|
+
# Check if the token matches this flag.
|
|
154
|
+
#
|
|
155
|
+
# @parameter token [String] The token to check.
|
|
156
|
+
# @returns [Boolean] True if the token matches.
|
|
157
|
+
def prefix?(token)
|
|
158
|
+
@prefix == token or @alternatives&.include?(token)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# The possible flag prefixes for completion.
|
|
162
|
+
#
|
|
163
|
+
# @returns [Array(String)] The flag prefix and alternatives.
|
|
164
|
+
def completions
|
|
165
|
+
[@prefix, *@alternatives]
|
|
166
|
+
end
|
|
135
167
|
end
|
|
136
168
|
|
|
137
169
|
# Represents a flag that accepts a value or acts as a boolean.
|
data/lib/samovar/many.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2016-
|
|
4
|
+
# Copyright, 2016-2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "completion"
|
|
5
7
|
|
|
6
8
|
module Samovar
|
|
7
9
|
# Represents multiple positional arguments in a command.
|
|
@@ -15,12 +17,14 @@ module Samovar
|
|
|
15
17
|
# @parameter stop [Regexp] A pattern that indicates the end of this argument list.
|
|
16
18
|
# @parameter default [Object] The default value if no arguments are provided.
|
|
17
19
|
# @parameter required [Boolean] Whether at least one argument is required.
|
|
18
|
-
|
|
20
|
+
# @parameter completions [Array | Proc | Nil] Completions for these arguments.
|
|
21
|
+
def initialize(key, description = nil, stop: /^-/, default: nil, required: false, completions: nil)
|
|
19
22
|
@key = key
|
|
20
23
|
@description = description
|
|
21
24
|
@stop = stop
|
|
22
25
|
@default = default
|
|
23
26
|
@required = required
|
|
27
|
+
@completions = completions
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# The name of the attribute to store the values in.
|
|
@@ -48,6 +52,11 @@ module Samovar
|
|
|
48
52
|
# @attribute [Boolean]
|
|
49
53
|
attr :required
|
|
50
54
|
|
|
55
|
+
# Completions for these arguments.
|
|
56
|
+
#
|
|
57
|
+
# @attribute [Array | Proc | Nil]
|
|
58
|
+
attr :completions
|
|
59
|
+
|
|
51
60
|
# Generate a string representation for usage output.
|
|
52
61
|
#
|
|
53
62
|
# @returns [String] The usage string.
|
|
@@ -87,5 +96,27 @@ module Samovar
|
|
|
87
96
|
raise MissingValueError.new(parent, @key)
|
|
88
97
|
end
|
|
89
98
|
end
|
|
99
|
+
|
|
100
|
+
# Complete this repeating positional argument.
|
|
101
|
+
#
|
|
102
|
+
# @parameter input [Array(String)] Previously completed command-line arguments.
|
|
103
|
+
# @parameter context [Completion::Context] The completion context.
|
|
104
|
+
# @parameter collected [Array(Completion::Suggestion)] Suggestions collected so far.
|
|
105
|
+
# @returns [Completion::Result | Nil] A final completion result, or nil to continue.
|
|
106
|
+
def complete(input, context, collected)
|
|
107
|
+
if @stop
|
|
108
|
+
input.shift while input.any? && !(@stop === input.first)
|
|
109
|
+
|
|
110
|
+
return nil if @stop === context.current
|
|
111
|
+
else
|
|
112
|
+
input.clear
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if input.empty?
|
|
116
|
+
return Completion::Result.new(collected) + Completion::Provider.new(context.with_row(self), @completions).suggestions
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
return nil
|
|
120
|
+
end
|
|
90
121
|
end
|
|
91
122
|
end
|
data/lib/samovar/nested.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2016-
|
|
4
|
+
# Copyright, 2016-2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "completion"
|
|
5
7
|
|
|
6
8
|
module Samovar
|
|
7
9
|
# Represents nested sub-commands in a command.
|
|
@@ -85,16 +87,57 @@ module Samovar
|
|
|
85
87
|
name = input.shift
|
|
86
88
|
|
|
87
89
|
# puts "Instantiating #{command} with #{input}"
|
|
88
|
-
command.new(input, name: name, parent: parent)
|
|
90
|
+
command.new(input, name: name, parent: parent, output: parent&.output)
|
|
89
91
|
elsif default
|
|
90
92
|
return default
|
|
91
93
|
elsif @default
|
|
92
|
-
@commands[@default].new(input, name: @default, parent: parent)
|
|
94
|
+
@commands[@default].new(input, name: @default, parent: parent, output: parent&.output)
|
|
93
95
|
elsif @required
|
|
94
96
|
raise MissingValueError.new(parent, @key)
|
|
95
97
|
end
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
# Complete nested command names or continue into a selected command.
|
|
101
|
+
#
|
|
102
|
+
# @parameter input [Array(String)] Previously completed command-line arguments.
|
|
103
|
+
# @parameter context [Completion::Context] The completion context.
|
|
104
|
+
# @parameter collected [Array(Completion::Suggestion)] Suggestions collected so far.
|
|
105
|
+
# @returns [Completion::Result | Nil] A final completion result, or nil to continue.
|
|
106
|
+
def complete(input, context, collected)
|
|
107
|
+
if input.empty?
|
|
108
|
+
result = suggestions(context)
|
|
109
|
+
|
|
110
|
+
if result.empty? && @default
|
|
111
|
+
return Completion::Result.new(collected) + context.complete_command(@commands.fetch(@default))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
return Completion::Result.new(collected) + result
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if command = @commands[input.first]
|
|
118
|
+
input.shift
|
|
119
|
+
return context.complete_command(command, input)
|
|
120
|
+
elsif @default
|
|
121
|
+
return context.complete_command(@commands.fetch(@default), input)
|
|
122
|
+
else
|
|
123
|
+
return Completion::Result.new(collected)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Complete nested command names for the current token.
|
|
128
|
+
#
|
|
129
|
+
# @parameter context [Completion::Context] The completion context.
|
|
130
|
+
# @returns [Completion::Result] The matching nested command suggestions.
|
|
131
|
+
def suggestions(context)
|
|
132
|
+
suggestions = @commands.collect do |name, command_class|
|
|
133
|
+
next unless name.start_with?(context.current)
|
|
134
|
+
|
|
135
|
+
Completion::Suggestion.new(name, description: command_class.description, type: :command)
|
|
136
|
+
end.compact
|
|
137
|
+
|
|
138
|
+
Completion::Result.new(suggestions)
|
|
139
|
+
end
|
|
140
|
+
|
|
98
141
|
# Generate usage information for this nested command.
|
|
99
142
|
#
|
|
100
143
|
# @parameter rows [Output::Rows] The rows to append usage information to.
|
data/lib/samovar/one.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2016-
|
|
4
|
+
# Copyright, 2016-2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "completion"
|
|
5
7
|
|
|
6
8
|
module Samovar
|
|
7
9
|
# Represents a single positional argument in a command.
|
|
@@ -15,12 +17,14 @@ module Samovar
|
|
|
15
17
|
# @parameter pattern [Regexp] A pattern to match valid values.
|
|
16
18
|
# @parameter default [Object] The default value if no argument is provided.
|
|
17
19
|
# @parameter required [Boolean] Whether the argument is required.
|
|
18
|
-
|
|
20
|
+
# @parameter completions [Array | Proc | Nil] Completions for this argument.
|
|
21
|
+
def initialize(key, description, pattern: //, default: nil, required: false, completions: nil)
|
|
19
22
|
@key = key
|
|
20
23
|
@description = description
|
|
21
24
|
@pattern = pattern
|
|
22
25
|
@default = default
|
|
23
26
|
@required = required
|
|
27
|
+
@completions = completions
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# The name of the attribute to store the value in.
|
|
@@ -48,6 +52,11 @@ module Samovar
|
|
|
48
52
|
# @attribute [Boolean]
|
|
49
53
|
attr :required
|
|
50
54
|
|
|
55
|
+
# Completions for this argument.
|
|
56
|
+
#
|
|
57
|
+
# @attribute [Array | Proc | Nil]
|
|
58
|
+
attr :completions
|
|
59
|
+
|
|
51
60
|
# Generate a string representation for usage output.
|
|
52
61
|
#
|
|
53
62
|
# @returns [String] The usage string.
|
|
@@ -85,5 +94,22 @@ module Samovar
|
|
|
85
94
|
raise MissingValueError.new(parent, @key)
|
|
86
95
|
end
|
|
87
96
|
end
|
|
97
|
+
|
|
98
|
+
# Complete this positional argument.
|
|
99
|
+
#
|
|
100
|
+
# @parameter input [Array(String)] Previously completed command-line arguments.
|
|
101
|
+
# @parameter context [Completion::Context] The completion context.
|
|
102
|
+
# @parameter collected [Array(Completion::Suggestion)] Suggestions collected so far.
|
|
103
|
+
# @returns [Completion::Result | Nil] A final completion result, or nil to continue.
|
|
104
|
+
def complete(input, context, collected)
|
|
105
|
+
if input.empty?
|
|
106
|
+
return Completion::Result.new(collected) + Completion::Provider.new(context.with_row(self), @completions).suggestions
|
|
107
|
+
elsif @pattern =~ input.first
|
|
108
|
+
input.shift
|
|
109
|
+
return nil
|
|
110
|
+
else
|
|
111
|
+
return Completion::Result.new(collected)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
88
114
|
end
|
|
89
115
|
end
|
data/lib/samovar/option.rb
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
require_relative "flags"
|
|
7
7
|
require_relative "error"
|
|
8
|
+
require_relative "completion/provider"
|
|
9
|
+
require_relative "completion/result"
|
|
8
10
|
|
|
9
11
|
module Samovar
|
|
10
12
|
# Represents a single command-line option.
|
|
@@ -20,8 +22,9 @@ module Samovar
|
|
|
20
22
|
# @parameter value [Object | Nil] A fixed value to use regardless of user input.
|
|
21
23
|
# @parameter type [Class | Proc | Nil] The type to coerce the value to.
|
|
22
24
|
# @parameter required [Boolean] Whether the option is required.
|
|
25
|
+
# @parameter completions [Array | Proc | Nil] Completions for option values.
|
|
23
26
|
# @yields {|value| ...} An optional block to transform the parsed value.
|
|
24
|
-
def initialize(flags, description, key: nil, default: nil, value: nil, type: nil, required: false, &block)
|
|
27
|
+
def initialize(flags, description, key: nil, default: nil, value: nil, type: nil, required: false, completions: nil, &block)
|
|
25
28
|
@flags = Flags.new(flags)
|
|
26
29
|
@description = description
|
|
27
30
|
|
|
@@ -39,6 +42,7 @@ module Samovar
|
|
|
39
42
|
|
|
40
43
|
@type = type
|
|
41
44
|
@required = required
|
|
45
|
+
@completions = completions
|
|
42
46
|
@block = block
|
|
43
47
|
end
|
|
44
48
|
|
|
@@ -59,8 +63,21 @@ module Samovar
|
|
|
59
63
|
|
|
60
64
|
# The default value if the option is not provided.
|
|
61
65
|
#
|
|
62
|
-
# @
|
|
63
|
-
|
|
66
|
+
# @returns [Object | Nil] The resolved default value.
|
|
67
|
+
def default
|
|
68
|
+
if @default.respond_to?(:call)
|
|
69
|
+
@default.call
|
|
70
|
+
else
|
|
71
|
+
@default
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Whether this option has a default value.
|
|
76
|
+
#
|
|
77
|
+
# @returns [Boolean] True if the option has a default value.
|
|
78
|
+
def default?
|
|
79
|
+
!@default.nil?
|
|
80
|
+
end
|
|
64
81
|
|
|
65
82
|
# A fixed value to use regardless of user input.
|
|
66
83
|
#
|
|
@@ -77,11 +94,52 @@ module Samovar
|
|
|
77
94
|
# @attribute [Boolean]
|
|
78
95
|
attr :required
|
|
79
96
|
|
|
97
|
+
# Completions for option values.
|
|
98
|
+
#
|
|
99
|
+
# @attribute [Array | Proc | Nil]
|
|
100
|
+
attr :completions
|
|
101
|
+
|
|
80
102
|
# An optional block to transform the parsed value.
|
|
81
103
|
#
|
|
82
104
|
# @attribute [Proc | Nil]
|
|
83
105
|
attr :block
|
|
84
106
|
|
|
107
|
+
# Find the flag that matches the given token.
|
|
108
|
+
#
|
|
109
|
+
# @parameter token [String] The token to match.
|
|
110
|
+
# @returns [Flag | Nil] The matching flag.
|
|
111
|
+
def flag_for(token)
|
|
112
|
+
@flags.flag_for(token)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Whether this option consumes a value after the flag.
|
|
116
|
+
#
|
|
117
|
+
# @returns [Boolean] True if any flag for this option consumes a value.
|
|
118
|
+
def value?
|
|
119
|
+
@flags.any?{|flag| !flag.boolean?}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Complete values for this option.
|
|
123
|
+
#
|
|
124
|
+
# @parameter context [Completion::Context] The completion context.
|
|
125
|
+
# @returns [Completion::Result] The matching option value completions.
|
|
126
|
+
def suggestions(context)
|
|
127
|
+
suggestions = []
|
|
128
|
+
context = context.with_row(self)
|
|
129
|
+
|
|
130
|
+
if default?
|
|
131
|
+
suggestion = Completion::Provider.new(context, [default]).suggestions.first
|
|
132
|
+
|
|
133
|
+
suggestions << suggestion if suggestion
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
Completion::Provider.new(context, @completions).suggestions.each do |suggestion|
|
|
137
|
+
suggestions << suggestion unless suggestions.any?{|existing| existing.value == suggestion.value}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
Completion::Result.new(suggestions)
|
|
141
|
+
end
|
|
142
|
+
|
|
85
143
|
# Coerce the result to the specified type.
|
|
86
144
|
#
|
|
87
145
|
# @parameter result [Object] The value to coerce.
|
|
@@ -141,8 +199,8 @@ module Samovar
|
|
|
141
199
|
#
|
|
142
200
|
# @returns [Array] The usage array.
|
|
143
201
|
def to_a
|
|
144
|
-
if
|
|
145
|
-
[@flags, @description, "(default: #{
|
|
202
|
+
if default?
|
|
203
|
+
[@flags, @description, "(default: #{default})"]
|
|
146
204
|
elsif @required
|
|
147
205
|
[@flags, @description, "(required)"]
|
|
148
206
|
else
|
data/lib/samovar/options.rb
CHANGED
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
# Copyright, 2016-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require_relative "option"
|
|
7
|
+
require_relative "completion"
|
|
7
8
|
|
|
8
9
|
module Samovar
|
|
9
10
|
# Represents a collection of command-line options.
|
|
10
11
|
#
|
|
11
12
|
# Options provide a DSL for defining multiple option flags in a single block.
|
|
12
13
|
class Options
|
|
14
|
+
include Enumerable
|
|
15
|
+
|
|
13
16
|
# Parse and create an options collection from a block.
|
|
14
17
|
#
|
|
15
18
|
# @parameter arguments [Array] The arguments for the options collection.
|
|
@@ -68,8 +71,10 @@ module Samovar
|
|
|
68
71
|
|
|
69
72
|
# The default values for options.
|
|
70
73
|
#
|
|
71
|
-
# @
|
|
72
|
-
|
|
74
|
+
# @returns [Hash] The resolved default values.
|
|
75
|
+
def defaults
|
|
76
|
+
@defaults.transform_values(&:default)
|
|
77
|
+
end
|
|
73
78
|
|
|
74
79
|
# Freeze this options collection.
|
|
75
80
|
#
|
|
@@ -93,6 +98,21 @@ module Samovar
|
|
|
93
98
|
@ordered.each(&block)
|
|
94
99
|
end
|
|
95
100
|
|
|
101
|
+
# Find the option that matches the given flag token.
|
|
102
|
+
#
|
|
103
|
+
# @parameter token [String] The flag token to match.
|
|
104
|
+
# @returns [Option | Nil] The matching option.
|
|
105
|
+
def option_for(token)
|
|
106
|
+
@keyed[token]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The possible flag prefixes for completion.
|
|
110
|
+
#
|
|
111
|
+
# @returns [Array(String)] The option flag prefixes and alternatives.
|
|
112
|
+
def completions
|
|
113
|
+
@ordered.flat_map{|option| option.flags.completions}
|
|
114
|
+
end
|
|
115
|
+
|
|
96
116
|
# Check if this options collection is empty.
|
|
97
117
|
#
|
|
98
118
|
# @returns [Boolean] True if there are no options.
|
|
@@ -131,8 +151,8 @@ module Samovar
|
|
|
131
151
|
end
|
|
132
152
|
end
|
|
133
153
|
|
|
134
|
-
if
|
|
135
|
-
@defaults[option.key] = option
|
|
154
|
+
if option.default?
|
|
155
|
+
@defaults[option.key] = option
|
|
136
156
|
end
|
|
137
157
|
end
|
|
138
158
|
|
|
@@ -143,7 +163,7 @@ module Samovar
|
|
|
143
163
|
# @parameter default [Hash | Nil] Default values to use.
|
|
144
164
|
# @returns [Hash] The parsed option values.
|
|
145
165
|
def parse(input, parent = nil, default = nil)
|
|
146
|
-
values = (default ||
|
|
166
|
+
values = (default || defaults).dup
|
|
147
167
|
|
|
148
168
|
while option = @keyed[input.first]
|
|
149
169
|
# prefix = input.first
|
|
@@ -161,7 +181,71 @@ module Samovar
|
|
|
161
181
|
end
|
|
162
182
|
|
|
163
183
|
return values
|
|
164
|
-
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Complete option flags or option values.
|
|
187
|
+
#
|
|
188
|
+
# @parameter input [Array(String)] Previously completed command-line arguments.
|
|
189
|
+
# @parameter context [Completion::Context] The completion context.
|
|
190
|
+
# @parameter collected [Array(Completion::Suggestion)] Suggestions collected so far.
|
|
191
|
+
# @returns [Completion::Result | Nil] A final completion result, or nil to continue.
|
|
192
|
+
def complete(input, context, collected)
|
|
193
|
+
result = consume(input, context)
|
|
194
|
+
return result if result
|
|
195
|
+
|
|
196
|
+
return nil unless input.empty?
|
|
197
|
+
|
|
198
|
+
flags = suggestions(context.current)
|
|
199
|
+
|
|
200
|
+
if context.current.start_with?("-") && flags.any?
|
|
201
|
+
return Completion::Result.new(flags)
|
|
202
|
+
elsif context.current.empty?
|
|
203
|
+
collected.concat(flags)
|
|
204
|
+
return nil
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
return nil
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Consume option tokens before the current completion position.
|
|
211
|
+
#
|
|
212
|
+
# @parameter input [Array(String)] Previously completed command-line arguments.
|
|
213
|
+
# @parameter context [Completion::Context] The completion context.
|
|
214
|
+
# @returns [Completion::Result | Nil] A completion result for an option value, or nil to continue.
|
|
215
|
+
def consume(input, context)
|
|
216
|
+
while token = input.first
|
|
217
|
+
break unless option = option_for(token)
|
|
218
|
+
|
|
219
|
+
flag = option.flag_for(token)
|
|
220
|
+
input.shift
|
|
221
|
+
|
|
222
|
+
if flag && !flag.boolean?
|
|
223
|
+
if input.any?
|
|
224
|
+
input.shift
|
|
225
|
+
else
|
|
226
|
+
return option.suggestions(context)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
return nil
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Complete option flags for the given prefix.
|
|
235
|
+
#
|
|
236
|
+
# @parameter prefix [String] The option prefix being completed.
|
|
237
|
+
# @returns [Array(Completion::Suggestion)] The matching option flag suggestions.
|
|
238
|
+
def suggestions(prefix)
|
|
239
|
+
flat_map do |option|
|
|
240
|
+
option.flags.completions.collect do |value|
|
|
241
|
+
next unless value.start_with?(prefix)
|
|
242
|
+
|
|
243
|
+
Completion::Suggestion.new(value, description: option.description, type: :option)
|
|
244
|
+
end
|
|
245
|
+
end.compact
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Generate a string representation for usage output.
|
|
165
249
|
#
|
|
166
250
|
# @returns [String] The usage string.
|
|
167
251
|
def to_s
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2019-2025, by Samuel Williams.
|
|
5
|
+
# Copyright, 2026, by Gerhard Schlager.
|
|
5
6
|
|
|
6
|
-
require "mapping/model"
|
|
7
7
|
require "console/terminal"
|
|
8
8
|
|
|
9
9
|
require_relative "../error"
|
|
@@ -17,8 +17,8 @@ module Samovar
|
|
|
17
17
|
module Output
|
|
18
18
|
# Formats and prints usage information to a terminal.
|
|
19
19
|
#
|
|
20
|
-
#
|
|
21
|
-
class UsageFormatter
|
|
20
|
+
# Dispatches on the type of each output object to apply custom formatting rules.
|
|
21
|
+
class UsageFormatter
|
|
22
22
|
# Print usage information to the output.
|
|
23
23
|
#
|
|
24
24
|
# @parameter rows [Rows] The rows to format and print.
|
|
@@ -39,7 +39,6 @@ module Samovar
|
|
|
39
39
|
def initialize(output)
|
|
40
40
|
@output = output
|
|
41
41
|
@width = 80
|
|
42
|
-
@first = true
|
|
43
42
|
|
|
44
43
|
@terminal = Console::Terminal.for(@output)
|
|
45
44
|
@terminal[:header] = @terminal.style(nil, nil, :bright)
|
|
@@ -47,43 +46,50 @@ module Samovar
|
|
|
47
46
|
@terminal[:error] = @terminal.style(:red)
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
# Format and print the given object according to its type.
|
|
50
|
+
#
|
|
51
|
+
# @parameter object [Object] The object to format (a {Rows}, {Row}, {Header}, or error).
|
|
52
|
+
# @parameter arguments [Array] Extra context passed through to nested rows (the containing {Rows}).
|
|
53
|
+
def map(object, *arguments, first: true)
|
|
54
|
+
case object
|
|
55
|
+
when InvalidInputError
|
|
56
|
+
# This is a little hack which avoids printing out "--help" if it was part of an incomplete parse. In the future I'd prefer if this was handled explicitly.
|
|
57
|
+
@terminal.puts("#{object.message} in:", style: :error) unless object.help?
|
|
58
|
+
when MissingValueError
|
|
59
|
+
@terminal.puts("#{object.message} in:", style: :error)
|
|
60
|
+
when Header
|
|
61
|
+
header, rows = object, arguments.first
|
|
62
|
+
|
|
63
|
+
if first
|
|
64
|
+
first = false
|
|
65
|
+
else
|
|
66
|
+
@terminal.puts
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
command_line = header.object.command_line(header.name)
|
|
70
|
+
@terminal.puts "#{rows.indentation}#{command_line}", style: :header
|
|
71
|
+
|
|
72
|
+
if description = header.object.description
|
|
73
|
+
@terminal.puts "#{rows.indentation}\t#{description}", style: :description
|
|
74
|
+
@terminal.puts
|
|
75
|
+
end
|
|
76
|
+
when Row
|
|
77
|
+
row, rows = object, arguments.first
|
|
78
|
+
@terminal.puts "#{rows.indentation}#{row.align(rows.columns)}"
|
|
79
|
+
when Rows
|
|
80
|
+
object.each do |row, rows|
|
|
81
|
+
first = map(row, rows, first: first)
|
|
82
|
+
end
|
|
62
83
|
else
|
|
63
|
-
|
|
84
|
+
raise ArgumentError, "Unable to format #{object.class}!"
|
|
64
85
|
end
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
@terminal.puts "#{rows.indentation}#{command_line}", style: :header
|
|
68
|
-
|
|
69
|
-
if description = header.object.description
|
|
70
|
-
@terminal.puts "#{rows.indentation}\t#{description}", style: :description
|
|
71
|
-
@terminal.puts
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
map(Row) do |row, rows|
|
|
76
|
-
@terminal.puts "#{rows.indentation}#{row.align(rows.columns)}"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
map(Rows) do |items|
|
|
80
|
-
items.collect{|row, rows| map(row, rows)}
|
|
87
|
+
return first
|
|
81
88
|
end
|
|
82
89
|
|
|
83
90
|
# Print the formatted usage output.
|
|
84
|
-
def print(rows, first:
|
|
85
|
-
|
|
86
|
-
map(rows)
|
|
91
|
+
def print(rows, first: true)
|
|
92
|
+
map(rows, first: first)
|
|
87
93
|
end
|
|
88
94
|
end
|
|
89
95
|
end
|