Skip to content

Commit fc64455

Browse files
committed
- Add support for negatable flags (--[no-]color)
1 parent 0571f53 commit fc64455

28 files changed

Lines changed: 276 additions & 6 deletions

lib/bashly/completions/bashly-completions.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ _bashly_completions() {
301301
return
302302
;;
303303
6:0)
304-
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "arg arg.allowed arg.completions arg.default arg.help arg.name arg.repeatable arg.required arg.validate command command.alias command.args command.catch_all command.commands command.completions command.default command.dependencies command.environment_variables command.examples command.expose command.extensible command.filename command.filters command.flags command.footer command.function command.group command.help command.help_header_override command.name command.private command.variables command.version environment_variable environment_variable.default environment_variable.help environment_variable.name environment_variable.private environment_variable.required environment_variable.validate flag flag.alias flag.allowed flag.arg flag.completions flag.conflicts flag.default flag.help flag.long flag.needs flag.private flag.repeatable flag.required flag.short flag.unique flag.validate variable variable.name variable.value" -- "$cur")
304+
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "arg arg.allowed arg.completions arg.default arg.help arg.name arg.repeatable arg.required arg.validate command command.alias command.args command.catch_all command.commands command.completions command.default command.dependencies command.environment_variables command.examples command.expose command.extensible command.filename command.filters command.flags command.footer command.function command.group command.help command.help_header_override command.name command.private command.variables command.version environment_variable environment_variable.default environment_variable.help environment_variable.name environment_variable.private environment_variable.required environment_variable.validate flag flag.alias flag.allowed flag.arg flag.completions flag.conflicts flag.default flag.help flag.long flag.needs flag.negatable flag.private flag.repeatable flag.required flag.short flag.unique flag.validate variable variable.name variable.value" -- "$cur")
305305
return
306306
;;
307307
8:0)

lib/bashly/completions/completely.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ tokens:
127127
- flag.help
128128
- flag.long
129129
- flag.needs
130+
- flag.negatable
130131
- flag.private
131132
- flag.repeatable
132133
- flag.required

lib/bashly/config_validator.rb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def assert_flag(key, value)
130130
assert_string_or_array "#{key}.default", value['default']
131131
assert_string_or_array "#{key}.validate", value['validate']
132132

133+
assert_boolean "#{key}.negatable", value['negatable']
133134
assert_boolean "#{key}.private", value['private']
134135
assert_boolean "#{key}.repeatable", value['repeatable']
135136
assert_boolean "#{key}.unique", value['unique']
@@ -148,6 +149,13 @@ def assert_flag(key, value)
148149

149150
refute value['required'] && value['default'], "#{key} cannot have both nub`required` and nub`default`"
150151

152+
if value['negatable']
153+
assert value['long'], "#{key}.negatable requires nub`long`"
154+
refute value['arg'], "#{key}.negatable does not make sense with nub`arg`"
155+
refute value['repeatable'], "#{key}.negatable does not make sense with nub`repeatable`"
156+
refute value['required'], "#{key}.negatable does not make sense with nub`required`"
157+
end
158+
151159
if value['default']
152160
assert value['arg'], "#{key}.default does not make sense without nub`arg`"
153161
end
@@ -234,7 +242,7 @@ def assert_command(key, value)
234242
assert_array "#{key}.variables", value['variables'], of: :var
235243

236244
assert_uniq "#{key}.commands", value['commands'], %w[name alias]
237-
assert_uniq "#{key}.flags", value['flags'], %w[long short alias]
245+
assert_uniq_flags key, value['flags']
238246
assert_uniq "#{key}.args", value['args'], 'name'
239247

240248
if value['function']
@@ -276,5 +284,23 @@ def assert_command(key, value)
276284
refute value['extensible'], "#{key}.extensible makes no sense"
277285
end
278286
end
287+
288+
def assert_uniq_flags(key, flags)
289+
return unless flags
290+
291+
list = flags.flat_map do |flag|
292+
[flag['long'], flag['short'], *Array(flag['alias']), negated_flag_name(flag)].compact
293+
end
294+
295+
nonuniqs = list.nonuniq
296+
assert nonuniqs.empty?,
297+
"#{key}.flags contains non-unique elements (#{nonuniqs.join ', '}) in long or short or alias"
298+
end
299+
300+
def negated_flag_name(flag)
301+
return unless flag['negatable'] == true && flag['long']
302+
303+
"--no-#{flag['long'].delete_prefix '--'}"
304+
end
279305
end
280306
end

lib/bashly/docs/flag.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ flag.needs:
147147
help: Where to add the alias
148148
needs: [--add]
149149
150+
flag.negatable:
151+
help: Allow a boolean long flag to also be disabled with its `--no-` form.
152+
url: https://bashly.dev/configuration/flag/#negatable
153+
example: |-
154+
flags:
155+
- long: --color
156+
help: Enable color output
157+
negatable: true
158+
150159
flag.private:
151160
help: Specify that this flag should not be displayed in the help text.
152161
url: https://bashly.dev/configuration/flag/#private

lib/bashly/script/flag.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class << self
1111
def option_keys
1212
@option_keys ||= %i[
1313
alias allowed arg completions conflicts default help long needs
14-
private repeatable required short unique validate
14+
negatable private repeatable required short unique validate
1515
]
1616
end
1717
end
@@ -23,9 +23,17 @@ def alt
2323
end
2424

2525
def aliases
26+
[long, negated_long, short].compact + alt
27+
end
28+
29+
def positive_aliases
2630
primary_aliases + alt
2731
end
2832

33+
def negated_long
34+
"--no-#{long_without_prefix}" if negatable && long
35+
end
36+
2937
def default_string
3038
if default.is_a?(Array)
3139
Shellwords.shelljoin default
@@ -41,7 +49,7 @@ def name
4149
end
4250

4351
def usage_string(extended: false)
44-
result = [aliases.join(', ')]
52+
result = [usage_aliases.join(', ')]
4553
result << arg.upcase if arg
4654
result << strings[:required] if required && extended
4755
result << strings[:repeatable] if repeatable && extended
@@ -53,6 +61,18 @@ def usage_string(extended: false)
5361
def primary_aliases
5462
[long, short].compact
5563
end
64+
65+
def usage_aliases
66+
[usage_long, short].compact + alt
67+
end
68+
69+
def usage_long
70+
negatable && long ? "--[no-]#{long_without_prefix}" : long
71+
end
72+
73+
def long_without_prefix
74+
long.delete_prefix '--'
75+
end
5676
end
5777
end
5878
end
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
= view_marker
22

3-
> {{ aliases.join " | " }})
3+
> {{ positive_aliases.join " | " }})
44
= render(arg ? :argfile_case_arg : :argfile_case_no_arg).indent 2
55
> ;;
66
>
7+
8+
if negatable
9+
> {{ negated_long }})
10+
> unset "args[{{ name }}]"
11+
> ;;
12+
>
13+
end

lib/bashly/views/flag/case.gtx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
= view_marker
22

3-
> {{ aliases.join " | " }})
3+
> {{ positive_aliases.join " | " }})
44
= render(:conflicts).indent 2
55
= render(arg ? :case_arg : :case_no_arg).indent 2
66
>
7+
8+
if negatable
9+
> {{ negated_long }})
10+
> unset "args[{{ name }}]"
11+
> shift
12+
> ;;
13+
>
14+
end

schemas/bashly.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@
290290
]
291291
}
292292
},
293+
"negatable": {
294+
"title": "negatable",
295+
"description": "Whether the current flag also accepts its --no- form\nhttps://bashly.dev/configuration/flag/#negatable",
296+
"type": "boolean",
297+
"default": false
298+
},
293299
"completions": {
294300
"title": "completions",
295301
"description": "Completions of the current flag\nhttps://bashly.dev/configuration/flag/#completions",

spec/approvals/cli/doc/full

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,17 @@ flag.needs
784784

785785
See https://bashly.dev/configuration/flag/#needs
786786

787+
flag.negatable
788+
789+
Allow a boolean long flag to also be disabled with its --no- form.
790+
791+
flags:
792+
- long: --color
793+
help: Enable color output
794+
negatable: true
795+
796+
See https://bashly.dev/configuration/flag/#negatable
797+
787798
flag.private
788799

789800
Specify that this flag should not be displayed in the help text.

spec/approvals/cli/doc/index

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ flag.default
4848
flag.help
4949
flag.long
5050
flag.needs
51+
flag.negatable
5152
flag.private
5253
flag.repeatable
5354
flag.required

0 commit comments

Comments
 (0)