From 39fb642c4148cc12eefffcef056a4cbfbbfab314 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 23 Dec 2017 20:47:59 +0300 Subject: fix bash equal sign tokenization --- args.hxx | 47 +++++++++++++++++++++++++++++++++++++++-------- test.cxx | 8 +++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/args.hxx b/args.hxx index 91cd368..42934bc 100644 --- a/args.hxx +++ b/args.hxx @@ -2209,14 +2209,14 @@ namespace args Positional }; - OptionType ParseOption(const std::string &s) + OptionType ParseOption(const std::string &s, bool allowEmpty = false) { - if (s.find(longprefix) == 0 && s.length() > longprefix.length()) + if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length())) { return OptionType::LongFlag; } - if (s.find(shortprefix) == 0 && s.length() > shortprefix.length()) + if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length())) { return OptionType::ShortFlag; } @@ -2435,7 +2435,13 @@ namespace args { if (cur.empty() || choice.find(cur) == 0) { - completion->reply.push_back(choice); + if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) + { + completion->reply.push_back(choice.substr(choice.find(longseparator) + 1)); + } else + { + completion->reply.push_back(choice); + } return true; } @@ -2454,8 +2460,9 @@ namespace args const auto &chunk = *it; auto pos = GetNextPositional(); std::vector commands = GetCommands(); + const auto optionType = ParseOption(chunk, true); - if (!commands.empty() && (chunk.empty() || ParseOption(chunk) == OptionType::Positional)) + if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) { for (auto &cmd : commands) { @@ -2482,7 +2489,7 @@ namespace args if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) { auto choices = pos->HelpChoices(helpParams); - hasPositionalCompletion = !choices.empty(); + hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; for (auto &choice : choices) { AddCompletionReply(chunk, choice); @@ -2513,7 +2520,7 @@ namespace args } } - if (ParseOption(chunk) == OptionType::LongFlag && allowJoinedLongValue) + if (optionType == OptionType::LongFlag && allowJoinedLongValue) { const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); if (separator != chunk.npos) @@ -2527,7 +2534,7 @@ namespace args } } } - } else if (ParseOption(chunk) == OptionType::ShortFlag && allowJoinedShortValue) + } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) { if (chunk.size() > shortprefix.size() + 1) { @@ -2665,6 +2672,30 @@ namespace args std::vector curArgs(++it, end); curArgs.resize(completion->cword); + + if (completion->syntax == "bash") + { + // bash tokenizes --flag=value as --flag=value + for (size_t idx = 0; idx < curArgs.size(); ) + { + if (idx > 0 && curArgs[idx] == "=") + { + curArgs[idx - 1] += "="; + if (idx + 1 < curArgs.size()) + { + curArgs[idx - 1] += curArgs[idx + 1]; + curArgs.erase(curArgs.begin() + idx, curArgs.begin() + idx + 2); + } else + { + curArgs.erase(curArgs.begin() + idx); + } + } else + { + ++idx; + } + } + + } #ifndef ARGS_NOEXCEPT try { diff --git a/test.cxx b/test.cxx index 4768cde..94defee 100644 --- a/test.cxx +++ b/test.cxx @@ -1260,8 +1260,14 @@ TEST_CASE("Completion works as expected", "[args]") args::MapFlag m(p, "mappos", "mappos", {'m', "map"}, {{"1",1}, {"2", 2}}); REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "2", "test", "-m", ""}), Equals("1\n2")); - REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", "--map="}), Equals("--map=1\n--map=2")); + REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", "--map="}), Equals("1\n2")); + REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "2", "test", "--map", "="}), Equals("1\n2")); REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", "-m1"}), Equals("-m1")); + + args::Positional pos(p, "name", "desc"); + REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", ""}), Equals("")); + REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", "-"}), Equals("-f\n-b\n-m")); + REQUIRE_THROWS_WITH(p.ParseArgs(std::vector{"--completion", "bash", "1", "test", "--"}), Equals("--foo\n--bar\n--map")); } #undef ARGS_HXX -- cgit v1.2.1