diff --git a/doc_src/cmds/path.rst b/doc_src/cmds/path.rst index da99da089..ccb90504c 100644 --- a/doc_src/cmds/path.rst +++ b/doc_src/cmds/path.rst @@ -21,7 +21,7 @@ Synopsis path resolve GENERAL_OPTIONS [PATH...] path change-extension GENERAL_OPTIONS EXTENSION [PATH...] path sort GENERAL_OPTIONS [(-v | --invert)] \ - [--what=basename|dirname|path] [([PATH...] + [-u | --unique] [--what=basename|dirname|path] [([PATH...] GENERAL_OPTIONS := [(-z | --null-in)] [(-Z | --null-out)] [(-q | --quiet)] @@ -351,6 +351,8 @@ With ``--invert`` or ``-v`` the sort is reversed. With ``--what=`` only the given path of the path is compared, e.g. ``--what=dirname`` causes only the dirname to be compared, ``--what=basename`` only the basename and ``--what=path`` causes the entire path to be compared (this is the default). +With ``--unique`` or ``-u`` the sort is deduplicated, meaning only the first of a run that have the same key is kept. So if you are sorting by basename, then only the first of each basename is used. + The sort used is stable, so sorting first by basename and then by dirname works and causes the files to be grouped according to directory. It currently returns 0 if it was given any paths. @@ -368,6 +370,10 @@ Examples 10-foo 2-bar + >_ path sort --unique --what=basename $fish_function_path/*.fish + # prints a list of all function files fish would use, sorted by name. + + Combining ``path`` ------------------- diff --git a/src/builtins/path.cpp b/src/builtins/path.cpp index 59b91fcfd..b674523f3 100644 --- a/src/builtins/path.cpp +++ b/src/builtins/path.cpp @@ -157,6 +157,8 @@ struct options_t { //!OCLINT(too many fields) bool type_valid = false; bool invert_valid = false; bool what_valid = false; + bool unique_valid = false; + bool unique = false; bool have_what = false; const wchar_t *what = nullptr; @@ -348,6 +350,16 @@ static int handle_flag_v(const wchar_t **argv, parser_t &parser, io_streams_t &s return STATUS_INVALID_ARGS; } +static int handle_flag_u(const wchar_t **argv, parser_t &parser, io_streams_t &streams, + const wgetopter_t &w, options_t *opts) { + if (opts->unique_valid) { + opts->unique = true; + return STATUS_CMD_OK; + } + path_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; +} + static int handle_flag_what(const wchar_t **argv, parser_t &parser, io_streams_t &streams, const wgetopter_t &w, options_t *opts) { UNUSED(argv); @@ -373,6 +385,7 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co short_opts.append(L"fld"); } if (opts->invert_valid) short_opts.append(L"v"); + if (opts->unique_valid) short_opts.append(L"u"); return short_opts; } @@ -386,6 +399,7 @@ static const struct woption long_options[] = { {L"perm", required_argument, nullptr, 'p'}, {L"type", required_argument, nullptr, 't'}, {L"invert", required_argument, nullptr, 't'}, + {L"unique", no_argument, nullptr, 'u'}, {L"what", required_argument, nullptr, 1}, {}}; @@ -396,7 +410,8 @@ static const std::unordered_map flag_to_function {'r', handle_flag_r}, {'w', handle_flag_w}, {'x', handle_flag_x}, {'f', handle_flag_f}, {'l', handle_flag_l}, {'d', handle_flag_d}, - {1, handle_flag_what}, + {'l', handle_flag_l}, {'d', handle_flag_d}, + {'u', handle_flag_u}, {1, handle_flag_what}, }; /// Parse the arguments for flags recognized by a specific string subcommand. @@ -700,6 +715,7 @@ static int path_sort(parser_t &parser, io_streams_t &streams, int argc, const wc options_t opts; opts.invert_valid = true; opts.what_valid = true; + opts.unique_valid = true; int optind; int retval = parse_opts(&opts, &optind, 0, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; @@ -745,6 +761,13 @@ static int path_sort(parser_t &parser, io_streams_t &streams, int argc, const wc else return (wcsfilecmp_glob(funced[a].c_str(), funced[b].c_str()) > 0); }); + if (opts.unique) { + list.erase(std::unique(list.begin(), list.end(), + [&](const wcstring &a, const wcstring &b) { + return funced[a] == funced[b]; + }), + list.end()); + } } else { // Without --what, we just sort by the entire path, // so we have no need to transform and such. @@ -755,6 +778,9 @@ static int path_sort(parser_t &parser, io_streams_t &streams, int argc, const wc else return (wcsfilecmp_glob(a.c_str(), b.c_str()) > 0); }); + if (opts.unique) { + list.erase(std::unique(list.begin(), list.end()), list.end()); + } } for (const auto &entry : list) { diff --git a/tests/checks/path.fish b/tests/checks/path.fish index 7ba7f126e..6f1ce78c2 100644 --- a/tests/checks/path.fish +++ b/tests/checks/path.fish @@ -155,3 +155,16 @@ path sort --what=basename {def,abc}/{456,123,789,abc,def,0} | path sort --what=d # CHECK: abc/789 # CHECK: abc/abc # CHECK: abc/def + +path sort --unique --what=basename {def,abc}/{456,123,789} def/{abc,def,0} abc/{foo,bar,baz} +# CHECK: def/0 +# CHECK: def/123 +# CHECK: def/456 +# CHECK: def/789 +# CHECK: def/abc +# CHECK: abc/bar +# CHECK: abc/baz +# CHECK: def/def +# CHECK: abc/foo + +