diff --git a/src/vcpkg/commands.integrate.cpp b/src/vcpkg/commands.integrate.cpp index d1591e1ab4..3393cdf171 100644 --- a/src/vcpkg/commands.integrate.cpp +++ b/src/vcpkg/commands.integrate.cpp @@ -459,7 +459,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console for (auto&& line : bashrc_content) { std::smatch match; - if (std::regex_match(line, match, std::regex{R"###(^source.*scripts/vcpkg_completion.bash$)###"})) + if (std::regex_match(line, match, std::regex{R"###(source.*scripts/vcpkg_completion.bash)###"})) { matches.push_back(line); } @@ -482,6 +482,66 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console Checks::exit_success(VCPKG_LINE_INFO); } + static void integrate_zsh(const VcpkgPaths& paths) + { + const auto home_path = get_environment_variable("HOME").value_or_exit(VCPKG_LINE_INFO); + const path zshrc_path = path{home_path} / ".zshrc"; + + auto& fs = paths.get_filesystem(); + const path completion_script_path = paths.scripts / "vcpkg_completion.zsh"; + + Expected> maybe_zshrc_content = fs.read_lines(zshrc_path); + Checks::check_exit(VCPKG_LINE_INFO, maybe_zshrc_content.has_value(), "Unable to read %s", u8string(zshrc_path)); + + std::vector zshrc_content = maybe_zshrc_content.value_or_exit(VCPKG_LINE_INFO); + + // How to use bash completions in zsh: https://stackoverflow.com/a/8492043/10162645 + bool has_autoload_bashcompinit = false; + bool has_bashcompinit = false; + std::vector matches; + for (auto&& line : zshrc_content) + { + std::smatch match; + if (std::regex_match(line, match, std::regex{R"###(source.*scripts/vcpkg_completion.zsh)###"})) + { + matches.push_back(line); + } + else if (std::regex_search(line, std::regex{R"###(^ *autoload[ a-zA-Z0-9-]+bashcompinit)###"}), + std::regex_constants::match_any) + { + has_autoload_bashcompinit = true; + } + if (std::regex_match(line, std::regex{R"###(^ *([^#]*&& *)?bashcompinit)###"})) + { + has_bashcompinit = true; + } + } + + if (!matches.empty()) + { + printf("vcpkg zsh 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 zsh shell for the changes to take effect.\n", + u8string(zshrc_path), + Strings::join("\n ", matches)); + Checks::exit_success(VCPKG_LINE_INFO); + } + + printf("Adding vcpkg completion entry to %s\n", u8string(zshrc_path)); + if (!has_autoload_bashcompinit) + { + zshrc_content.push_back("autoload bashcompinit"); + } + if (!has_bashcompinit) + { + zshrc_content.push_back("bashcompinit"); + } + zshrc_content.push_back(Strings::format("source %s", u8string(completion_script_path))); + fs.write_contents(zshrc_path, Strings::join("\n", zshrc_content) + '\n', VCPKG_LINE_INFO); + Checks::exit_success(VCPKG_LINE_INFO); + } + static void integrate_fish(const VcpkgPaths& paths) { path fish_completions_path; @@ -523,6 +583,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console table.format("vcpkg integrate install", "Make installed packages available user-wide"); table.format("vcpkg integrate remove", "Remove user-wide integration"); table.format("vcpkg integrate bash", "Enable bash tab-completion"); + table.format("vcpkg integrate zsh", "Enable zsh tab-completion"); table.format("vcpkg integrate x-fish", "Enable fish tab-completion"); #endif // ^^^ !defined(_WIN32) } @@ -541,6 +602,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console static const std::string PROJECT = "project"; static const std::string POWERSHELL = "powershell"; static const std::string BASH = "bash"; + static const std::string ZSH = "zsh"; static const std::string FISH = "x-fish"; } @@ -591,6 +653,10 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { return integrate_bash(paths); } + if (args.command_arguments[0] == Subcommand::ZSH) + { + return integrate_zsh(paths); + } if (args.command_arguments[0] == Subcommand::FISH) { return integrate_fish(paths);