From d2d2b646c1bf26629663035b000b1a85ce949c56 Mon Sep 17 00:00:00 2001 From: jmwatte Date: Tue, 17 Nov 2015 12:37:15 +0100 Subject: [PATCH 1/5] Add plugin for Fish shell tab completion --- beetsplug/fish.py | 274 ++++++++++++++++++++++++++++++++++++++++++ docs/plugins/fish.rst | 43 +++++++ 2 files changed, 317 insertions(+) create mode 100644 beetsplug/fish.py create mode 100644 docs/plugins/fish.rst diff --git a/beetsplug/fish.py b/beetsplug/fish.py new file mode 100644 index 000000000..3a7682c2a --- /dev/null +++ b/beetsplug/fish.py @@ -0,0 +1,274 @@ +# This file is part of beets. +# Copyright 2015, winters jean-marie. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""If you use the fish-shell http://fishshell.com/ ... this will do +autocomplete for you. It does the main commands and options for beet +and the plugins. +It gives you all the album and itemfields (like genre, album) but not all the +values for these. It suggest genre: or album: but not genre: Pop..Jazz...Rock +You can get that by specifying ex. --extravalues genre. +""" + +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from beets.plugins import BeetsPlugin +from beets import library, ui +from beets.ui import commands +from operator import attrgetter +import os +BL_NEED2 = """complete -c beet -n '__fish_beet_needs_command' {} {}\n""" +BL_USE3 = """complete -c beet -n '__fish_beet_using_command {}' {} {}\n""" +BL_SUBS = """complete -c beet -n '__fish_at_level {} ""' {} {}\n""" +BL_EXTRA3 = """complete -c beet -n '__fish_beet_use_extra {}' {} {}\n""" + +HEAD = ''' +function __fish_beet_needs_command + set cmd (commandline -opc) + if test (count $cmd) -eq 1 + return 0 + end + return 1 +end + +function __fish_beet_using_command + set cmd (commandline -opc) + set needle (count $cmd) + if test $needle -gt 1 + if begin test $argv[1] = $cmd[2]; + and not contains -- $cmd[$needle] $FIELDS; end + return 0 + end + end + return 1 +end + +function __fish_beet_use_extra + set cmd (commandline -opc) + set needle (count $cmd) + if test $argv[2] = $cmd[$needle] + return 0 + end + return 1 +end +''' + + +class FishPlugin(BeetsPlugin): + + def commands(self): + cmd = ui.Subcommand('fish', help='make fish autocomplete beet') + cmd.func = self.run + cmd.parser.add_option('-f', '--noFields', action='store_true', + default=False, + help='no item/album fields for autocomplete') + cmd.parser.add_option( + '-e', + '--extravalues', + action='append', + type='choice', + choices=library.Item.all_keys() + + library.Album.all_keys(), + help='pick field, get field-values for autocomplete') + return [cmd] + + def run(self, lib, opts, args): + # we gather the commands from beet and from the plugins. + # we take the album and item fields. + # it wanted, we take the values from these fields. + # we make a giant string of tehm formatted in a way that + # allows fish to do autocompletion for beet. + homeDir = os.path.expanduser("~") + completePath = os.path.join(homeDir, '.config/fish/completions') + try: + os.makedirs(completePath) + except OSError: + if not os.path.isdir(completePath): + raise + pathAndFile = os.path.join(completePath, 'beet.fish') + nobasicfields = opts.noFields # do not complete for item/album fields + extravalues = opts.extravalues # ex complete all artist values + beetcmds = sorted( + (commands.default_commands + + commands.plugins.commands()), + key=attrgetter('name')) + fields = sorted(set( + library.Album.all_keys() + library.Item.all_keys())) + # collect cmds and their aliases and their help message + cmd_names_help = [] + for cmd in beetcmds: + names = ["\?" if alias == "?" else alias for alias in cmd.aliases] + names.append(cmd.name) + for name in names: + cmd_names_help.append((name, cmd.help)) + # here we go assembling the string + totstring = HEAD + "\n" + totstring += get_cmds_list([name[0] for name in cmd_names_help]) + totstring += '' if nobasicfields else get_standard_fields(fields) + totstring += get_extravalues(lib, extravalues) if extravalues else '' + totstring += "\n" + "# ====== {} =====".format( + "setup basic beet completion") + "\n" * 2 + totstring += get_basic_beet_options() + totstring += "\n" + "# ====== {} =====".format( + "setup field completion for subcommands") + "\n" + totstring += get_subcommands( + cmd_names_help, nobasicfields, extravalues) + # setup completion for all the command-options + totstring += get_all_commands(beetcmds) + + with open(pathAndFile, 'w') as fish_file: + fish_file.write(totstring) + + +def get_cmds_list(cmds_names): + # make list of all commands in beet&plugins + substr = '' + substr += ( + "set CMDS " + " ".join(cmds_names) + ("\n" * 2) + ) + return substr + + +def get_standard_fields(fields): + # make list of item/album fields & append with ':' + fields = (field + ":" for field in fields) + substr = '' + substr += ( + "set FIELDS " + " ".join(fields) + ("\n" * 2) + ) + return substr + + +def get_extravalues(lib, extravalues): + # make list of all values from a item/album field + # so type artist: and get completion for stones, beatles .. + word = '' + setOfValues = get_set_of_values_for_field(lib, extravalues) + for fld in extravalues: + extraname = fld.upper() + 'S' + word += ( + "set " + extraname + " " + " ".join(sorted(setOfValues[fld])) + + ("\n" * 2) + ) + return word + + +def get_set_of_values_for_field(lib, fields): + # get the unique values from a item/album field + dictOfFields = {} + for each in fields: + dictOfFields[each] = set() + for item in lib.items(): + for field in fields: + dictOfFields[field].add(wrap(item[field])) + return dictOfFields + + +def get_basic_beet_options(): + word = ( + BL_NEED2.format("-l format-item", + "-f -d 'print with custom format'") + + BL_NEED2.format("-l format-album", + "-f -d 'print with custom format'") + + BL_NEED2.format("-s l -l library", + "-f -r -d 'library database file to use'") + + BL_NEED2.format("-s d -l directory", + "-f -r -d 'destination music directory'") + + BL_NEED2.format("-s v -l verbose", + "-f -d 'print debugging information'") + + + BL_NEED2.format("-s c -l config", + "-f -r -d 'path to configuration file'") + + BL_NEED2.format("-s h -l help", + "-f -d 'print this help message and exit'")) + return word + + +def get_subcommands(cmd_name_and_help, nobasicfields, extravalues): + # formatting for fish to complete our fields/values + word = "" + for cmdname, cmdhelp in cmd_name_and_help: + word += "\n" + "# ------ {} -------".format( + "fieldsetups for " + cmdname) + "\n" + word += ( + BL_NEED2.format( + ("-a " + cmdname), + ("-f " + "-d " + wrap(clean_whitespace(cmdhelp))))) + + if nobasicfields is False: + word += ( + BL_USE3.format( + cmdname, + ("-a " + wrap("$FIELDS")), + ("-f " + "-d " + wrap("fieldname")))) + + if extravalues: + for f in extravalues: + setvar = wrap("$" + f.upper() + "S") + word += " ".join(BL_EXTRA3.format( + (cmdname + " " + f + ":"), + ('-f ' + '-A ' + '-a ' + setvar), + ('-d ' + wrap(f))).split()) + "\n" + return word + + +def get_all_commands(beetcmds): + # formatting for fish to complete command-options + word = "" + for cmd in beetcmds: + names = ["\?" if alias == "?" else alias for alias in cmd.aliases] + names.append(cmd.name) + for name in names: + word += "\n" + word += ("\n" * 2) + "# ====== {} =====".format( + "completions for " + name) + "\n" + + for option in cmd.parser._get_all_options()[1:]: + cmd_LO = (" -l " + option._long_opts[0].replace('--', '') + )if option._long_opts else '' + cmd_SO = (" -s " + option._short_opts[0].replace('-', '') + ) if option._short_opts else '' + cmd_needARG = ' -r ' if option.nargs in [1] else '' + cmd_helpstr = (" -d " + wrap(' '.join(option.help.split())) + ) if option.help else '' + cmd_arglist = (' -a ' + wrap(" ".join(option.choices)) + ) if option.choices else '' + + word += " ".join(BL_USE3.format( + name, + (cmd_needARG + cmd_SO + cmd_LO + " -f " + cmd_arglist), + cmd_helpstr).split()) + "\n" + + word = (word + " ".join(BL_USE3.format( + name, + ("-s " + "h " + "-l " + "help" + " -f "), + ('-d ' + wrap("print help") + "\n") + ).split())) + return word + + +def clean_whitespace(word): + # remove to much whitespace,tabs in string + return " ".join(word.split()) + + +def wrap(word): + # need " or ' around strings but watch out if they're in the string + sptoken = '\"' + if ('"') in word and ("'") in word: + word.replace('"', sptoken) + return '"' + word + '"' + + tok = '"' if "'" in word else "'" + return tok + word + tok diff --git a/docs/plugins/fish.rst b/docs/plugins/fish.rst new file mode 100644 index 000000000..a6e41a46d --- /dev/null +++ b/docs/plugins/fish.rst @@ -0,0 +1,43 @@ +Fish plugins +============ + +The ``fish`` plugin adds a ``beet fish`` command that will create a fish +autocompletion file ``beet.fish`` in ``~/.config/fish/completions`` +This makes `fish`_ - a different shell - autocomplete commands for beet. + +.. _fish: http://fishshell.com/ + +Configuring +=========== + +This will only make sense if you have the `fish`_ shell installed. +Enable the ``fish`` plugin (see :ref:`using-plugins`). +If you install or disable plugins, run ``beet fish`` again. It takes the values +from the plugins you have enabled. + +Using +===== + +Type ``beet fish``. Hit ``enter`` and will see the file ``beet.fish`` appear +in ``.config/fish/completions`` in your home folder. + +For a not-fish user: After you type ``beet`` in your fish-prompt and ``TAB`` +you will get the autosuggestions for all your plugins/commands and +typing ``-`` will get you all the options available to you. +If you type ``beet ls`` and you ``TAB`` you will get a list of all the album/item +fields that beet offers. Start typing ``genr`` ``TAB`` and fish completes +``genre:`` ... ready to type on... + +Options +======= + +The default is that you get autocompletion for all the album/item fields. +You can disable that with ``beet fish -f`` In that case you only get all +the plugins/commands/options. Everything else you type in yourself. +If you want completion for a specific album/item field, you can get that like +this ``beet fish -e genre`` or ``beet fish -e genre -e albumartist`` . +Then when you type at your fish-prompt ``beet list genre:`` and you ``TAB`` +you will get a list of all your genres to choose from. +REMEMBER : we get all the values of these fields and put them in the completion +file. It is not meant to be a replacement of your database. In other words : +speed and size matters. From 82c3867fc086e6729218c2884f494cfefbd11f56 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Mar 2020 13:52:35 +0100 Subject: [PATCH 2/5] Rewrite Fish completion plugin docs & code comments --- beetsplug/fish.py | 58 ++++++++++++++++--------------- docs/plugins/fish.rst | 79 ++++++++++++++++++++++++------------------- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/beetsplug/fish.py b/beetsplug/fish.py index 3a7682c2a..b81b9c387 100644 --- a/beetsplug/fish.py +++ b/beetsplug/fish.py @@ -1,5 +1,6 @@ # This file is part of beets. # Copyright 2015, winters jean-marie. +# Copyright 2020, Justin Mayer # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -12,12 +13,13 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""If you use the fish-shell http://fishshell.com/ ... this will do -autocomplete for you. It does the main commands and options for beet -and the plugins. -It gives you all the album and itemfields (like genre, album) but not all the -values for these. It suggest genre: or album: but not genre: Pop..Jazz...Rock -You can get that by specifying ex. --extravalues genre. +"""This plugin generates tab completions for Beets commands for the Fish shell +, including completions for Beets commands, plugin +commands, and option flags. Also generated are completions for all the album +and track fields, suggesting for example `genre:` or `album:` when querying the +Beets database. Completions for the *values* of those fields are not generated by +default but can be included via the `-e` or `--extravalues` flag. For example: +`beet fish -e genre -e albumartist` """ from __future__ import (division, absolute_import, print_function, @@ -68,11 +70,11 @@ end class FishPlugin(BeetsPlugin): def commands(self): - cmd = ui.Subcommand('fish', help='make fish autocomplete beet') + cmd = ui.Subcommand('fish', help='generate Fish shell tab completions') cmd.func = self.run cmd.parser.add_option('-f', '--noFields', action='store_true', default=False, - help='no item/album fields for autocomplete') + help='omit album/track field completions') cmd.parser.add_option( '-e', '--extravalues', @@ -80,15 +82,15 @@ class FishPlugin(BeetsPlugin): type='choice', choices=library.Item.all_keys() + library.Album.all_keys(), - help='pick field, get field-values for autocomplete') + help='include specified field *values* in completions') return [cmd] def run(self, lib, opts, args): - # we gather the commands from beet and from the plugins. - # we take the album and item fields. - # it wanted, we take the values from these fields. - # we make a giant string of tehm formatted in a way that - # allows fish to do autocompletion for beet. + # Gather the commands from Beets core and its plugins. + # Collect the album and track fields. + # If specified, also collect the values for these fields. + # Make a giant string of all the above, formatted in a way that + # allows Fish to do tab completion for the `beet` command. homeDir = os.path.expanduser("~") completePath = os.path.join(homeDir, '.config/fish/completions') try: @@ -97,22 +99,22 @@ class FishPlugin(BeetsPlugin): if not os.path.isdir(completePath): raise pathAndFile = os.path.join(completePath, 'beet.fish') - nobasicfields = opts.noFields # do not complete for item/album fields - extravalues = opts.extravalues # ex complete all artist values + nobasicfields = opts.noFields # Do not complete for album/track fields + extravalues = opts.extravalues # e.g., Also complete artists names beetcmds = sorted( (commands.default_commands + commands.plugins.commands()), key=attrgetter('name')) fields = sorted(set( library.Album.all_keys() + library.Item.all_keys())) - # collect cmds and their aliases and their help message + # Collect commands, their aliases, and their help text cmd_names_help = [] for cmd in beetcmds: names = ["\?" if alias == "?" else alias for alias in cmd.aliases] names.append(cmd.name) for name in names: cmd_names_help.append((name, cmd.help)) - # here we go assembling the string + # Concatenate the string totstring = HEAD + "\n" totstring += get_cmds_list([name[0] for name in cmd_names_help]) totstring += '' if nobasicfields else get_standard_fields(fields) @@ -124,7 +126,7 @@ class FishPlugin(BeetsPlugin): "setup field completion for subcommands") + "\n" totstring += get_subcommands( cmd_names_help, nobasicfields, extravalues) - # setup completion for all the command-options + # Set up completion for all the command options totstring += get_all_commands(beetcmds) with open(pathAndFile, 'w') as fish_file: @@ -132,7 +134,7 @@ class FishPlugin(BeetsPlugin): def get_cmds_list(cmds_names): - # make list of all commands in beet&plugins + # Make a list of all Beets core & plugin commands substr = '' substr += ( "set CMDS " + " ".join(cmds_names) + ("\n" * 2) @@ -141,7 +143,7 @@ def get_cmds_list(cmds_names): def get_standard_fields(fields): - # make list of item/album fields & append with ':' + # Make a list of album/track fields and append with ':' fields = (field + ":" for field in fields) substr = '' substr += ( @@ -151,8 +153,8 @@ def get_standard_fields(fields): def get_extravalues(lib, extravalues): - # make list of all values from a item/album field - # so type artist: and get completion for stones, beatles .. + # Make a list of all values from an album/track field. + # 'beet ls albumartist: ' yields completions for ABBA, Beatles, etc. word = '' setOfValues = get_set_of_values_for_field(lib, extravalues) for fld in extravalues: @@ -165,7 +167,7 @@ def get_extravalues(lib, extravalues): def get_set_of_values_for_field(lib, fields): - # get the unique values from a item/album field + # Get unique values from a specified album/track field dictOfFields = {} for each in fields: dictOfFields[each] = set() @@ -196,7 +198,7 @@ def get_basic_beet_options(): def get_subcommands(cmd_name_and_help, nobasicfields, extravalues): - # formatting for fish to complete our fields/values + # Formatting for Fish to complete our fields/values word = "" for cmdname, cmdhelp in cmd_name_and_help: word += "\n" + "# ------ {} -------".format( @@ -224,7 +226,7 @@ def get_subcommands(cmd_name_and_help, nobasicfields, extravalues): def get_all_commands(beetcmds): - # formatting for fish to complete command-options + # Formatting for Fish to complete command options word = "" for cmd in beetcmds: names = ["\?" if alias == "?" else alias for alias in cmd.aliases] @@ -259,12 +261,12 @@ def get_all_commands(beetcmds): def clean_whitespace(word): - # remove to much whitespace,tabs in string + # Remove excess whitespace and tabs in a string return " ".join(word.split()) def wrap(word): - # need " or ' around strings but watch out if they're in the string + # Need " or ' around strings but watch out if they're in the string sptoken = '\"' if ('"') in word and ("'") in word: word.replace('"', sptoken) diff --git a/docs/plugins/fish.rst b/docs/plugins/fish.rst index a6e41a46d..b2cb096ee 100644 --- a/docs/plugins/fish.rst +++ b/docs/plugins/fish.rst @@ -1,43 +1,52 @@ -Fish plugins -============ - -The ``fish`` plugin adds a ``beet fish`` command that will create a fish -autocompletion file ``beet.fish`` in ``~/.config/fish/completions`` -This makes `fish`_ - a different shell - autocomplete commands for beet. - -.. _fish: http://fishshell.com/ - -Configuring +Fish Plugin =========== -This will only make sense if you have the `fish`_ shell installed. -Enable the ``fish`` plugin (see :ref:`using-plugins`). -If you install or disable plugins, run ``beet fish`` again. It takes the values -from the plugins you have enabled. +The ``fish`` plugin adds a ``beet fish`` command that creates a `Fish shell`_ +tab-completion file named ``beet.fish`` in ``~/.config/fish/completions``. +This enables tab-completion of ``beet`` commands for the `Fish shell`_. -Using -===== +.. _Fish shell: https://fishshell.com/ -Type ``beet fish``. Hit ``enter`` and will see the file ``beet.fish`` appear -in ``.config/fish/completions`` in your home folder. +Configuration +------------- -For a not-fish user: After you type ``beet`` in your fish-prompt and ``TAB`` -you will get the autosuggestions for all your plugins/commands and -typing ``-`` will get you all the options available to you. -If you type ``beet ls`` and you ``TAB`` you will get a list of all the album/item -fields that beet offers. Start typing ``genr`` ``TAB`` and fish completes -``genre:`` ... ready to type on... +Enable the ``fish`` plugin (see :ref:`using-plugins`) on a system running the +`Fish shell`_. + +Usage +----- + +Type ``beet fish`` to generate the ``beet.fish`` completions file at: +``~/.config/fish/completions/``. If you later install or disable plugins, run +``beet fish`` again to update the completions based on the enabled plugins. + +For users not accustomed to tab completion… After you type ``beet`` followed by +a space in your shell prompt and then the ``TAB`` key, you should see a list of +the beets commands (and their abbreviated versions) that can be invoked in your +current environment. Similarly, typing ``beet -`` will show you all the +option flags available to you, which also applies to subcommands such as +``beet import -``. If you type ``beet ls`` followed by a space and then the +and the ``TAB`` key, you will see a list of all the album/track fields that can +be used in beets queries. For example, typing ``beet ls ge`` will complete +to ``genre:`` and leave you ready to type the rest of your query. Options -======= +------- -The default is that you get autocompletion for all the album/item fields. -You can disable that with ``beet fish -f`` In that case you only get all -the plugins/commands/options. Everything else you type in yourself. -If you want completion for a specific album/item field, you can get that like -this ``beet fish -e genre`` or ``beet fish -e genre -e albumartist`` . -Then when you type at your fish-prompt ``beet list genre:`` and you ``TAB`` -you will get a list of all your genres to choose from. -REMEMBER : we get all the values of these fields and put them in the completion -file. It is not meant to be a replacement of your database. In other words : -speed and size matters. +In addition to beets commands, plugin commands, and option flags, the generated +completions also include by default all the album/track fields. If you only want +the former and do not want the album/track fields included in the generated +completions, use ``beet fish -f`` to only generate completions for beets/plugin +commands and option flags. + +If you want generated completions to also contain album/track field *values* for +the items in your library, you can use the ``-e`` or ``--extravalues`` option. +For example: ``beet fish -e genre`` or ``beet fish -e genre -e albumartist`` +In the latter case, subsequently typing ``beet list genre: `` will display +a list of all the genres in your library and ``beet list albumartist: `` +will show a list of the album artists in your library. Keep in mind that all of +these values will be put into the generated completions file, so use this option +with care when specified fields contain a large number of values. Libraries with, +for example, very large numbers of genres/artists may result in higher memory +utilization, completion latency, et cetera. This option is not meant to replace +database queries altogether. From 05db0d18eb7f01f319bb30deb6d348d0ffe5150d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Mar 2020 15:38:56 +0100 Subject: [PATCH 3/5] Don't escape question marks in Fish completions Fish shell previously interpreted question marks as glob characters, but that behavior has been deprecated and will soon be removed. Plus, the completion for `help` and its alias `?` does not currently seem to behave as expected anyway and is thus, at present, of limited utility. --- beetsplug/fish.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/fish.py b/beetsplug/fish.py index b81b9c387..fd9753733 100644 --- a/beetsplug/fish.py +++ b/beetsplug/fish.py @@ -110,7 +110,7 @@ class FishPlugin(BeetsPlugin): # Collect commands, their aliases, and their help text cmd_names_help = [] for cmd in beetcmds: - names = ["\?" if alias == "?" else alias for alias in cmd.aliases] + names = [alias for alias in cmd.aliases] names.append(cmd.name) for name in names: cmd_names_help.append((name, cmd.help)) @@ -229,7 +229,7 @@ def get_all_commands(beetcmds): # Formatting for Fish to complete command options word = "" for cmd in beetcmds: - names = ["\?" if alias == "?" else alias for alias in cmd.aliases] + names = [alias for alias in cmd.aliases] names.append(cmd.name) for name in names: word += "\n" From f465c90e78d6bb102aec307057e37ad85e55422a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Mar 2020 15:46:04 +0100 Subject: [PATCH 4/5] Enforce PEP-8 compliance on Fish completion plugin --- beetsplug/fish.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/beetsplug/fish.py b/beetsplug/fish.py index fd9753733..b842ac70f 100644 --- a/beetsplug/fish.py +++ b/beetsplug/fish.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2015, winters jean-marie. # Copyright 2020, Justin Mayer @@ -17,13 +18,12 @@ , including completions for Beets commands, plugin commands, and option flags. Also generated are completions for all the album and track fields, suggesting for example `genre:` or `album:` when querying the -Beets database. Completions for the *values* of those fields are not generated by -default but can be included via the `-e` or `--extravalues` flag. For example: +Beets database. Completions for the *values* of those fields are not generated +by default but can be added via the `-e` / `--extravalues` flag. For example: `beet fish -e genre -e albumartist` """ -from __future__ import (division, absolute_import, print_function, - unicode_literals) +from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import library, ui @@ -91,14 +91,14 @@ class FishPlugin(BeetsPlugin): # If specified, also collect the values for these fields. # Make a giant string of all the above, formatted in a way that # allows Fish to do tab completion for the `beet` command. - homeDir = os.path.expanduser("~") - completePath = os.path.join(homeDir, '.config/fish/completions') + home_dir = os.path.expanduser("~") + completion_dir = os.path.join(home_dir, '.config/fish/completions') try: - os.makedirs(completePath) + os.makedirs(completion_dir) except OSError: - if not os.path.isdir(completePath): + if not os.path.isdir(completion_dir): raise - pathAndFile = os.path.join(completePath, 'beet.fish') + completion_file_path = os.path.join(completion_dir, 'beet.fish') nobasicfields = opts.noFields # Do not complete for album/track fields extravalues = opts.extravalues # e.g., Also complete artists names beetcmds = sorted( @@ -129,7 +129,7 @@ class FishPlugin(BeetsPlugin): # Set up completion for all the command options totstring += get_all_commands(beetcmds) - with open(pathAndFile, 'w') as fish_file: + with open(completion_file_path, 'w') as fish_file: fish_file.write(totstring) @@ -156,11 +156,11 @@ def get_extravalues(lib, extravalues): # Make a list of all values from an album/track field. # 'beet ls albumartist: ' yields completions for ABBA, Beatles, etc. word = '' - setOfValues = get_set_of_values_for_field(lib, extravalues) + values_set = get_set_of_values_for_field(lib, extravalues) for fld in extravalues: extraname = fld.upper() + 'S' word += ( - "set " + extraname + " " + " ".join(sorted(setOfValues[fld])) + "set " + extraname + " " + " ".join(sorted(values_set[fld])) + ("\n" * 2) ) return word @@ -168,13 +168,13 @@ def get_extravalues(lib, extravalues): def get_set_of_values_for_field(lib, fields): # Get unique values from a specified album/track field - dictOfFields = {} + fields_dict = {} for each in fields: - dictOfFields[each] = set() + fields_dict[each] = set() for item in lib.items(): for field in fields: - dictOfFields[field].add(wrap(item[field])) - return dictOfFields + fields_dict[field].add(wrap(item[field])) + return fields_dict def get_basic_beet_options(): @@ -237,11 +237,11 @@ def get_all_commands(beetcmds): "completions for " + name) + "\n" for option in cmd.parser._get_all_options()[1:]: - cmd_LO = (" -l " + option._long_opts[0].replace('--', '') - )if option._long_opts else '' - cmd_SO = (" -s " + option._short_opts[0].replace('-', '') - ) if option._short_opts else '' - cmd_needARG = ' -r ' if option.nargs in [1] else '' + cmd_l = (" -l " + option._long_opts[0].replace('--', '') + )if option._long_opts else '' + cmd_s = (" -s " + option._short_opts[0].replace('-', '') + ) if option._short_opts else '' + cmd_need_arg = ' -r ' if option.nargs in [1] else '' cmd_helpstr = (" -d " + wrap(' '.join(option.help.split())) ) if option.help else '' cmd_arglist = (' -a ' + wrap(" ".join(option.choices)) @@ -249,7 +249,7 @@ def get_all_commands(beetcmds): word += " ".join(BL_USE3.format( name, - (cmd_needARG + cmd_SO + cmd_LO + " -f " + cmd_arglist), + (cmd_need_arg + cmd_s + cmd_l + " -f " + cmd_arglist), cmd_helpstr).split()) + "\n" word = (word + " ".join(BL_USE3.format( From 14a654bbdbbeacf2c5792d112a0ef4eb03c06a10 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Mar 2020 09:56:40 +0100 Subject: [PATCH 5/5] Add Fish completion to changelog & plugin index --- docs/changelog.rst | 2 ++ docs/plugins/index.rst | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7ef19871b..e33cf8c12 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,7 @@ Changelog New features: +* A new :doc:`/plugins/fish` adds `Fish shell`_ tab autocompletion to beets * :doc:`plugins/fetchart` and :doc:`plugins/embedart`: Added a new ``quality`` option that controls the quality of the image output when the image is resized. @@ -217,6 +218,7 @@ For packagers: the test may no longer be necessary. * This version drops support for Python 3.4. +.. _Fish shell: https://fishshell.com/ .. _MediaFile: https://github.com/beetbox/mediafile .. _Confuse: https://github.com/beetbox/confuse .. _works: https://musicbrainz.org/doc/Work diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 383466a68..6c643ce61 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -78,6 +78,7 @@ following to your configuration:: export fetchart filefilter + fish freedesktop fromfilename ftintitle @@ -184,6 +185,7 @@ Interoperability * :doc:`badfiles`: Check audio file integrity. * :doc:`embyupdate`: Automatically notifies `Emby`_ whenever the beets library changes. +* :doc:`fish`: Adds `Fish shell`_ tab autocompletion to ``beet`` commands. * :doc:`importfeeds`: Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. * :doc:`ipfs`: Import libraries from friends and get albums from them via ipfs. * :doc:`kodiupdate`: Automatically notifies `Kodi`_ whenever the beets library @@ -203,6 +205,7 @@ Interoperability .. _Emby: https://emby.media +.. _Fish shell: https://fishshell.com/ .. _Plex: https://plex.tv .. _Kodi: https://kodi.tv .. _Sonos: https://sonos.com @@ -326,4 +329,4 @@ Here are a few of the plugins written by the beets community: .. _beet-summarize: https://github.com/steven-murray/beet-summarize .. _beets-mosaic: https://github.com/SusannaMaria/beets-mosaic .. _beets-bpmanalyser: https://github.com/adamjakab/BeetsPluginBpmAnalyser -.. _beets-goingrunning: https://pypi.org/project/beets-goingrunning/ \ No newline at end of file +.. _beets-goingrunning: https://pypi.org/project/beets-goingrunning/