diff --git a/README.md b/README.md index 73f9563..b122226 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ The following is a table of the available subcommands for the CLI tool (Tinty), | `sync` | Installs and updates schemes and templates defined in `tinty/config.toml` | - | `tinty sync` | | `list` | Lists all available themes. | Optional argument `--custom-schemes` to list saved custom theme files using `tinty generate-scheme`.
Optional argument `--json` to output more info about each scheme in JSON form | `tinty list` | | `apply` | Applies a specific theme. | `-`: Name of the system and scheme to apply. | `tinty apply base16-mocha` | -| `cycle` | Applies the next theme among your preferred ones. See [Configuration](#configuration). | | `tinty cycle` | +| `cycle` | Applies the next theme in a configured ring. See [Configuration](#configuration). | Optional `--ring ` to choose a specific ring. | `tinty cycle --ring dark` | | `init` | Initializes the tool with the last applied theme otherwise `default-scheme` from `config.toml`. | - | `tinty init` | | `current` | Displays the currently applied theme or current theme values. | `` (Optional argument with the following supported values: `author` \| `description` \| `name` \| `slug` \| `system` \| `variant`) | `tinty current` | | `config` | Displays config related information currently in use by Tinty. Without flags it returns `config.yml` content. | - | `tinty config` | @@ -220,10 +220,19 @@ to your preferences and environment. |-------------------|--------------------|----------|----------------------------------------------------------------------------------------|---------|---------| | `shell` | `string` | Optional | Specifies the shell command used to execute hooks. | `"sh -c '{}'"` | `shell = "bash -c '{}'"` | | `default-scheme` | `string` | Optional | Defines the default theme scheme to be applied if no specific scheme is set. | None | `default-scheme = "base16-mocha"` | -| `preferred-schemes` | `array` | Optional | A list of your favorite theme schemes you'd like to cycle through | `[]` | `preferred-schemes = ["base16-gruvbox-dark", "base16-gruvbox-light"]` | +| `default-cycle-ring` | `string` | Optional | The configured ring used by `tinty cycle` when `--ring` is not provided. | None | `default-cycle-ring = "default"` | +| `[[rings]]` | `array` | Optional | Named scheme cycles used by `tinty cycle`. | - | See below | | `hooks` | `array` | Optional | A list of strings which are executed after every `tinty apply` | None | `hooks = ["echo \"The current scheme is: $(tinty current)\""]` | | `[[items]]` | `array` | Required | An array of `items` configurations. Each item represents a themeable component. Detailed structure provided in the next section. | - | - | +```toml +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = ["base16-gruvbox-dark", "base16-gruvbox-light"] +``` + #### hooks **New in Tinty 0.29+**: Theme & color values are now available to hooks as environment variables: @@ -326,7 +335,15 @@ multiple items along with global settings: # Global settings shell = "zsh -c '{}'" default-scheme = "base16-mocha" -preferred-schemes = ["base16-gruvbox-dark", "base16-gruvbox-light", "base16-github-dark"] +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = ["base16-gruvbox-dark", "base16-gruvbox-light", "base16-github-dark"] + +[[rings]] +name = "dark" +schemes = ["base16-gruvbox-dark", "base16-github-dark"] # Item configurations [[items]] diff --git a/contrib/completion/tinty.bash b/contrib/completion/tinty.bash index 6a440ef..ceefced 100644 --- a/contrib/completion/tinty.bash +++ b/contrib/completion/tinty.bash @@ -253,12 +253,19 @@ _tinty() { return 0 ;; tinty__cycle) - opts="-q -c -d -h --quiet --config --data-dir --help" + opts="-q -c -d -h --ring --quiet --config --data-dir --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in + --ring) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; --config) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/contrib/completion/tinty.elvish b/contrib/completion/tinty.elvish index 8bc0821..bbf6ab3 100644 --- a/contrib/completion/tinty.elvish +++ b/contrib/completion/tinty.elvish @@ -30,7 +30,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand current 'Prints the last scheme name applied or specific values from the current scheme' cand generate-completion 'Generates a shell completion script' cand generate-scheme 'Generates a scheme based on an image' - cand info 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)' + cand info 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)' cand init 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' cand list 'Lists available schemes' cand config 'Provides config related information' @@ -38,7 +38,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand install 'Install the environment needed for tinty' cand update 'Update to the latest themes' cand sync 'Install missing templates in tinty/config.toml and update existing templates' - cand cycle 'Cycle through your preferred themes' + cand cycle 'Cycle through a configured ring of schemes' cand help 'Print this message or the help of the given subcommand(s)' } &'tinty;build'= { @@ -87,6 +87,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand --config 'Optional path to the tinty config.toml file' cand -d 'Optional path to the tinty data directory' cand --data-dir 'Optional path to the tinty data directory' + cand --all 'Lists all availabile custom schemes' cand --custom-schemes 'Lists availabile custom schemes' cand -h 'Print help' cand --help 'Print help' @@ -161,6 +162,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand --help 'Print help' } &'tinty;cycle'= { + cand --ring 'Name of the configured ring to cycle through' cand -c 'Optional path to the tinty config.toml file' cand --config 'Optional path to the tinty config.toml file' cand -d 'Optional path to the tinty data directory' @@ -175,7 +177,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand current 'Prints the last scheme name applied or specific values from the current scheme' cand generate-completion 'Generates a shell completion script' cand generate-scheme 'Generates a scheme based on an image' - cand info 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)' + cand info 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)' cand init 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' cand list 'Lists available schemes' cand config 'Provides config related information' @@ -183,7 +185,7 @@ set edit:completion:arg-completer[tinty] = {|@words| cand install 'Install the environment needed for tinty' cand update 'Update to the latest themes' cand sync 'Install missing templates in tinty/config.toml and update existing templates' - cand cycle 'Cycle through your preferred themes' + cand cycle 'Cycle through a configured ring of schemes' cand help 'Print this message or the help of the given subcommand(s)' } &'tinty;help;build'= { diff --git a/contrib/completion/tinty.fish b/contrib/completion/tinty.fish index b9195a9..7040c75 100644 --- a/contrib/completion/tinty.fish +++ b/contrib/completion/tinty.fish @@ -32,7 +32,7 @@ complete -c tinty -n "__fish_tinty_needs_command" -f -a "build" -d 'Builds the t complete -c tinty -n "__fish_tinty_needs_command" -f -a "current" -d 'Prints the last scheme name applied or specific values from the current scheme' complete -c tinty -n "__fish_tinty_needs_command" -f -a "generate-completion" -d 'Generates a shell completion script' complete -c tinty -n "__fish_tinty_needs_command" -f -a "generate-scheme" -d 'Generates a scheme based on an image' -complete -c tinty -n "__fish_tinty_needs_command" -f -a "info" -d 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)' +complete -c tinty -n "__fish_tinty_needs_command" -f -a "info" -d 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)' complete -c tinty -n "__fish_tinty_needs_command" -f -a "init" -d 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' complete -c tinty -n "__fish_tinty_needs_command" -f -a "list" -d 'Lists available schemes' complete -c tinty -n "__fish_tinty_needs_command" -f -a "config" -d 'Provides config related information' @@ -40,7 +40,7 @@ complete -c tinty -n "__fish_tinty_needs_command" -f -a "apply" -d 'Applies a th complete -c tinty -n "__fish_tinty_needs_command" -f -a "install" -d 'Install the environment needed for tinty' complete -c tinty -n "__fish_tinty_needs_command" -f -a "update" -d 'Update to the latest themes' complete -c tinty -n "__fish_tinty_needs_command" -f -a "sync" -d 'Install missing templates in tinty/config.toml and update existing templates' -complete -c tinty -n "__fish_tinty_needs_command" -f -a "cycle" -d 'Cycle through your preferred themes' +complete -c tinty -n "__fish_tinty_needs_command" -f -a "cycle" -d 'Cycle through a configured ring of schemes' complete -c tinty -n "__fish_tinty_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c tinty -n "__fish_tinty_using_subcommand build" -s c -l config -d 'Optional path to the tinty config.toml file' -r complete -c tinty -n "__fish_tinty_using_subcommand build" -s d -l data-dir -d 'Optional path to the tinty data directory' -r @@ -66,6 +66,7 @@ complete -c tinty -n "__fish_tinty_using_subcommand generate-scheme" -l save -d complete -c tinty -n "__fish_tinty_using_subcommand generate-scheme" -s h -l help -d 'Print help' complete -c tinty -n "__fish_tinty_using_subcommand info" -s c -l config -d 'Optional path to the tinty config.toml file' -r complete -c tinty -n "__fish_tinty_using_subcommand info" -s d -l data-dir -d 'Optional path to the tinty data directory' -r +complete -c tinty -n "__fish_tinty_using_subcommand info" -l all -d 'Lists all availabile custom schemes' complete -c tinty -n "__fish_tinty_using_subcommand info" -l custom-schemes -d 'Lists availabile custom schemes' complete -c tinty -n "__fish_tinty_using_subcommand info" -s h -l help -d 'Print help' complete -c tinty -n "__fish_tinty_using_subcommand init" -s c -l config -d 'Optional path to the tinty config.toml file' -r @@ -98,6 +99,7 @@ complete -c tinty -n "__fish_tinty_using_subcommand sync" -s c -l config -d 'Opt complete -c tinty -n "__fish_tinty_using_subcommand sync" -s d -l data-dir -d 'Optional path to the tinty data directory' -r complete -c tinty -n "__fish_tinty_using_subcommand sync" -s q -l quiet -d 'Silence stdout' complete -c tinty -n "__fish_tinty_using_subcommand sync" -s h -l help -d 'Print help' +complete -c tinty -n "__fish_tinty_using_subcommand cycle" -l ring -d 'Name of the configured ring to cycle through' -r complete -c tinty -n "__fish_tinty_using_subcommand cycle" -s c -l config -d 'Optional path to the tinty config.toml file' -r complete -c tinty -n "__fish_tinty_using_subcommand cycle" -s d -l data-dir -d 'Optional path to the tinty data directory' -r complete -c tinty -n "__fish_tinty_using_subcommand cycle" -s q -l quiet -d 'Silence stdout' @@ -106,7 +108,7 @@ complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_su complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "current" -d 'Prints the last scheme name applied or specific values from the current scheme' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "generate-completion" -d 'Generates a shell completion script' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "generate-scheme" -d 'Generates a scheme based on an image' -complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "info" -d 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)' +complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "info" -d 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "init" -d 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "list" -d 'Lists available schemes' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "config" -d 'Provides config related information' @@ -114,5 +116,5 @@ complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_su complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "install" -d 'Install the environment needed for tinty' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "update" -d 'Update to the latest themes' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "sync" -d 'Install missing templates in tinty/config.toml and update existing templates' -complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "cycle" -d 'Cycle through your preferred themes' +complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "cycle" -d 'Cycle through a configured ring of schemes' complete -c tinty -n "__fish_tinty_using_subcommand help; and not __fish_seen_subcommand_from build current generate-completion generate-scheme info init list config apply install update sync cycle help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' diff --git a/contrib/completion/tinty.powershell b/contrib/completion/tinty.powershell index aa31453..4bf7217 100644 --- a/contrib/completion/tinty.powershell +++ b/contrib/completion/tinty.powershell @@ -33,7 +33,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { [CompletionResult]::new('current', 'current', [CompletionResultType]::ParameterValue, 'Prints the last scheme name applied or specific values from the current scheme') [CompletionResult]::new('generate-completion', 'generate-completion', [CompletionResultType]::ParameterValue, 'Generates a shell completion script') [CompletionResult]::new('generate-scheme', 'generate-scheme', [CompletionResultType]::ParameterValue, 'Generates a scheme based on an image') - [CompletionResult]::new('info', 'info', [CompletionResultType]::ParameterValue, 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)') + [CompletionResult]::new('info', 'info', [CompletionResultType]::ParameterValue, 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)') [CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up') [CompletionResult]::new('list', 'list', [CompletionResultType]::ParameterValue, 'Lists available schemes') [CompletionResult]::new('config', 'config', [CompletionResultType]::ParameterValue, 'Provides config related information') @@ -41,7 +41,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { [CompletionResult]::new('install', 'install', [CompletionResultType]::ParameterValue, 'Install the environment needed for tinty') [CompletionResult]::new('update', 'update', [CompletionResultType]::ParameterValue, 'Update to the latest themes') [CompletionResult]::new('sync', 'sync', [CompletionResultType]::ParameterValue, 'Install missing templates in tinty/config.toml and update existing templates') - [CompletionResult]::new('cycle', 'cycle', [CompletionResultType]::ParameterValue, 'Cycle through your preferred themes') + [CompletionResult]::new('cycle', 'cycle', [CompletionResultType]::ParameterValue, 'Cycle through a configured ring of schemes') [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') break } @@ -95,6 +95,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'Optional path to the tinty config.toml file') [CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'Optional path to the tinty data directory') [CompletionResult]::new('--data-dir', '--data-dir', [CompletionResultType]::ParameterName, 'Optional path to the tinty data directory') + [CompletionResult]::new('--all', '--all', [CompletionResultType]::ParameterName, 'Lists all availabile custom schemes') [CompletionResult]::new('--custom-schemes', '--custom-schemes', [CompletionResultType]::ParameterName, 'Lists availabile custom schemes') [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') @@ -177,6 +178,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { break } 'tinty;cycle' { + [CompletionResult]::new('--ring', '--ring', [CompletionResultType]::ParameterName, 'Name of the configured ring to cycle through') [CompletionResult]::new('-c', '-c', [CompletionResultType]::ParameterName, 'Optional path to the tinty config.toml file') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'Optional path to the tinty config.toml file') [CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'Optional path to the tinty data directory') @@ -192,7 +194,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { [CompletionResult]::new('current', 'current', [CompletionResultType]::ParameterValue, 'Prints the last scheme name applied or specific values from the current scheme') [CompletionResult]::new('generate-completion', 'generate-completion', [CompletionResultType]::ParameterValue, 'Generates a shell completion script') [CompletionResult]::new('generate-scheme', 'generate-scheme', [CompletionResultType]::ParameterValue, 'Generates a scheme based on an image') - [CompletionResult]::new('info', 'info', [CompletionResultType]::ParameterValue, 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha)') + [CompletionResult]::new('info', 'info', [CompletionResultType]::ParameterValue, 'Shows scheme colors for all schemes matching - (Eg: tinty info base16-mocha or tinty info tinted8-mocha)') [CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Initializes with the exising config. Used to Initialize exising theme for when your shell starts up') [CompletionResult]::new('list', 'list', [CompletionResultType]::ParameterValue, 'Lists available schemes') [CompletionResult]::new('config', 'config', [CompletionResultType]::ParameterValue, 'Provides config related information') @@ -200,7 +202,7 @@ Register-ArgumentCompleter -Native -CommandName 'tinty' -ScriptBlock { [CompletionResult]::new('install', 'install', [CompletionResultType]::ParameterValue, 'Install the environment needed for tinty') [CompletionResult]::new('update', 'update', [CompletionResultType]::ParameterValue, 'Update to the latest themes') [CompletionResult]::new('sync', 'sync', [CompletionResultType]::ParameterValue, 'Install missing templates in tinty/config.toml and update existing templates') - [CompletionResult]::new('cycle', 'cycle', [CompletionResultType]::ParameterValue, 'Cycle through your preferred themes') + [CompletionResult]::new('cycle', 'cycle', [CompletionResultType]::ParameterValue, 'Cycle through a configured ring of schemes') [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') break } diff --git a/contrib/completion/tinty.zsh b/contrib/completion/tinty.zsh index 3a3a1b1..6d1fba3 100644 --- a/contrib/completion/tinty.zsh +++ b/contrib/completion/tinty.zsh @@ -91,10 +91,11 @@ _arguments "${_arguments_options[@]}" : \ '--config=[Optional path to the tinty config.toml file]:FILE:_default' \ '-d+[Optional path to the tinty data directory]:DIRECTORY:_default' \ '--data-dir=[Optional path to the tinty data directory]:DIRECTORY:_default' \ +'--all[Lists all availabile custom schemes]' \ '--custom-schemes[Lists availabile custom schemes]' \ '-h[Print help]' \ '--help[Print help]' \ -'::scheme_name -- The scheme you want to get information about:_default' \ +'::scheme-name -- The scheme you want to get information about:_default' \ && ret=0 ;; (init) @@ -142,7 +143,7 @@ _arguments "${_arguments_options[@]}" : \ '--quiet[Silence stdout]' \ '-h[Print help]' \ '--help[Print help]' \ -':scheme_name -- The scheme you want to apply:_default' \ +':scheme-name -- The scheme you want to apply:_default' \ && ret=0 ;; (install) @@ -183,6 +184,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (cycle) _arguments "${_arguments_options[@]}" : \ +'--ring=[Name of the configured ring to cycle through]:RING:_default' \ '-c+[Optional path to the tinty config.toml file]:FILE:_default' \ '--config=[Optional path to the tinty config.toml file]:FILE:_default' \ '-d+[Optional path to the tinty data directory]:DIRECTORY:_default' \ @@ -277,7 +279,7 @@ _tinty_commands() { 'current:Prints the last scheme name applied or specific values from the current scheme' \ 'generate-completion:Generates a shell completion script' \ 'generate-scheme:Generates a scheme based on an image' \ -'info:Shows scheme colors for all schemes matching - (Eg\: tinty info base16-mocha)' \ +'info:Shows scheme colors for all schemes matching - (Eg\: tinty info base16-mocha or tinty info tinted8-mocha)' \ 'init:Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' \ 'list:Lists available schemes' \ 'config:Provides config related information' \ @@ -285,7 +287,7 @@ _tinty_commands() { 'install:Install the environment needed for tinty' \ 'update:Update to the latest themes' \ 'sync:Install missing templates in tinty/config.toml and update existing templates' \ -'cycle:Cycle through your preferred themes' \ +'cycle:Cycle through a configured ring of schemes' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'tinty commands' commands "$@" @@ -332,7 +334,7 @@ _tinty__help_commands() { 'current:Prints the last scheme name applied or specific values from the current scheme' \ 'generate-completion:Generates a shell completion script' \ 'generate-scheme:Generates a scheme based on an image' \ -'info:Shows scheme colors for all schemes matching - (Eg\: tinty info base16-mocha)' \ +'info:Shows scheme colors for all schemes matching - (Eg\: tinty info base16-mocha or tinty info tinted8-mocha)' \ 'init:Initializes with the exising config. Used to Initialize exising theme for when your shell starts up' \ 'list:Lists available schemes' \ 'config:Provides config related information' \ @@ -340,7 +342,7 @@ _tinty__help_commands() { 'install:Install the environment needed for tinty' \ 'update:Update to the latest themes' \ 'sync:Install missing templates in tinty/config.toml and update existing templates' \ -'cycle:Cycle through your preferred themes' \ +'cycle:Cycle through a configured ring of schemes' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'tinty help commands' commands "$@" diff --git a/example.toml b/example.toml index 93bc322..1a4d38e 100644 --- a/example.toml +++ b/example.toml @@ -18,6 +18,16 @@ # # Default scheme to apply if a previously set scheme doesn't exist. # # Used specifically in the `tinty init` subcommand. # default-scheme = "base16-mocha" +# # Default ring to use when running `tinty cycle` without `--ring`. +# default-cycle-ring = "default" +# +# [[rings]] +# name = "default" +# schemes = ["base16-mocha", "base16-gruvbox-dark"] +# +# [[rings]] +# name = "light" +# schemes = ["base16-gruvbox-light", "base16-github"] # # [[items]] # # Path to template repository. Can be a URL, absolute path or path @@ -39,6 +49,11 @@ # shell = "zsh -c '{}'" # default-scheme = "base16-oceanicnext" +# default-cycle-ring = "default" +# +# [[rings]] +# name = "default" +# schemes = ["base16-oceanicnext", "base16-gruvbox-dark", "base16-github-dark"] # # [[items]] # path = "https://github.com/tinted-theming/tinted-shell" diff --git a/src/cli.rs b/src/cli.rs index c8b6882..147d62d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -243,7 +243,14 @@ pub fn build_cli() -> Command { ), ) .subcommand( - Command::new("cycle").about("Cycle through your preferred themes") + Command::new("cycle").about("Cycle through a configured ring of schemes") + .arg( + Arg::new("ring") + .long("ring") + .help("Name of the configured ring to cycle through") + .value_name("RING") + .action(ArgAction::Set), + ) .arg( Arg::new("quiet") .long("quiet") diff --git a/src/config.rs b/src/config.rs index e474505..81dd99c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -64,6 +64,29 @@ impl fmt::Display for ConfigItem { } } +/// Structure for configuration cycle rings +#[derive(Deserialize, Debug)] +pub struct ConfigRing { + pub name: String, + pub schemes: Vec, +} + +impl fmt::Display for ConfigRing { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let schemes_text = self + .schemes + .iter() + .map(|scheme| format!("\"{scheme}\"")) + .collect::>() + .join(", "); + + writeln!(f)?; + writeln!(f, "[[rings]]")?; + writeln!(f, "name = \"{}\"", self.name)?; + write!(f, "schemes = [{schemes_text}]") + } +} + /// Structure for configuration #[derive(Deserialize, Debug)] pub struct Config { @@ -72,6 +95,9 @@ pub struct Config { pub default_scheme: Option, #[serde(rename = "preferred-schemes")] pub preferred_schemes: Option>, + pub rings: Option>, + #[serde(rename = "default-cycle-ring")] + pub default_cycle_ring: Option, pub items: Option>, pub hooks: Option>, } @@ -88,6 +114,22 @@ fn ensure_item_name_is_unique(items: &[ConfigItem]) -> Result<()> { Ok(()) } +fn ensure_ring_names_are_valid(rings: &[ConfigRing]) -> Result<()> { + let mut names = HashSet::new(); + + for ring in rings { + if ring.name.trim().is_empty() { + return Err(anyhow!("config.toml rings.name should not be empty")); + } + + if !names.insert(&ring.name) { + return Err(anyhow!("config.toml rings.name should be unique values, but \"{}\" is used for more than 1 rings.name. Please change this to a unique value.", ring.name)); + } + } + + Ok(()) +} + impl Config { pub fn read(path: &Path) -> Result { if path.exists() && !path.is_file() { @@ -131,6 +173,22 @@ impl Config { } } + if let Some(rings) = config.rings.as_ref() { + ensure_ring_names_are_valid(rings)?; + + if let Some(default_cycle_ring) = config.default_cycle_ring.as_ref() { + if !rings.iter().any(|ring| ring.name == *default_cycle_ring) { + return Err(anyhow!( + "config.toml default-cycle-ring is set to \"{default_cycle_ring}\", but no ring with that name exists" + )); + } + } + } else if let Some(default_cycle_ring) = config.default_cycle_ring.as_ref() { + return Err(anyhow!( + "config.toml default-cycle-ring is set to \"{default_cycle_ring}\", but no rings are configured" + )); + } + // Set default `system` property for missing systems if let Some(ref mut items) = config.items { for item in items.iter_mut() { @@ -188,6 +246,10 @@ impl fmt::Display for Config { writeln!(f, "default-scheme = \"{default_scheme}\"")?; } + if let Some(default_cycle_ring) = &self.default_cycle_ring { + writeln!(f, "default-cycle-ring = \"{default_cycle_ring}\"")?; + } + if let Some(items) = &self.preferred_schemes { let preferred_schemes_text = items .clone() @@ -206,6 +268,12 @@ impl fmt::Display for Config { writeln!(f, "]")?; } + if let Some(rings) = &self.rings { + for ring in rings { + writeln!(f, "{ring}")?; + } + } + if let Some(items) = &self.items { for item in items { writeln!(f, "{item}")?; diff --git a/src/main.rs b/src/main.rs index 2b9f055..f90092e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -162,8 +162,9 @@ fn main() -> Result<()> { let is_quiet = sub_matches .get_one::("quiet") .is_some_and(ToOwned::to_owned); + let ring_name = sub_matches.get_one::("ring").map(String::as_str); - operations::cycle::cycle(&config_path, &data_path, is_quiet, None) + operations::cycle::cycle(&config_path, &data_path, is_quiet, ring_name, None) .context("Failed to cycle to your next preferred theme")?; } Some(("install", sub_matches)) => { diff --git a/src/operations/cycle.rs b/src/operations/cycle.rs index 264078d..30c14b0 100644 --- a/src/operations/cycle.rs +++ b/src/operations/cycle.rs @@ -1,26 +1,24 @@ use crate::config::Config; use crate::operations::apply::apply; use crate::operations::current::get_current_scheme_slug; -use crate::utils::{next_scheme_in_cycle, user_curated_scheme_list}; +use crate::utils::{cycle_scheme_list, next_scheme_in_cycle}; use anyhow::Result; use std::path::Path; -/// Cycle to next preferred scheme +/// Cycle to next scheme in a configured ring. pub fn cycle( config_path: &Path, data_path: &Path, is_quiet: bool, + ring_name: Option<&str>, active_operation: Option<&str>, ) -> Result<()> { let config = Config::read(config_path)?; let current_scheme_slug = get_current_scheme_slug(data_path); - // Figure out what the next theme should be given current theme & preferred schemes. - let next_theme = user_curated_scheme_list(&config) - .as_ref() - .map(|vec| next_scheme_in_cycle(¤t_scheme_slug, vec)) - .unwrap_or(current_scheme_slug); + let schemes = cycle_scheme_list(&config, ring_name)?; + let next_theme = next_scheme_in_cycle(¤t_scheme_slug, &schemes); if !is_quiet { println!("Applying next theme in cycle: {next_theme}"); diff --git a/src/utils.rs b/src/utils.rs index 6fa4373..ff0b88e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ #![allow(clippy::arithmetic_side_effects)] -use crate::config::{Config, ConfigItem, DEFAULT_CONFIG_SHELL}; +use crate::config::{Config, ConfigItem, ConfigRing, DEFAULT_CONFIG_SHELL}; use crate::constants::REPO_NAME; use anyhow::{anyhow, Context, Error, Result}; use home::home_dir; @@ -538,32 +538,77 @@ pub fn next_scheme_in_cycle(current: &String, schemes: &[String]) -> String { } } -pub fn user_curated_scheme_list(config: &Config) -> Option> { - // Return a list of preferred schemes based on presence of this value in the config, and - // whatever the default scheme is if specified in config also. - config - .preferred_schemes +fn ring_names(rings: &[ConfigRing]) -> String { + rings + .iter() + .map(|ring| ring.name.clone()) + .collect::>() + .join(", ") +} + +pub fn preferred_schemes_migration_message(config: &Config) -> String { + let mut migration_schemes = config.preferred_schemes.clone().unwrap_or_default(); + + if let Some(default_scheme) = config + .default_scheme .as_ref() - .map(|preferred| { - // If default scheme is defined, add it to the cycle. - config - .default_scheme - .as_ref() - .filter(|default| !preferred.contains(default)) - .map_or_else( - || preferred.clone(), - |default| { - let mut result = vec![default.clone()]; - result.extend(preferred.clone()); - result - }, - ) - }) - .or_else(|| { - // If default scheme is defined, use it if preferred schemes is unset. - config - .default_scheme - .as_ref() - .map(|theme| vec![theme.clone()]) - }) + .filter(|default_scheme| !migration_schemes.contains(default_scheme)) + { + migration_schemes.insert(0, default_scheme.clone()); + } + + let schemes = migration_schemes + .iter() + .map(|scheme| format!("\"{scheme}\"")) + .collect::>() + .join(", "); + + format!( + "`preferred-schemes` is no longer supported by `tinty cycle`.\n\ +Remove `preferred-schemes` from your config and add this instead:\n\n\ +default-cycle-ring = \"default\"\n\n\ +[[rings]]\n\ +name = \"default\"\n\ +schemes = [{schemes}]" + ) +} + +pub fn cycle_scheme_list(config: &Config, requested_ring: Option<&str>) -> Result> { + if config.preferred_schemes.is_some() { + return Err(anyhow!(preferred_schemes_migration_message(config))); + } + + let ring_name = requested_ring + .or(config.default_cycle_ring.as_deref()) + .ok_or_else(|| { + anyhow!( + "`tinty cycle` requires either `default-cycle-ring` in config.toml or `--ring `" + ) + })?; + + let rings = config + .rings + .as_ref() + .ok_or_else(|| anyhow!("`tinty cycle` requires at least one configured `[[rings]]`"))?; + + let ring = rings + .iter() + .find(|ring| ring.name == ring_name) + .ok_or_else(|| { + let available_rings = ring_names(rings); + if available_rings.is_empty() { + anyhow!("No ring named \"{ring_name}\" exists") + } else { + anyhow!("No ring named \"{ring_name}\" exists. Available rings: {available_rings}") + } + })?; + + if ring.schemes.is_empty() { + return Err(anyhow!( + "Ring \"{}\" does not contain any schemes and cannot be cycled", + ring.name + )); + } + + Ok(ring.schemes.clone()) } diff --git a/tests/cli_config_subcommand_tests.rs b/tests/cli_config_subcommand_tests.rs index aacb180..e4f5e16 100644 --- a/tests/cli_config_subcommand_tests.rs +++ b/tests/cli_config_subcommand_tests.rs @@ -71,6 +71,51 @@ themes-dir = "colors" Ok(()) } +#[test] +fn test_cli_config_with_rings() -> Result<()> { + let config_text = r#"shell = "zsh -c '{}'" +default-scheme = "base16-oceanicnext" +default-cycle-ring = "default" +hooks = [ + "echo hook" +] + +[[rings]] +name = "default" +schemes = ["base16-oceanicnext", "base16-gruvbox-dark"] + +[[rings]] +name = "light" +schemes = ["base16-github", "base16-gruvbox-light"] + +[[items]] +name = "tinted-vim" +path = "https://github.com/tinted-theming/tinted-vim" +supported-systems = ["base16", "base24"] +themes-dir = "colors" + +"#; + let (config_path, data_path, command_vec, _temp_dir) = + setup("test_cli_config_with_rings", "config")?; + + write_to_file(&config_path, config_text)?; + + let (stdout, _) = utils::run_command(&command_vec, &data_path, true)?; + + ensure!(stdout == config_text, "std not as expected"); + ensure!( + stdout.find("default-cycle-ring").unwrap_or_default() + < stdout.find("[[rings]]").unwrap_or_default(), + "default-cycle-ring should be printed before [[rings]]" + ); + ensure!( + stdout.find("hooks = [").unwrap_or_default() < stdout.find("[[rings]]").unwrap_or_default(), + "hooks should be printed before [[rings]]" + ); + + Ok(()) +} + #[test] fn test_cli_config_with_config_flag() -> Result<()> { // ------- diff --git a/tests/cli_cycle_subcommand_tests.rs b/tests/cli_cycle_subcommand_tests.rs index 789e78f..36bbbcb 100644 --- a/tests/cli_cycle_subcommand_tests.rs +++ b/tests/cli_cycle_subcommand_tests.rs @@ -1,8 +1,7 @@ //! Integration tests for the `cycle` subcommand. //! -//! Covers: cycling through preferred schemes, default-scheme behavior, -//! wrap-around at end of list, default-scheme prepended to cycle, and -//! deduplication when default-scheme is already in preferred-schemes. +//! Covers cycling through configured rings, default-cycle-ring behavior, +//! wrap-around at end of list, and legacy preferred-schemes migration errors. //! //! Requires network access on first run (repos are cached in `tmp/repos/`). @@ -10,43 +9,45 @@ mod utils; use crate::utils::{setup, write_to_file}; use anyhow::{ensure, Result}; +use std::fs; use utils::build_command_vec; #[test] -fn test_cli_cycle_subcommand_with_default_scheme_only() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_with_explicit_ring() -> Result<()> { let scheme_name = "base16-oceanicnext"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_with_default_scheme_only", + "test_cli_cycle_subcommand_with_explicit_ring", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -default-scheme = "base16-dracula" +[[rings]] +name = "dark" +schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] + +[[rings]] +name = "light" +schemes = ["base16-github", "base16-gruvbox-material-light-soft"] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; let (cycle_stdout, cycle_stderr) = utils::run_command( - &build_command_vec("cycle", config_path.as_path(), data_path.as_path())?, + &build_command_vec( + "cycle --ring dark", + config_path.as_path(), + data_path.as_path(), + )?, &data_path, true, )?; - // ------ - // Assert - // ------ ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" ); ensure!( - cycle_stdout == "Applying next theme in cycle: base16-dracula\n", + cycle_stdout == "Applying next theme in cycle: base24-dracula\n", "cycle_stdout not as expected" ); ensure!( @@ -58,97 +59,61 @@ default-scheme = "base16-dracula" } #[test] -fn test_cli_cycle_subcommand_with_preferred_schemes() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_uses_default_cycle_ring() -> Result<()> { let scheme_name = "base16-oceanicnext"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_with_preferred_schemes", + "test_cli_cycle_subcommand_uses_default_cycle_ring", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] +default-cycle-ring = "dark" + +[[rings]] +name = "dark" +schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; - let (cycle1_stdout, cycle1_stderr) = utils::run_command( + let (cycle_stdout, cycle_stderr) = utils::run_command( &build_command_vec("cycle", config_path.as_path(), data_path.as_path())?, &data_path, true, )?; - // ------ - // Assert - // ------ ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" ); ensure!( - cycle1_stdout == "Applying next theme in cycle: base24-dracula\n", - "cycle1_stdout not as expected" - ); - ensure!( - cycle1_stderr.is_empty(), - "Expected empty stderr, got: {cycle1_stderr}" - ); - - let (cycle2_stdout, cycle2_stderr) = utils::run_command( - &build_command_vec("cycle", config_path.as_path(), data_path.as_path())?, - &data_path, - true, - )?; - - ensure!( - cycle2_stdout == "Applying next theme in cycle: base24-zenburn\n", - "cycle2_stdout not as expected" - ); - ensure!( - cycle2_stderr.is_empty(), - "Expected empty stderr, got: {cycle2_stderr}" - ); - let (cycle3_stdout, cycle3_stderr) = utils::run_command( - &build_command_vec("cycle", config_path.as_path(), data_path.as_path())?, - &data_path, - true, - )?; - - ensure!( - cycle3_stdout == "Applying next theme in cycle: base24-ubuntu\n", - "cycle3 stdout not as expected" + cycle_stdout == "Applying next theme in cycle: base24-dracula\n", + "cycle_stdout not as expected" ); ensure!( - cycle3_stderr.is_empty(), - "Expected empty stderr, got: {cycle3_stderr}" + cycle_stderr.is_empty(), + "Expected empty stderr, got: {cycle_stderr}" ); Ok(()) } #[test] -fn test_cli_cycle_subcommand_correct_next_scheme() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_correct_next_scheme_in_ring() -> Result<()> { let scheme_name = "base24-zenburn"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_correct_next_scheme", + "test_cli_cycle_subcommand_correct_next_scheme_in_ring", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; let (cycle_stdout, cycle_stderr) = utils::run_command( @@ -157,9 +122,6 @@ preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] true, )?; - // ------ - // Assert - // ------ ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" @@ -177,23 +139,21 @@ preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] } #[test] -fn test_cli_cycle_subcommand_wraps_around() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_wraps_around_ring() -> Result<()> { let scheme_name = "base24-ubuntu"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_wraps_around", + "test_cli_cycle_subcommand_wraps_around_ring", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; let (cycle_stdout, cycle_stderr) = utils::run_command( @@ -202,9 +162,6 @@ preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] true, )?; - // ------ - // Assert - // ------ ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" @@ -213,7 +170,6 @@ preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] cycle_stdout == "Applying next theme in cycle: base24-dracula\n", "cycle_stdout not as expected" ); - ensure!( cycle_stderr.is_empty(), "Expected empty stderr, got: {cycle_stderr}" @@ -223,24 +179,21 @@ preferred-schemes = ["base24-dracula", "base24-zenburn", "base24-ubuntu"] } #[test] -fn test_cli_cycle_subcommand_default_scheme_prepended_to_cycle() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_errors_for_empty_ring() -> Result<()> { let scheme_name = "base16-oceanicnext"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_default_scheme_prepended_to_cycle", + "test_cli_cycle_subcommand_errors_for_empty_ring", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -default-scheme = "base24-dracula" -preferred-schemes = ["base24-zenburn", "base24-ubuntu"] +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = [] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; let (cycle_stdout, cycle_stderr) = utils::run_command( @@ -249,44 +202,104 @@ preferred-schemes = ["base24-zenburn", "base24-ubuntu"] true, )?; - // ------ - // Assert - // ------ + let current_scheme = fs::read_to_string(data_path.join("current_scheme"))?; + ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" ); + ensure!(cycle_stdout.is_empty(), "Expected empty stdout"); ensure!( - cycle_stdout == "Applying next theme in cycle: base24-dracula\n", - "cycle_stdout not as expected" + cycle_stderr.contains("Ring \"default\" does not contain any schemes and cannot be cycled"), + "cycle_stderr not as expected: {cycle_stderr}" ); ensure!( - cycle_stderr.is_empty(), - "Expected empty stderr, got: {cycle_stderr}" + current_scheme == scheme_name, + "Expected current scheme to remain unchanged" + ); + + Ok(()) +} + +#[test] +fn test_cli_cycle_subcommand_errors_for_missing_ring() -> Result<()> { + let (config_path, data_path, _apply_command_vec, _temp_dir) = setup( + "test_cli_cycle_subcommand_errors_for_missing_ring", + "cycle --ring dark", + )?; + let config_content = r#" +[[rings]] +name = "light" +schemes = ["base16-github"] +"#; + write_to_file(&config_path, config_content)?; + + let (cycle_stdout, cycle_stderr) = utils::run_command( + &build_command_vec( + "cycle --ring dark", + config_path.as_path(), + data_path.as_path(), + )?, + &data_path, + false, + )?; + + ensure!(cycle_stdout.is_empty(), "Expected empty stdout"); + ensure!( + cycle_stderr.contains("No ring named \"dark\" exists. Available rings: light"), + "cycle_stderr not as expected: {cycle_stderr}" ); Ok(()) } #[test] -fn test_cli_cycle_subcommand_default_scheme_not_duplicated_in_cycle() -> Result<()> { - // ------- - // Arrange - // ------- +fn test_cli_cycle_subcommand_errors_for_duplicate_ring_names() -> Result<()> { + let (config_path, data_path, _apply_command_vec, _temp_dir) = setup( + "test_cli_cycle_subcommand_errors_for_duplicate_ring_names", + "cycle", + )?; + let config_content = r#" +default-cycle-ring = "default" + +[[rings]] +name = "default" +schemes = ["base16-github"] + +[[rings]] +name = "default" +schemes = ["base16-dracula"] +"#; + write_to_file(&config_path, config_content)?; + + let (cycle_stdout, cycle_stderr) = utils::run_command( + &build_command_vec("cycle", config_path.as_path(), data_path.as_path())?, + &data_path, + false, + )?; + + ensure!(cycle_stdout.is_empty(), "Expected empty stdout"); + ensure!( + cycle_stderr.contains("config.toml rings.name should be unique values"), + "cycle_stderr not as expected: {cycle_stderr}" + ); + + Ok(()) +} + +#[test] +fn test_cli_cycle_subcommand_errors_for_preferred_schemes_with_migration() -> Result<()> { let scheme_name = "base16-oceanicnext"; let (config_path, data_path, apply_command_vec, _temp_dir) = setup( - "test_cli_cycle_subcommand_default_scheme_not_duplicated_in_cycle", + "test_cli_cycle_subcommand_errors_for_preferred_schemes_with_migration", format!("apply {scheme_name}").as_str(), )?; let config_content = r#" -default-scheme = "base24-dracula" -preferred-schemes = ["base24-zenburn", "base24-dracula", "base24-ubuntu"] +default-scheme = "base24-ubuntu" +preferred-schemes = ["base24-dracula", "base24-zenburn"] "#; write_to_file(&config_path, config_content)?; - // --- - // Act - // --- let (_, apply_stderr) = utils::run_command(&apply_command_vec, &data_path, true)?; let (cycle_stdout, cycle_stderr) = utils::run_command( @@ -295,20 +308,33 @@ preferred-schemes = ["base24-zenburn", "base24-dracula", "base24-ubuntu"] true, )?; - // ------ - // Assert - // ------ + let current_scheme = fs::read_to_string(data_path.join("current_scheme"))?; + ensure!( apply_stderr.is_empty(), "Expected empty stderr, got: {apply_stderr}" ); + ensure!(cycle_stdout.is_empty(), "Expected empty stdout"); ensure!( - cycle_stdout == "Applying next theme in cycle: base24-zenburn\n", - "cycle_stdout not as expected" + cycle_stderr.contains("`preferred-schemes` is no longer supported by `tinty cycle`"), + "cycle_stderr did not include deprecation message: {cycle_stderr}" ); ensure!( - cycle_stderr.is_empty(), - "Expected empty stderr, got: {cycle_stderr}" + cycle_stderr + .contains("schemes = [\"base24-ubuntu\", \"base24-dracula\", \"base24-zenburn\"]"), + "cycle_stderr did not include migration snippet: {cycle_stderr}" + ); + ensure!( + cycle_stderr.contains("default-cycle-ring = \"default\""), + "cycle_stderr did not include default-cycle-ring: {cycle_stderr}" + ); + ensure!( + cycle_stderr.find("default-cycle-ring = \"default\"") < cycle_stderr.find("[[rings]]"), + "default-cycle-ring should appear before [[rings]] in migration snippet: {cycle_stderr}" + ); + ensure!( + current_scheme == scheme_name, + "Expected current scheme to remain unchanged" ); Ok(())