From fed9a245268df9591a4150fb1f916df0894ca9d3 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Thu, 13 Dec 2018 13:49:24 -0800 Subject: [PATCH] [autocomplete] Add tab-completion support for bash --- README.md | 7 ++-- scripts/vcpkg_completion.bash | 17 ++++++++ toolsrc/src/vcpkg/commands.integrate.cpp | 52 +++++++++++++++++++++++- 3 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 scripts/vcpkg_completion.bash diff --git a/README.md b/README.md index 93262a1cab..e32ef70251 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,12 @@ For more information, see our [using a package](docs/examples/installing-and-usi Additional notes on macOS and Linux support can be found in the [official announcement](https://blogs.msdn.microsoft.com/vcblog/2018/04/24/announcing-a-single-c-library-manager-for-linux-macos-and-windows-vcpkg/). ## Tab-Completion / Auto-Completion -`vcpkg` supports auto-completion of commands, package names, options etc. To enable tab-completion in Powershell, use +`vcpkg` supports auto-completion of commands, package names, options etc in Powershell and bash. To enable tab-completion, use one of the following: ``` -.\vcpkg integrate powershell +PS> .\vcpkg integrate powershell +Linux:~/$ ./vcpkg integrate bash ``` -and restart Powershell. +and restart your console. ## Examples diff --git a/scripts/vcpkg_completion.bash b/scripts/vcpkg_completion.bash new file mode 100644 index 0000000000..804507d585 --- /dev/null +++ b/scripts/vcpkg_completion.bash @@ -0,0 +1,17 @@ +#/usr/bin/env bash + +_vcpkg_completions() +{ + local vcpkg_executable=${COMP_WORDS[0]} + local remaining_command_line=${COMP_LINE:(${#vcpkg_executable}+1)} + COMPREPLY=($(${vcpkg_executable} autocomplete "${remaining_command_line}" -- 2>/dev/null)) + + # Colon is treated as a delimiter in bash. The following workaround + # allows triplet completion to work correctly in the syntax: + # zlib:x64-windows + local cur + _get_comp_words_by_ref -n : cur + __ltrim_colon_completions "$cur" +} + +complete -F _vcpkg_completions vcpkg diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index 82172e3636..c1d3a8c8d6 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include +#include #include #include #include @@ -122,7 +123,7 @@ namespace vcpkg::Commands::Integrate static ElevationPromptChoice elevated_cmd_execute(const std::string& param) { - SHELLEXECUTEINFOW sh_ex_info{}; + SHELLEXECUTEINFOW sh_ex_info {}; sh_ex_info.cbSize = sizeof(sh_ex_info); sh_ex_info.fMask = SEE_MASK_NOCLOSEPROCESS; sh_ex_info.hwnd = nullptr; @@ -404,6 +405,47 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console Checks::exit_with_code(VCPKG_LINE_INFO, rc); } +#elif defined(__unix__) + static void integrate_bash(const VcpkgPaths& paths) + { + const auto home_path = System::get_environment_variable("HOME").value_or_exit(VCPKG_LINE_INFO); + const fs::path bashrc_path = fs::path {home_path} / ".bashrc"; + + auto& fs = paths.get_filesystem(); + const fs::path completion_script_path = paths.scripts / "vcpkg_completion.bash"; + + Expected> maybe_bashrc_content = fs.read_lines(bashrc_path); + Checks::check_exit( + VCPKG_LINE_INFO, maybe_bashrc_content.has_value(), "Unable to read %s", bashrc_path.u8string()); + + std::vector bashrc_content = maybe_bashrc_content.value_or_exit(VCPKG_LINE_INFO); + + std::vector matches; + for (auto&& line : bashrc_content) + { + std::smatch match; + if (std::regex_match(line, match, std::regex {R"###(^source.*scripts/vcpkg_completion.bash$)###"})) + { + matches.push_back(line); + } + } + + if (!matches.empty()) + { + System::print("vcpkg bash completion is already imported to your %s file.\n" + "The following entries were found:\n" + " %s\n" + "Please make sure you have started a new bash shell for the changes to take effect.\n", + bashrc_path.u8string(), + Strings::join("\n ", matches)); + Checks::exit_success(VCPKG_LINE_INFO); + } + + System::print("Adding vcpkg completion entry to %s\n", bashrc_path.u8string()); + bashrc_content.push_back(Strings::format("source %s", completion_script_path.u8string())); + fs.write_contents(bashrc_path, Strings::join("\n", bashrc_content)); + Checks::exit_success(VCPKG_LINE_INFO); + } #endif #if defined(_WIN32) @@ -425,11 +467,12 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console static const std::string REMOVE = "remove"; static const std::string PROJECT = "project"; static const std::string POWERSHELL = "powershell"; + static const std::string BASH = "bash"; } static std::vector valid_arguments(const VcpkgPaths&) { - return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT, Subcommand::POWERSHELL}; + return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT, Subcommand::POWERSHELL, Subcommand::BASH}; } const CommandStructure COMMAND_STRUCTURE = { @@ -463,6 +506,11 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { return integrate_powershell(paths); } +#elif defined(__unix__) + if (args.command_arguments[0] == Subcommand::BASH) + { + return integrate_bash(paths); + } #endif Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown parameter %s for integrate", args.command_arguments[0]);