mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
WIP Add path sort
This sorts paths by basename, dirname or full path - in future possibly size or age. It takes --invert to invert the sort and "--what=basename|dirname|..." to specify what to sort This can be used to implement better conf.d sorting, with something like ```fish set -l sourcelist for file in (path sort --what=basename $__fish_config_dir/conf.d/*.fish $__fish_sysconf_dir/conf.d/*.fish $vendor_confdirs/*.fish) ``` which will iterate over the files by their basename. Then we keep a list of their basenames to skip over anything that was already sourced, like before.
This commit is contained in:
parent
e429f76e9f
commit
9fdfad1d45
1 changed files with 77 additions and 0 deletions
|
@ -14,6 +14,7 @@
|
|||
#include "../common.h"
|
||||
#include "../fallback.h" // IWYU pragma: keep
|
||||
#include "../io.h"
|
||||
#include "../util.h"
|
||||
#include "../wcstringutil.h"
|
||||
#include "../wgetopt.h"
|
||||
#include "../wutil.h" // IWYU pragma: keep
|
||||
|
@ -155,6 +156,9 @@ struct options_t { //!OCLINT(too many fields)
|
|||
bool perm_valid = false;
|
||||
bool type_valid = false;
|
||||
bool invert_valid = false;
|
||||
bool what_valid = false;
|
||||
bool have_what = false;
|
||||
const wchar_t *what = nullptr;
|
||||
|
||||
bool null_in = false;
|
||||
bool null_out = false;
|
||||
|
@ -344,6 +348,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_what(const wchar_t **argv, parser_t &parser, io_streams_t &streams,
|
||||
const wgetopter_t &w, options_t *opts) {
|
||||
UNUSED(argv);
|
||||
UNUSED(parser);
|
||||
UNUSED(streams);
|
||||
opts->have_what = true;
|
||||
opts->what = w.woptarg;
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
||||
/// This constructs the wgetopt() short options string based on which arguments are valid for the
|
||||
/// subcommand. We have to do this because many short flags have multiple meanings and may or may
|
||||
/// not require an argument depending on the meaning.
|
||||
|
@ -372,6 +386,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"what", required_argument, nullptr, 1},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
static const std::unordered_map<char, decltype(*handle_flag_q)> flag_to_function = {
|
||||
|
@ -381,6 +396,7 @@ static const std::unordered_map<char, decltype(*handle_flag_q)> 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},
|
||||
};
|
||||
|
||||
/// Parse the arguments for flags recognized by a specific string subcommand.
|
||||
|
@ -666,6 +682,66 @@ static int path_resolve(parser_t &parser, io_streams_t &streams, int argc, const
|
|||
return n_transformed > 0 ? STATUS_CMD_OK : STATUS_CMD_ERROR;
|
||||
}
|
||||
|
||||
static int path_sort(parser_t &parser, io_streams_t &streams, int argc, const wchar_t **argv) {
|
||||
options_t opts;
|
||||
opts.invert_valid = true;
|
||||
opts.what_valid = true;
|
||||
int optind;
|
||||
int retval = parse_opts(&opts, &optind, 0, argc, argv, parser, streams);
|
||||
if (retval != STATUS_CMD_OK) return retval;
|
||||
|
||||
auto func = +[] (const wcstring &x) {
|
||||
return wbasename(x.c_str());
|
||||
};
|
||||
if (opts.have_what) {
|
||||
if (std::wcscmp(opts.what, L"basename") == 0) {
|
||||
// Do nothing, this is the default
|
||||
} else if (std::wcscmp(opts.what, L"dirname") == 0) {
|
||||
func = +[] (const wcstring &x) {
|
||||
return wdirname(x.c_str());
|
||||
};
|
||||
} else if (std::wcscmp(opts.what, L"path") == 0) {
|
||||
// Act as if --what hadn't been given.
|
||||
opts.have_what = false;
|
||||
} else {
|
||||
path_error(streams, _(L"%ls: Invalid sort key '%ls'\n"), argv[0], opts.what);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
wcstring_list_t list;
|
||||
arg_iterator_t aiter(argv, optind, streams, opts.null_in);
|
||||
while (const wcstring *arg = aiter.nextstr()) {
|
||||
list.push_back(*arg);
|
||||
}
|
||||
|
||||
if (opts.have_what) {
|
||||
// Keep a map to avoid repeated func calls and to keep things alive.
|
||||
std::map<wcstring, wcstring> funced;
|
||||
for (const auto &arg : list) {
|
||||
funced[arg] = func(arg);
|
||||
}
|
||||
|
||||
std::sort(list.begin(), list.end(),
|
||||
[&](const wcstring &a, const wcstring &b) {
|
||||
return (wcsfilecmp_glob(funced[a].c_str(), funced[b].c_str()) < 0) != opts.invert;
|
||||
});
|
||||
} else {
|
||||
// Without --what, we just sort by the entire path,
|
||||
// so we have no need to transform and such.
|
||||
std::sort(list.begin(), list.end(),
|
||||
[&](const wcstring &a, const wcstring &b) {
|
||||
return (wcsfilecmp_glob(a.c_str(), b.c_str()) < 0) != opts.invert;
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto &entry : list) {
|
||||
path_out(streams, opts, entry);
|
||||
}
|
||||
|
||||
/* TODO: Return true only if already sorted? */
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
||||
// All strings are taken to be filenames, and if they match the type/perms/etc (and exist!)
|
||||
// they are passed along.
|
||||
|
@ -723,6 +799,7 @@ static constexpr const struct path_subcommand {
|
|||
{L"is", &path_is},
|
||||
{L"normalize", &path_normalize},
|
||||
{L"resolve", &path_resolve},
|
||||
{L"sort", &path_sort},
|
||||
};
|
||||
ASSERT_SORTED_BY_NAME(path_subcommands);
|
||||
|
||||
|
|
Loading…
Reference in a new issue