diff options
author | amatanhead <amatanhead@yandex-team.ru> | 2022-02-10 16:50:04 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:50:04 +0300 |
commit | 8879605a63ac17539be5b3bd41b529791f4d4b02 (patch) | |
tree | 5739c7303cbe09d02b881e25bb294a4a173422a0 /library | |
parent | 830fe7ae4073c2707f3f3138303ccc56052c0327 (diff) | |
download | ydb-8879605a63ac17539be5b3bd41b529791f4d4b02.tar.gz |
Restoring authorship annotation for <amatanhead@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'library')
66 files changed, 5185 insertions, 5185 deletions
diff --git a/library/cpp/actors/core/actorsystem.cpp b/library/cpp/actors/core/actorsystem.cpp index c58698a206..fb32251ddf 100644 --- a/library/cpp/actors/core/actorsystem.cpp +++ b/library/cpp/actors/core/actorsystem.cpp @@ -10,7 +10,7 @@ #include "scheduler_actor.h" #include "log.h" #include "probes.h" -#include "ask.h" +#include "ask.h" #include <library/cpp/actors/util/affinity.h> #include <library/cpp/actors/util/datetime.h> #include <util/generic/hash.h> @@ -145,14 +145,14 @@ namespace NActors { return CpuManager->GetExecutorPool(executorPool)->Register(actor, mailboxType, revolvingCounter, parentId); } - NThreading::TFuture<THolder<IEventBase>> TActorSystem::AskGeneric(TMaybe<ui32> expectedEventType, + NThreading::TFuture<THolder<IEventBase>> TActorSystem::AskGeneric(TMaybe<ui32> expectedEventType, TActorId recipient, THolder<IEventBase> event, - TDuration timeout) { - auto promise = NThreading::NewPromise<THolder<IEventBase>>(); - Register(MakeAskActor(expectedEventType, recipient, std::move(event), timeout, promise).Release()); - return promise.GetFuture(); - } - + TDuration timeout) { + auto promise = NThreading::NewPromise<THolder<IEventBase>>(); + Register(MakeAskActor(expectedEventType, recipient, std::move(event), timeout, promise).Release()); + return promise.GetFuture(); + } + ui64 TActorSystem::AllocateIDSpace(ui64 count) { Y_VERIFY_DEBUG(count < Max<ui32>() / 65536); diff --git a/library/cpp/actors/core/actorsystem.h b/library/cpp/actors/core/actorsystem.h index 40499d7586..85e3af817d 100644 --- a/library/cpp/actors/core/actorsystem.h +++ b/library/cpp/actors/core/actorsystem.h @@ -261,7 +261,7 @@ namespace NActors { bool Send(TAutoPtr<IEventHandle> ev) const; bool Send(const TActorId& recipient, IEventBase* ev, ui32 flags = 0) const; - /** + /** * Schedule one-shot event that will be send at given time point in the future. * * @param deadline the wallclock time point in future when event must be send @@ -289,38 +289,38 @@ namespace NActors { void Schedule(TDuration delta, TAutoPtr<IEventHandle> ev, ISchedulerCookie* cookie = nullptr) const; /** - * A way to interact with actors from non-actor context. - * - * This method will send the `event` to the `recipient` and then will wait for a response. When response arrives, - * it will be passed to the future. If response is not of type `T`, the future will resolve into an exception. - * - * @tparam T expected response type. Must be derived from `TEventBase`, - * or use `IEventBase` to catch any response. - * @param actorSystem actor system that will be used to register an actor that'll wait for response. - * @param recipient who will get a request. - * @param event a request message. - * @return future that will be resolved when a message from `recipient` arrives. - */ - template <typename T> - [[nodiscard]] + * A way to interact with actors from non-actor context. + * + * This method will send the `event` to the `recipient` and then will wait for a response. When response arrives, + * it will be passed to the future. If response is not of type `T`, the future will resolve into an exception. + * + * @tparam T expected response type. Must be derived from `TEventBase`, + * or use `IEventBase` to catch any response. + * @param actorSystem actor system that will be used to register an actor that'll wait for response. + * @param recipient who will get a request. + * @param event a request message. + * @return future that will be resolved when a message from `recipient` arrives. + */ + template <typename T> + [[nodiscard]] NThreading::TFuture<THolder<T>> Ask(TActorId recipient, THolder<IEventBase> event, TDuration timeout = TDuration::Max()) { - if constexpr (std::is_same_v<T, IEventBase>) { - return AskGeneric(Nothing(), recipient, std::move(event), timeout); - } else { - return AskGeneric(T::EventType, recipient, std::move(event), timeout) - .Apply([](const NThreading::TFuture<THolder<IEventBase>>& ev) { - return THolder<T>(static_cast<T*>(const_cast<THolder<IEventBase>&>(ev.GetValueSync()).Release())); // =( - }); - } - } - - [[nodiscard]] - NThreading::TFuture<THolder<IEventBase>> AskGeneric( - TMaybe<ui32> expectedEventType, + if constexpr (std::is_same_v<T, IEventBase>) { + return AskGeneric(Nothing(), recipient, std::move(event), timeout); + } else { + return AskGeneric(T::EventType, recipient, std::move(event), timeout) + .Apply([](const NThreading::TFuture<THolder<IEventBase>>& ev) { + return THolder<T>(static_cast<T*>(const_cast<THolder<IEventBase>&>(ev.GetValueSync()).Release())); // =( + }); + } + } + + [[nodiscard]] + NThreading::TFuture<THolder<IEventBase>> AskGeneric( + TMaybe<ui32> expectedEventType, TActorId recipient, - THolder<IEventBase> event, - TDuration timeout); - + THolder<IEventBase> event, + TDuration timeout); + ui64 AllocateIDSpace(ui64 count); TActorId InterconnectProxy(ui32 destinationNode) const; diff --git a/library/cpp/actors/core/ask.cpp b/library/cpp/actors/core/ask.cpp index 0054c9a906..3b18fa0cb4 100644 --- a/library/cpp/actors/core/ask.cpp +++ b/library/cpp/actors/core/ask.cpp @@ -1,74 +1,74 @@ -#include "ask.h" - -#include "actor_bootstrapped.h" -#include "actorid.h" -#include "event.h" -#include "hfunc.h" - -namespace NActors { - namespace { - class TAskActor: public TActorBootstrapped<TAskActor> { - enum { - Timeout = EventSpaceBegin(TEvents::ES_PRIVATE), - }; - - // We can't use the standard timeout event because recipient may send us one. - struct TTimeout: public TEventLocal<TTimeout, Timeout> { - }; - - public: - TAskActor( - TMaybe<ui32> expectedEventType, +#include "ask.h" + +#include "actor_bootstrapped.h" +#include "actorid.h" +#include "event.h" +#include "hfunc.h" + +namespace NActors { + namespace { + class TAskActor: public TActorBootstrapped<TAskActor> { + enum { + Timeout = EventSpaceBegin(TEvents::ES_PRIVATE), + }; + + // We can't use the standard timeout event because recipient may send us one. + struct TTimeout: public TEventLocal<TTimeout, Timeout> { + }; + + public: + TAskActor( + TMaybe<ui32> expectedEventType, TActorId recipient, - THolder<IEventBase> event, - TDuration timeout, - const NThreading::TPromise<THolder<IEventBase>>& promise) - : ExpectedEventType_(expectedEventType) - , Recipient_(recipient) - , Event_(std::move(event)) - , Timeout_(timeout) - , Promise_(promise) - { - } - - public: - void Bootstrap() { - Send(Recipient_, std::move(Event_)); - Become(&TAskActor::Waiting); - - if (Timeout_ != TDuration::Max()) { - Schedule(Timeout_, new TTimeout); - } - } - - STATEFN(Waiting) { - if (ev->GetTypeRewrite() == TTimeout::EventType) { - Promise_.SetException(std::make_exception_ptr(yexception() << "ask timeout")); - } else if (!ExpectedEventType_ || ev->GetTypeRewrite() == ExpectedEventType_) { - Promise_.SetValue(ev->ReleaseBase()); - } else { - Promise_.SetException(std::make_exception_ptr(yexception() << "received unexpected response " << ev->GetBase()->ToString())); - } - - PassAway(); - } - - public: - TMaybe<ui32> ExpectedEventType_; + THolder<IEventBase> event, + TDuration timeout, + const NThreading::TPromise<THolder<IEventBase>>& promise) + : ExpectedEventType_(expectedEventType) + , Recipient_(recipient) + , Event_(std::move(event)) + , Timeout_(timeout) + , Promise_(promise) + { + } + + public: + void Bootstrap() { + Send(Recipient_, std::move(Event_)); + Become(&TAskActor::Waiting); + + if (Timeout_ != TDuration::Max()) { + Schedule(Timeout_, new TTimeout); + } + } + + STATEFN(Waiting) { + if (ev->GetTypeRewrite() == TTimeout::EventType) { + Promise_.SetException(std::make_exception_ptr(yexception() << "ask timeout")); + } else if (!ExpectedEventType_ || ev->GetTypeRewrite() == ExpectedEventType_) { + Promise_.SetValue(ev->ReleaseBase()); + } else { + Promise_.SetException(std::make_exception_ptr(yexception() << "received unexpected response " << ev->GetBase()->ToString())); + } + + PassAway(); + } + + public: + TMaybe<ui32> ExpectedEventType_; TActorId Recipient_; - THolder<IEventBase> Event_; - TDuration Timeout_; - NThreading::TPromise<THolder<IEventBase>> Promise_; - }; - } - - THolder<IActor> MakeAskActor( - TMaybe<ui32> expectedEventType, + THolder<IEventBase> Event_; + TDuration Timeout_; + NThreading::TPromise<THolder<IEventBase>> Promise_; + }; + } + + THolder<IActor> MakeAskActor( + TMaybe<ui32> expectedEventType, TActorId recipient, - THolder<IEventBase> event, - TDuration timeout, - const NThreading::TPromise<THolder<IEventBase>>& promise) - { + THolder<IEventBase> event, + TDuration timeout, + const NThreading::TPromise<THolder<IEventBase>>& promise) + { return MakeHolder<TAskActor>(expectedEventType, std::move(recipient), std::move(event), timeout, promise); - } -} + } +} diff --git a/library/cpp/actors/core/ask.h b/library/cpp/actors/core/ask.h index 036f1833a4..eccd21b530 100644 --- a/library/cpp/actors/core/ask.h +++ b/library/cpp/actors/core/ask.h @@ -1,18 +1,18 @@ -#pragma once - -#include "actor.h" -#include "event.h" - +#pragma once + +#include "actor.h" +#include "event.h" + #include <library/cpp/threading/future/future.h> - -namespace NActors { - /** - * See `TActorSystem::Ask`. - */ - THolder<IActor> MakeAskActor( - TMaybe<ui32> expectedEventType, + +namespace NActors { + /** + * See `TActorSystem::Ask`. + */ + THolder<IActor> MakeAskActor( + TMaybe<ui32> expectedEventType, TActorId recipient, - THolder<IEventBase> event, - TDuration timeout, - const NThreading::TPromise<THolder<IEventBase>>& promise); -} + THolder<IEventBase> event, + TDuration timeout, + const NThreading::TPromise<THolder<IEventBase>>& promise); +} diff --git a/library/cpp/actors/core/ask_ut.cpp b/library/cpp/actors/core/ask_ut.cpp index e72ebdba9b..4b53520cee 100644 --- a/library/cpp/actors/core/ask_ut.cpp +++ b/library/cpp/actors/core/ask_ut.cpp @@ -1,131 +1,131 @@ #include <library/cpp/testing/unittest/registar.h> - -#include "actorsystem.h" - -#include <library/cpp/actors/testlib/test_runtime.h> - -using namespace NActors; - -class TPingPong: public TActor<TPingPong> { -public: - TPingPong() - : TActor(&TPingPong::Main) - { - } - - STATEFN(Main) { - switch (ev->GetTypeRewrite()) { - hFunc(TEvents::TEvPing, OnPing); - hFunc(TEvents::TEvBlob, OnBlob); - } - } - - void OnPing(const TEvents::TEvPing::TPtr& ev) { - Send(ev->Sender, new TEvents::TEvPong); - } - - void OnBlob(const TEvents::TEvBlob::TPtr& ev) { - Send(ev->Sender, ev->Release().Release()); - } -}; - -class TPing: public TActor<TPing> { -public: - TPing() - : TActor(&TPing::Main) - { - } - - STATEFN(Main) { - Y_UNUSED(ev); - } -}; - -THolder<TTestActorRuntimeBase> CreateRuntime() { - auto runtime = MakeHolder<TTestActorRuntimeBase>(); - runtime->SetScheduledEventFilter([](auto&&, auto&&, auto&&, auto&&) { return false; }); - runtime->Initialize(); - return runtime; -} - -Y_UNIT_TEST_SUITE(AskActor) { - Y_UNIT_TEST(Ok) { - auto runtime = CreateRuntime(); - auto pingpong = runtime->Register(new TPingPong); - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvPong>( - pingpong, + +#include "actorsystem.h" + +#include <library/cpp/actors/testlib/test_runtime.h> + +using namespace NActors; + +class TPingPong: public TActor<TPingPong> { +public: + TPingPong() + : TActor(&TPingPong::Main) + { + } + + STATEFN(Main) { + switch (ev->GetTypeRewrite()) { + hFunc(TEvents::TEvPing, OnPing); + hFunc(TEvents::TEvBlob, OnBlob); + } + } + + void OnPing(const TEvents::TEvPing::TPtr& ev) { + Send(ev->Sender, new TEvents::TEvPong); + } + + void OnBlob(const TEvents::TEvBlob::TPtr& ev) { + Send(ev->Sender, ev->Release().Release()); + } +}; + +class TPing: public TActor<TPing> { +public: + TPing() + : TActor(&TPing::Main) + { + } + + STATEFN(Main) { + Y_UNUSED(ev); + } +}; + +THolder<TTestActorRuntimeBase> CreateRuntime() { + auto runtime = MakeHolder<TTestActorRuntimeBase>(); + runtime->SetScheduledEventFilter([](auto&&, auto&&, auto&&, auto&&) { return false; }); + runtime->Initialize(); + return runtime; +} + +Y_UNIT_TEST_SUITE(AskActor) { + Y_UNIT_TEST(Ok) { + auto runtime = CreateRuntime(); + auto pingpong = runtime->Register(new TPingPong); + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvPong>( + pingpong, THolder(new TEvents::TEvPing)); - runtime->DispatchEvents(); - fut.ExtractValueSync(); - } - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvBlob>( - pingpong, + runtime->DispatchEvents(); + fut.ExtractValueSync(); + } + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvBlob>( + pingpong, THolder(new TEvents::TEvBlob("hello!"))); - runtime->DispatchEvents(); - auto ev = fut.ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(ev->Blob, "hello!"); - } - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<IEventBase>( - pingpong, + runtime->DispatchEvents(); + auto ev = fut.ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(ev->Blob, "hello!"); + } + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<IEventBase>( + pingpong, THolder(new TEvents::TEvPing)); - runtime->DispatchEvents(); - auto ev = fut.ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(ev->Type(), TEvents::TEvPong::EventType); - } - } - - Y_UNIT_TEST(Err) { - auto runtime = CreateRuntime(); - auto pingpong = runtime->Register(new TPingPong); - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvBlob>( - pingpong, + runtime->DispatchEvents(); + auto ev = fut.ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(ev->Type(), TEvents::TEvPong::EventType); + } + } + + Y_UNIT_TEST(Err) { + auto runtime = CreateRuntime(); + auto pingpong = runtime->Register(new TPingPong); + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvBlob>( + pingpong, THolder(new TEvents::TEvPing)); - runtime->DispatchEvents(); - UNIT_ASSERT_EXCEPTION_CONTAINS( - fut.ExtractValueSync(), - yexception, - "received unexpected response HelloWorld: Pong"); - } - } - - Y_UNIT_TEST(Timeout) { - auto runtime = CreateRuntime(); - auto ping = runtime->Register(new TPing); - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvPong>( - ping, + runtime->DispatchEvents(); + UNIT_ASSERT_EXCEPTION_CONTAINS( + fut.ExtractValueSync(), + yexception, + "received unexpected response HelloWorld: Pong"); + } + } + + Y_UNIT_TEST(Timeout) { + auto runtime = CreateRuntime(); + auto ping = runtime->Register(new TPing); + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<TEvents::TEvPong>( + ping, THolder(new TEvents::TEvPing), - TDuration::Seconds(1)); - auto start = runtime->GetCurrentTime(); - runtime->DispatchEvents({}, TDuration::Seconds(5)); - UNIT_ASSERT_EXCEPTION_CONTAINS( - fut.ExtractValueSync(), - yexception, - "ask timeout"); - UNIT_ASSERT_VALUES_EQUAL(runtime->GetCurrentTime() - start, TDuration::Seconds(1)); - } - - { - auto fut = runtime->GetAnyNodeActorSystem()->Ask<IEventBase>( - ping, + TDuration::Seconds(1)); + auto start = runtime->GetCurrentTime(); + runtime->DispatchEvents({}, TDuration::Seconds(5)); + UNIT_ASSERT_EXCEPTION_CONTAINS( + fut.ExtractValueSync(), + yexception, + "ask timeout"); + UNIT_ASSERT_VALUES_EQUAL(runtime->GetCurrentTime() - start, TDuration::Seconds(1)); + } + + { + auto fut = runtime->GetAnyNodeActorSystem()->Ask<IEventBase>( + ping, THolder(new TEvents::TEvPing), - TDuration::Seconds(1)); - auto start = runtime->GetCurrentTime(); - runtime->DispatchEvents({}, TDuration::Seconds(5)); - UNIT_ASSERT_EXCEPTION_CONTAINS( - fut.ExtractValueSync(), - yexception, - "ask timeout"); - UNIT_ASSERT_VALUES_EQUAL(runtime->GetCurrentTime() - start, TDuration::Seconds(1)); - } - } -} + TDuration::Seconds(1)); + auto start = runtime->GetCurrentTime(); + runtime->DispatchEvents({}, TDuration::Seconds(5)); + UNIT_ASSERT_EXCEPTION_CONTAINS( + fut.ExtractValueSync(), + yexception, + "ask timeout"); + UNIT_ASSERT_VALUES_EQUAL(runtime->GetCurrentTime() - start, TDuration::Seconds(1)); + } + } +} diff --git a/library/cpp/actors/core/hfunc.h b/library/cpp/actors/core/hfunc.h index 26f3c65013..d064e4978c 100644 --- a/library/cpp/actors/core/hfunc.h +++ b/library/cpp/actors/core/hfunc.h @@ -42,23 +42,23 @@ break; \ } -#define hTemplFunc(TEvType, HandleFunc) \ - case TEvType::EventType: { \ - typename TEvType::TPtr* x = reinterpret_cast<typename TEvType::TPtr*>(&ev); \ - HandleFunc(*x); \ - break; \ - } - -#define SFunc(TEvType, HandleFunc) \ - case TEvType::EventType: \ - HandleFunc(ctx); \ - break; - -#define sFunc(TEvType, HandleFunc) \ - case TEvType::EventType: \ - HandleFunc(); \ - break; - +#define hTemplFunc(TEvType, HandleFunc) \ + case TEvType::EventType: { \ + typename TEvType::TPtr* x = reinterpret_cast<typename TEvType::TPtr*>(&ev); \ + HandleFunc(*x); \ + break; \ + } + +#define SFunc(TEvType, HandleFunc) \ + case TEvType::EventType: \ + HandleFunc(ctx); \ + break; + +#define sFunc(TEvType, HandleFunc) \ + case TEvType::EventType: \ + HandleFunc(); \ + break; + #define CFunc(TEventType, HandleFunc) \ case TEventType: \ HandleFunc(ctx); \ diff --git a/library/cpp/actors/core/ut/ya.make b/library/cpp/actors/core/ut/ya.make index 3ee28d5850..d0c98046f0 100644 --- a/library/cpp/actors/core/ut/ya.make +++ b/library/cpp/actors/core/ut/ya.make @@ -32,7 +32,7 @@ SRCS( actor_coroutine_ut.cpp actor_ut.cpp actorsystem_ut.cpp - ask_ut.cpp + ask_ut.cpp balancer_ut.cpp event_pb_payload_ut.cpp event_pb_ut.cpp diff --git a/library/cpp/actors/core/ya.make b/library/cpp/actors/core/ya.make index 880a9d00db..1568f5bf0c 100644 --- a/library/cpp/actors/core/ya.make +++ b/library/cpp/actors/core/ya.make @@ -28,8 +28,8 @@ SRCS( actorid.h actorsystem.cpp actorsystem.h - ask.cpp - ask.h + ask.cpp + ask.h balancer.h balancer.cpp buffer.cpp diff --git a/library/cpp/colorizer/colors.cpp b/library/cpp/colorizer/colors.cpp index decc5c9847..0b5f225f4f 100644 --- a/library/cpp/colorizer/colors.cpp +++ b/library/cpp/colorizer/colors.cpp @@ -12,160 +12,160 @@ using namespace NColorizer; -namespace { - constexpr TStringBuf ToStringBufC(NColorizer::EAnsiCode x) { - switch(x) { - case RESET: +namespace { + constexpr TStringBuf ToStringBufC(NColorizer::EAnsiCode x) { + switch(x) { + case RESET: return "\033[0m"; - - case ST_LIGHT: + + case ST_LIGHT: return "\033[1m"; - case ST_DARK: + case ST_DARK: return "\033[2m"; - case ST_NORMAL: + case ST_NORMAL: return "\033[22m"; - - case ITALIC_ON: + + case ITALIC_ON: return "\033[3m"; - case ITALIC_OFF: + case ITALIC_OFF: return "\033[23m"; - case UNDERLINE_ON: + case UNDERLINE_ON: return "\033[4m"; - case UNDERLINE_OFF: + case UNDERLINE_OFF: return "\033[24m"; - - case FG_DEFAULT: + + case FG_DEFAULT: return "\033[39m"; - case FG_BLACK: + case FG_BLACK: return "\033[30m"; - case FG_RED: + case FG_RED: return "\033[31m"; - case FG_GREEN: + case FG_GREEN: return "\033[32m"; - case FG_YELLOW: + case FG_YELLOW: return "\033[33m"; - case FG_BLUE: + case FG_BLUE: return "\033[34m"; - case FG_MAGENTA: + case FG_MAGENTA: return "\033[35m"; - case FG_CYAN: + case FG_CYAN: return "\033[36m"; - case FG_WHITE: + case FG_WHITE: return "\033[37m"; - - case BG_DEFAULT: + + case BG_DEFAULT: return "\033[49m"; - case BG_BLACK: + case BG_BLACK: return "\033[40m"; - case BG_RED: + case BG_RED: return "\033[41m"; - case BG_GREEN: + case BG_GREEN: return "\033[42m"; - case BG_YELLOW: + case BG_YELLOW: return "\033[43m"; - case BG_BLUE: + case BG_BLUE: return "\033[44m"; - case BG_MAGENTA: + case BG_MAGENTA: return "\033[45m"; - case BG_CYAN: + case BG_CYAN: return "\033[46m"; - case BG_WHITE: + case BG_WHITE: return "\033[47m"; - - // Note: the following codes are split into two escabe sequences because of how ya.make handles them. - - case DEFAULT: + + // Note: the following codes are split into two escabe sequences because of how ya.make handles them. + + case DEFAULT: return "\033[0m\033[0;39m"; - case BLACK: + case BLACK: return "\033[0m\033[0;30m"; - case RED: + case RED: return "\033[0m\033[0;31m"; - case GREEN: + case GREEN: return "\033[0m\033[0;32m"; - case YELLOW: + case YELLOW: return "\033[0m\033[0;33m"; - case BLUE: + case BLUE: return "\033[0m\033[0;34m"; - case MAGENTA: + case MAGENTA: return "\033[0m\033[0;35m"; - case CYAN: + case CYAN: return "\033[0m\033[0;36m"; - case WHITE: + case WHITE: return "\033[0m\033[0;37m"; - - case LIGHT_DEFAULT: + + case LIGHT_DEFAULT: return "\033[0m\033[1;39m"; - case LIGHT_BLACK: + case LIGHT_BLACK: return "\033[0m\033[1;30m"; - case LIGHT_RED: + case LIGHT_RED: return "\033[0m\033[1;31m"; - case LIGHT_GREEN: + case LIGHT_GREEN: return "\033[0m\033[1;32m"; - case LIGHT_YELLOW: + case LIGHT_YELLOW: return "\033[0m\033[1;33m"; - case LIGHT_BLUE: + case LIGHT_BLUE: return "\033[0m\033[1;34m"; - case LIGHT_MAGENTA: + case LIGHT_MAGENTA: return "\033[0m\033[1;35m"; - case LIGHT_CYAN: + case LIGHT_CYAN: return "\033[0m\033[1;36m"; - case LIGHT_WHITE: + case LIGHT_WHITE: return "\033[0m\033[1;37m"; - - case DARK_DEFAULT: + + case DARK_DEFAULT: return "\033[0m\033[2;39m"; - case DARK_BLACK: + case DARK_BLACK: return "\033[0m\033[2;30m"; - case DARK_RED: + case DARK_RED: return "\033[0m\033[2;31m"; - case DARK_GREEN: + case DARK_GREEN: return "\033[0m\033[2;32m"; - case DARK_YELLOW: + case DARK_YELLOW: return "\033[0m\033[2;33m"; - case DARK_BLUE: + case DARK_BLUE: return "\033[0m\033[2;34m"; - case DARK_MAGENTA: + case DARK_MAGENTA: return "\033[0m\033[2;35m"; - case DARK_CYAN: + case DARK_CYAN: return "\033[0m\033[2;36m"; - case DARK_WHITE: + case DARK_WHITE: return "\033[0m\033[2;37m"; case INVALID: default: return ""; - } - } -} - -TStringBuf ToStringBuf(NColorizer::EAnsiCode x) { - return ToStringBufC(x); -} - -TString ToString(NColorizer::EAnsiCode x) { - return TString(ToStringBufC(x)); -} - -template<> -void Out<NColorizer::EAnsiCode>(IOutputStream& os, TTypeTraits<NColorizer::EAnsiCode>::TFuncParam x) { - if (AutoColors(os).IsTTY()) { - os << ToStringBufC(x); - } -} - -bool TColors::CalcIsTTY(FILE* file) { - if (GetEnv("ENFORCE_TTY")) { - return true; - } - -#if defined(_unix_) - return isatty(fileno(file)); -#else - Y_UNUSED(file); - return false; -#endif -} - + } + } +} + +TStringBuf ToStringBuf(NColorizer::EAnsiCode x) { + return ToStringBufC(x); +} + +TString ToString(NColorizer::EAnsiCode x) { + return TString(ToStringBufC(x)); +} + +template<> +void Out<NColorizer::EAnsiCode>(IOutputStream& os, TTypeTraits<NColorizer::EAnsiCode>::TFuncParam x) { + if (AutoColors(os).IsTTY()) { + os << ToStringBufC(x); + } +} + +bool TColors::CalcIsTTY(FILE* file) { + if (GetEnv("ENFORCE_TTY")) { + return true; + } + +#if defined(_unix_) + return isatty(fileno(file)); +#else + Y_UNUSED(file); + return false; +#endif +} + TColors::TColors(FILE* f) : IsTTY_(true) { @@ -178,254 +178,254 @@ TColors::TColors(bool ontty) SetIsTTY(ontty); } -TStringBuf TColors::Reset() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::RESET) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::StyleLight() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::ST_LIGHT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::StyleDark() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::ST_DARK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::StyleNormal() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::ST_NORMAL) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::ItalicOn() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::ITALIC_ON) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ItalicOff() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::ITALIC_OFF) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::UnderlineOn() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::UNDERLINE_ON) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::UnderlineOff() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::UNDERLINE_OFF) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::ForeDefault() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeBlack() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_BLACK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeRed() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_RED) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeGreen() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_GREEN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeYellow() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_YELLOW) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeBlue() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_BLUE) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeMagenta() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeCyan() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_CYAN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::ForeWhite() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::FG_WHITE) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::BackDefault() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackBlack() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_BLACK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackRed() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_RED) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackGreen() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_GREEN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackYellow() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_YELLOW) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackBlue() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_BLUE) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackMagenta() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackCyan() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_CYAN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::BackWhite() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BG_WHITE) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::Default() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DEFAULT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Black() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BLACK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Red() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::RED) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Green() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::GREEN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Yellow() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::YELLOW) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Blue() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::BLUE) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Magenta() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::MAGENTA) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::Cyan() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::CYAN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::White() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::WHITE) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::LightDefault() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightBlack() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_BLACK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightRed() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_RED) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightGreen() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_GREEN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightYellow() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_YELLOW) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightBlue() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_BLUE) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightMagenta() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightCyan() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_CYAN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::LightWhite() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_WHITE) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::DarkDefault() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkBlack() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_BLACK) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkRed() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_RED) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkGreen() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_GREEN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkYellow() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_YELLOW) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkBlue() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_BLUE) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkMagenta() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkCyan() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_CYAN) : ToStringBufC(EAnsiCode::INVALID); -} -TStringBuf TColors::DarkWhite() const noexcept { - return IsTTY() ? ToStringBufC(EAnsiCode::DARK_WHITE) : ToStringBufC(EAnsiCode::INVALID); -} - -TStringBuf TColors::OldColor() const noexcept { +TStringBuf TColors::Reset() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::RESET) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::StyleLight() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::ST_LIGHT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::StyleDark() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::ST_DARK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::StyleNormal() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::ST_NORMAL) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::ItalicOn() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::ITALIC_ON) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ItalicOff() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::ITALIC_OFF) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::UnderlineOn() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::UNDERLINE_ON) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::UnderlineOff() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::UNDERLINE_OFF) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::ForeDefault() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeBlack() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_BLACK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeRed() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_RED) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeGreen() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_GREEN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeYellow() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_YELLOW) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeBlue() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_BLUE) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeMagenta() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeCyan() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_CYAN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::ForeWhite() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::FG_WHITE) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::BackDefault() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackBlack() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_BLACK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackRed() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_RED) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackGreen() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_GREEN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackYellow() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_YELLOW) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackBlue() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_BLUE) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackMagenta() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackCyan() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_CYAN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::BackWhite() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BG_WHITE) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::Default() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DEFAULT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Black() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BLACK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Red() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::RED) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Green() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::GREEN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Yellow() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::YELLOW) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Blue() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::BLUE) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Magenta() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::MAGENTA) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::Cyan() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::CYAN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::White() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::WHITE) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::LightDefault() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightBlack() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_BLACK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightRed() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_RED) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightGreen() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_GREEN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightYellow() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_YELLOW) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightBlue() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_BLUE) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightMagenta() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightCyan() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_CYAN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::LightWhite() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::LIGHT_WHITE) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::DarkDefault() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_DEFAULT) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkBlack() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_BLACK) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkRed() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_RED) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkGreen() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_GREEN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkYellow() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_YELLOW) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkBlue() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_BLUE) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkMagenta() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_MAGENTA) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkCyan() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_CYAN) : ToStringBufC(EAnsiCode::INVALID); +} +TStringBuf TColors::DarkWhite() const noexcept { + return IsTTY() ? ToStringBufC(EAnsiCode::DARK_WHITE) : ToStringBufC(EAnsiCode::INVALID); +} + +TStringBuf TColors::OldColor() const noexcept { return IsTTY() ? "\033[22;39m" : ""; } -TStringBuf TColors::BoldColor() const noexcept { +TStringBuf TColors::BoldColor() const noexcept { return IsTTY() ? "\033[1m" : ""; } -TStringBuf TColors::BlackColor() const noexcept { +TStringBuf TColors::BlackColor() const noexcept { return IsTTY() ? "\033[22;30m" : ""; } -TStringBuf TColors::BlueColor() const noexcept { +TStringBuf TColors::BlueColor() const noexcept { return IsTTY() ? "\033[22;34m" : ""; } -TStringBuf TColors::GreenColor() const noexcept { +TStringBuf TColors::GreenColor() const noexcept { return IsTTY() ? "\033[22;32m" : ""; } -TStringBuf TColors::CyanColor() const noexcept { +TStringBuf TColors::CyanColor() const noexcept { return IsTTY() ? "\033[22;36m" : ""; } -TStringBuf TColors::RedColor() const noexcept { +TStringBuf TColors::RedColor() const noexcept { return IsTTY() ? "\033[22;31m" : ""; } -TStringBuf TColors::PurpleColor() const noexcept { +TStringBuf TColors::PurpleColor() const noexcept { return IsTTY() ? "\033[22;35m" : ""; } -TStringBuf TColors::BrownColor() const noexcept { +TStringBuf TColors::BrownColor() const noexcept { return IsTTY() ? "\033[22;33m" : ""; } -TStringBuf TColors::LightGrayColor() const noexcept { +TStringBuf TColors::LightGrayColor() const noexcept { return IsTTY() ? "\033[22;37m" : ""; } -TStringBuf TColors::DarkGrayColor() const noexcept { +TStringBuf TColors::DarkGrayColor() const noexcept { return IsTTY() ? "\033[1;30m" : ""; -} +} -TStringBuf TColors::LightBlueColor() const noexcept { +TStringBuf TColors::LightBlueColor() const noexcept { return IsTTY() ? "\033[1;34m" : ""; -} +} -TStringBuf TColors::LightGreenColor() const noexcept { +TStringBuf TColors::LightGreenColor() const noexcept { return IsTTY() ? "\033[1;32m" : ""; -} +} -TStringBuf TColors::LightCyanColor() const noexcept { +TStringBuf TColors::LightCyanColor() const noexcept { return IsTTY() ? "\033[1;36m" : ""; } -TStringBuf TColors::LightRedColor() const noexcept { +TStringBuf TColors::LightRedColor() const noexcept { return IsTTY() ? "\033[1;31m" : ""; -} +} -TStringBuf TColors::LightPurpleColor() const noexcept { +TStringBuf TColors::LightPurpleColor() const noexcept { return IsTTY() ? "\033[1;35m" : ""; } -TStringBuf TColors::YellowColor() const noexcept { +TStringBuf TColors::YellowColor() const noexcept { return IsTTY() ? "\033[1;33m" : ""; } -TStringBuf TColors::WhiteColor() const noexcept { +TStringBuf TColors::WhiteColor() const noexcept { return IsTTY() ? "\033[1;37m" : ""; } - + namespace { - class TStdErrColors: public TColors { - public: - TStdErrColors() - : TColors(stderr) - { - } - }; + class TStdErrColors: public TColors { + public: + TStdErrColors() + : TColors(stderr) + { + } + }; class TStdOutColors: public TColors { public: @@ -444,14 +444,14 @@ namespace { }; } // anonymous namespace -TColors& NColorizer::StdErr() { - return *Singleton<TStdErrColors>(); -} - -TColors& NColorizer::StdOut() { - return *Singleton<TStdOutColors>(); -} - +TColors& NColorizer::StdErr() { + return *Singleton<TStdErrColors>(); +} + +TColors& NColorizer::StdOut() { + return *Singleton<TStdOutColors>(); +} + TColors& NColorizer::AutoColors(IOutputStream& os) { if (&os == &Cerr) { return StdErr(); @@ -461,45 +461,45 @@ TColors& NColorizer::AutoColors(IOutputStream& os) { } return *Singleton<TDisabledColors>(); } - -size_t NColorizer::TotalAnsiEscapeCodeLen(TStringBuf text) { - enum { - TEXT, - BEFORE_CODE, - IN_CODE, - } state = TEXT; - - size_t totalLen = 0; - size_t curLen = 0; - - for (auto it = text.begin(); it < text.end(); ++it) { - switch (state) { - case TEXT: - if (*it == '\033') { - state = BEFORE_CODE; - curLen = 1; - } - break; - case BEFORE_CODE: - if (*it == '[') { - state = IN_CODE; - curLen++; - } else { - state = TEXT; - } - break; - case IN_CODE: - if (*it == ';' || isdigit(*it)) { - curLen++; - } else { - if (*it == 'm') { - totalLen += curLen + 1; - } - state = TEXT; - } - break; - } - } - - return totalLen; -} + +size_t NColorizer::TotalAnsiEscapeCodeLen(TStringBuf text) { + enum { + TEXT, + BEFORE_CODE, + IN_CODE, + } state = TEXT; + + size_t totalLen = 0; + size_t curLen = 0; + + for (auto it = text.begin(); it < text.end(); ++it) { + switch (state) { + case TEXT: + if (*it == '\033') { + state = BEFORE_CODE; + curLen = 1; + } + break; + case BEFORE_CODE: + if (*it == '[') { + state = IN_CODE; + curLen++; + } else { + state = TEXT; + } + break; + case IN_CODE: + if (*it == ';' || isdigit(*it)) { + curLen++; + } else { + if (*it == 'm') { + totalLen += curLen + 1; + } + state = TEXT; + } + break; + } + } + + return totalLen; +} diff --git a/library/cpp/colorizer/colors.h b/library/cpp/colorizer/colors.h index 474a918994..1ac3d689f6 100644 --- a/library/cpp/colorizer/colors.h +++ b/library/cpp/colorizer/colors.h @@ -3,195 +3,195 @@ #include "fwd.h" #include <util/generic/string.h> -#include <util/generic/strbuf.h> +#include <util/generic/strbuf.h> #include <cstdio> - + namespace NColorizer { - /** - * List of ECMA-48 colors. - * - * When printing elements of this enum via `operator<<`, `AutoColors()` (see below) function will be used - * to produce colors, i.e. nothing will be printed to non-tty streams. When converting elements of this enum - * via `ToString`, escape code is always returned. - * - * Note: as of now (2019-03), `ya make` strips out some escape codes from compiler output. - * It also inserts `RESET` before each color code. See https://st.yandex-team.ru/DEVTOOLS-5269 for details. - * For now, do not use `OLD`, `ST_*`, `FG_*` and `BG_*` in tools that run through `ya make`. - * - * Note: refrain from using black colors because there's high chance they'll not be visible on some terminals. - * Default windows and ubuntu color schemes shows them as black letters on black background. - * Also, white colors are barely visible in default OSX color scheme. Light black is usually fine though. - */ - enum EAnsiCode: i8 { - // Note: not using `GENERATE_ENUM_SERIALIZATION` because serialization generator depends on this library. - - /// Does not change anything. - INVALID, - - /// Reset all styles and colors. Safe to use in `ya make` tools. - RESET, - - /// Change style, don't change anything else. - ST_LIGHT, - ST_DARK, - ST_NORMAL, - - /// Additional styles. - ITALIC_ON, - ITALIC_OFF, - UNDERLINE_ON, - UNDERLINE_OFF, - - /// Change foreground color, don't change anything else. - FG_DEFAULT, - FG_BLACK, - FG_RED, - FG_GREEN, - FG_YELLOW, - FG_BLUE, - FG_MAGENTA, - FG_CYAN, - FG_WHITE, - - /// Change background color, don't change anything else. - BG_DEFAULT, - BG_BLACK, - BG_RED, - BG_GREEN, - BG_YELLOW, - BG_BLUE, - BG_MAGENTA, - BG_CYAN, - BG_WHITE, - - /// Reset all styles and colors, then enable a (possibly light or dark) color. Safe to use in `ya make` tools. - DEFAULT, - BLACK, - RED, - GREEN, - YELLOW, - BLUE, - MAGENTA, - CYAN, - WHITE, - LIGHT_DEFAULT, - LIGHT_BLACK, - LIGHT_RED, - LIGHT_GREEN, - LIGHT_YELLOW, - LIGHT_BLUE, - LIGHT_MAGENTA, - LIGHT_CYAN, - LIGHT_WHITE, - DARK_DEFAULT, - DARK_BLACK, - DARK_RED, - DARK_GREEN, - DARK_YELLOW, - DARK_BLUE, - DARK_MAGENTA, - DARK_CYAN, - DARK_WHITE, - }; - - /** - * Produces escape codes or empty stringbuf depending on settings. - * All color functions return zero-terminated stringbuf. - */ - class TColors { - public: - static bool CalcIsTTY(FILE* file); - + /** + * List of ECMA-48 colors. + * + * When printing elements of this enum via `operator<<`, `AutoColors()` (see below) function will be used + * to produce colors, i.e. nothing will be printed to non-tty streams. When converting elements of this enum + * via `ToString`, escape code is always returned. + * + * Note: as of now (2019-03), `ya make` strips out some escape codes from compiler output. + * It also inserts `RESET` before each color code. See https://st.yandex-team.ru/DEVTOOLS-5269 for details. + * For now, do not use `OLD`, `ST_*`, `FG_*` and `BG_*` in tools that run through `ya make`. + * + * Note: refrain from using black colors because there's high chance they'll not be visible on some terminals. + * Default windows and ubuntu color schemes shows them as black letters on black background. + * Also, white colors are barely visible in default OSX color scheme. Light black is usually fine though. + */ + enum EAnsiCode: i8 { + // Note: not using `GENERATE_ENUM_SERIALIZATION` because serialization generator depends on this library. + + /// Does not change anything. + INVALID, + + /// Reset all styles and colors. Safe to use in `ya make` tools. + RESET, + + /// Change style, don't change anything else. + ST_LIGHT, + ST_DARK, + ST_NORMAL, + + /// Additional styles. + ITALIC_ON, + ITALIC_OFF, + UNDERLINE_ON, + UNDERLINE_OFF, + + /// Change foreground color, don't change anything else. + FG_DEFAULT, + FG_BLACK, + FG_RED, + FG_GREEN, + FG_YELLOW, + FG_BLUE, + FG_MAGENTA, + FG_CYAN, + FG_WHITE, + + /// Change background color, don't change anything else. + BG_DEFAULT, + BG_BLACK, + BG_RED, + BG_GREEN, + BG_YELLOW, + BG_BLUE, + BG_MAGENTA, + BG_CYAN, + BG_WHITE, + + /// Reset all styles and colors, then enable a (possibly light or dark) color. Safe to use in `ya make` tools. + DEFAULT, + BLACK, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + WHITE, + LIGHT_DEFAULT, + LIGHT_BLACK, + LIGHT_RED, + LIGHT_GREEN, + LIGHT_YELLOW, + LIGHT_BLUE, + LIGHT_MAGENTA, + LIGHT_CYAN, + LIGHT_WHITE, + DARK_DEFAULT, + DARK_BLACK, + DARK_RED, + DARK_GREEN, + DARK_YELLOW, + DARK_BLUE, + DARK_MAGENTA, + DARK_CYAN, + DARK_WHITE, + }; + + /** + * Produces escape codes or empty stringbuf depending on settings. + * All color functions return zero-terminated stringbuf. + */ + class TColors { public: + static bool CalcIsTTY(FILE* file); + + public: explicit TColors(FILE* = stderr); explicit TColors(bool ontty); - TStringBuf Reset() const noexcept; - - TStringBuf StyleLight() const noexcept; - TStringBuf StyleDark() const noexcept; - TStringBuf StyleNormal() const noexcept; - - TStringBuf ItalicOn() const noexcept; - TStringBuf ItalicOff() const noexcept; - TStringBuf UnderlineOn() const noexcept; - TStringBuf UnderlineOff() const noexcept; - - TStringBuf ForeDefault() const noexcept; - TStringBuf ForeBlack() const noexcept; - TStringBuf ForeRed() const noexcept; - TStringBuf ForeGreen() const noexcept; - TStringBuf ForeYellow() const noexcept; - TStringBuf ForeBlue() const noexcept; - TStringBuf ForeMagenta() const noexcept; - TStringBuf ForeCyan() const noexcept; - TStringBuf ForeWhite() const noexcept; - - TStringBuf BackDefault() const noexcept; - TStringBuf BackBlack() const noexcept; - TStringBuf BackRed() const noexcept; - TStringBuf BackGreen() const noexcept; - TStringBuf BackYellow() const noexcept; - TStringBuf BackBlue() const noexcept; - TStringBuf BackMagenta() const noexcept; - TStringBuf BackCyan() const noexcept; - TStringBuf BackWhite() const noexcept; - - TStringBuf Default() const noexcept; - TStringBuf Black() const noexcept; - TStringBuf Red() const noexcept; - TStringBuf Green() const noexcept; - TStringBuf Yellow() const noexcept; - TStringBuf Blue() const noexcept; - TStringBuf Magenta() const noexcept; - TStringBuf Cyan() const noexcept; - TStringBuf White() const noexcept; - - TStringBuf LightDefault() const noexcept; - TStringBuf LightBlack() const noexcept; - TStringBuf LightRed() const noexcept; - TStringBuf LightGreen() const noexcept; - TStringBuf LightYellow() const noexcept; - TStringBuf LightBlue() const noexcept; - TStringBuf LightMagenta() const noexcept; - TStringBuf LightCyan() const noexcept; - TStringBuf LightWhite() const noexcept; - - TStringBuf DarkDefault() const noexcept; - TStringBuf DarkBlack() const noexcept; - TStringBuf DarkRed() const noexcept; - TStringBuf DarkGreen() const noexcept; - TStringBuf DarkYellow() const noexcept; - TStringBuf DarkBlue() const noexcept; - TStringBuf DarkMagenta() const noexcept; - TStringBuf DarkCyan() const noexcept; - TStringBuf DarkWhite() const noexcept; - - /// Compatibility; prefer using methods without `Color` suffix in their names. - /// Note: these behave differently from their un-suffixed counterparts. - /// While functions declared above will reset colors completely, these will only reset foreground color and - /// style, without changing the background color and underline/italic settings. Also, names of these functions - /// don't conform with standard, e.g. `YellowColor` actually emits the `lite yellow` escape code. - TStringBuf OldColor() const noexcept; - TStringBuf BoldColor() const noexcept; - TStringBuf BlackColor() const noexcept; - TStringBuf BlueColor() const noexcept; - TStringBuf GreenColor() const noexcept; - TStringBuf CyanColor() const noexcept; - TStringBuf RedColor() const noexcept; - TStringBuf PurpleColor() const noexcept; - TStringBuf BrownColor() const noexcept; - TStringBuf LightGrayColor() const noexcept; - TStringBuf DarkGrayColor() const noexcept; - TStringBuf LightBlueColor() const noexcept; - TStringBuf LightGreenColor() const noexcept; - TStringBuf LightCyanColor() const noexcept; - TStringBuf LightRedColor() const noexcept; - TStringBuf LightPurpleColor() const noexcept; - TStringBuf YellowColor() const noexcept; - TStringBuf WhiteColor() const noexcept; - + TStringBuf Reset() const noexcept; + + TStringBuf StyleLight() const noexcept; + TStringBuf StyleDark() const noexcept; + TStringBuf StyleNormal() const noexcept; + + TStringBuf ItalicOn() const noexcept; + TStringBuf ItalicOff() const noexcept; + TStringBuf UnderlineOn() const noexcept; + TStringBuf UnderlineOff() const noexcept; + + TStringBuf ForeDefault() const noexcept; + TStringBuf ForeBlack() const noexcept; + TStringBuf ForeRed() const noexcept; + TStringBuf ForeGreen() const noexcept; + TStringBuf ForeYellow() const noexcept; + TStringBuf ForeBlue() const noexcept; + TStringBuf ForeMagenta() const noexcept; + TStringBuf ForeCyan() const noexcept; + TStringBuf ForeWhite() const noexcept; + + TStringBuf BackDefault() const noexcept; + TStringBuf BackBlack() const noexcept; + TStringBuf BackRed() const noexcept; + TStringBuf BackGreen() const noexcept; + TStringBuf BackYellow() const noexcept; + TStringBuf BackBlue() const noexcept; + TStringBuf BackMagenta() const noexcept; + TStringBuf BackCyan() const noexcept; + TStringBuf BackWhite() const noexcept; + + TStringBuf Default() const noexcept; + TStringBuf Black() const noexcept; + TStringBuf Red() const noexcept; + TStringBuf Green() const noexcept; + TStringBuf Yellow() const noexcept; + TStringBuf Blue() const noexcept; + TStringBuf Magenta() const noexcept; + TStringBuf Cyan() const noexcept; + TStringBuf White() const noexcept; + + TStringBuf LightDefault() const noexcept; + TStringBuf LightBlack() const noexcept; + TStringBuf LightRed() const noexcept; + TStringBuf LightGreen() const noexcept; + TStringBuf LightYellow() const noexcept; + TStringBuf LightBlue() const noexcept; + TStringBuf LightMagenta() const noexcept; + TStringBuf LightCyan() const noexcept; + TStringBuf LightWhite() const noexcept; + + TStringBuf DarkDefault() const noexcept; + TStringBuf DarkBlack() const noexcept; + TStringBuf DarkRed() const noexcept; + TStringBuf DarkGreen() const noexcept; + TStringBuf DarkYellow() const noexcept; + TStringBuf DarkBlue() const noexcept; + TStringBuf DarkMagenta() const noexcept; + TStringBuf DarkCyan() const noexcept; + TStringBuf DarkWhite() const noexcept; + + /// Compatibility; prefer using methods without `Color` suffix in their names. + /// Note: these behave differently from their un-suffixed counterparts. + /// While functions declared above will reset colors completely, these will only reset foreground color and + /// style, without changing the background color and underline/italic settings. Also, names of these functions + /// don't conform with standard, e.g. `YellowColor` actually emits the `lite yellow` escape code. + TStringBuf OldColor() const noexcept; + TStringBuf BoldColor() const noexcept; + TStringBuf BlackColor() const noexcept; + TStringBuf BlueColor() const noexcept; + TStringBuf GreenColor() const noexcept; + TStringBuf CyanColor() const noexcept; + TStringBuf RedColor() const noexcept; + TStringBuf PurpleColor() const noexcept; + TStringBuf BrownColor() const noexcept; + TStringBuf LightGrayColor() const noexcept; + TStringBuf DarkGrayColor() const noexcept; + TStringBuf LightBlueColor() const noexcept; + TStringBuf LightGreenColor() const noexcept; + TStringBuf LightCyanColor() const noexcept; + TStringBuf LightRedColor() const noexcept; + TStringBuf LightPurpleColor() const noexcept; + TStringBuf YellowColor() const noexcept; + TStringBuf WhiteColor() const noexcept; + inline bool IsTTY() const noexcept { return IsTTY_; } @@ -212,17 +212,17 @@ namespace NColorizer { bool IsTTY_; }; - /// Singletone `TColors` instances for stderr/stdout. + /// Singletone `TColors` instances for stderr/stdout. TColors& StdErr(); TColors& StdOut(); - /// Choose `TColors` depending on output stream. If passed stream is stderr/stdout, return a corresponding - /// singletone. Otherwise, return a disabled singletone (which you can, but should *not* enable). + /// Choose `TColors` depending on output stream. If passed stream is stderr/stdout, return a corresponding + /// singletone. Otherwise, return a disabled singletone (which you can, but should *not* enable). TColors& AutoColors(IOutputStream& os); - - /// Calculate total length of all ANSI escape codes in the text. - size_t TotalAnsiEscapeCodeLen(TStringBuf text); + + /// Calculate total length of all ANSI escape codes in the text. + size_t TotalAnsiEscapeCodeLen(TStringBuf text); } - -TStringBuf ToStringBuf(NColorizer::EAnsiCode x); -TString ToString(NColorizer::EAnsiCode x); + +TStringBuf ToStringBuf(NColorizer::EAnsiCode x); +TString ToString(NColorizer::EAnsiCode x); diff --git a/library/cpp/colorizer/output.h b/library/cpp/colorizer/output.h index 99afbd3427..1219827d25 100644 --- a/library/cpp/colorizer/output.h +++ b/library/cpp/colorizer/output.h @@ -2,9 +2,9 @@ #include "colors.h" -// Note: this is an old interface for printing colors to stream. -// Consider printing elements of `EAnsiCode` directly. - +// Note: this is an old interface for printing colors to stream. +// Consider printing elements of `EAnsiCode` directly. + namespace NColorizer { typedef TStringBuf (TColors::*TColorFunc)() const; diff --git a/library/cpp/colorizer/ut/colorizer_ut.cpp b/library/cpp/colorizer/ut/colorizer_ut.cpp index 20341440af..d6f6be69a6 100644 --- a/library/cpp/colorizer/ut/colorizer_ut.cpp +++ b/library/cpp/colorizer/ut/colorizer_ut.cpp @@ -1,7 +1,7 @@ #include <library/cpp/colorizer/colors.h> #include <library/cpp/testing/unittest/registar.h> -#include <util/stream/str.h> +#include <util/stream/str.h> #include <util/string/escape.h> @@ -24,40 +24,40 @@ Y_UNIT_TEST_SUITE(ColorizerTest) { // 22, not 0, should be used to reset boldness UNIT_ASSERT_STRINGS_EQUAL(EscapeC(colors.PurpleColor()), "\\x1B[22;35m"); } - - Y_UNIT_TEST(PrintAnsi) { - UNIT_ASSERT_STRINGS_EQUAL(EscapeC(ToString(NColorizer::BLUE)), "\\x1B[0m\\x1B[0;34m"); - { - TString str; - TStringOutput sink{str}; - - sink << NColorizer::BLUE << "foo!" << NColorizer::RESET; - - UNIT_ASSERT_STRINGS_EQUAL(EscapeC(str), "foo!"); // TStringOutput is not tty - } - { - TString str; - TStringOutput sink{str}; - - // Enable this for test purposes. If you're making output of the `AutoColors` constant and this - // test does not compile, you're free to remove it. - NColorizer::AutoColors(sink).Enable(); - - sink << NColorizer::BLUE << "foo!" << NColorizer::RESET; - - UNIT_ASSERT_STRINGS_EQUAL(EscapeC(str), "\\x1B[0m\\x1B[0;34mfoo!\\x1B[0m"); // TStringOutput is not tty - } - } - - Y_UNIT_TEST(EscapeCodeLen) { - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some text"), 0); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0m text"), 4); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("\033[0msome text"), 4); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some text\033[0m"), 4); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0;1;2;3m text"), 10); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0;1;2;3 text"), 0); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\0330;1;2;3m text"), 0); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some [0;1;2;3m text"), 0); - UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[m text"), 3); - } + + Y_UNIT_TEST(PrintAnsi) { + UNIT_ASSERT_STRINGS_EQUAL(EscapeC(ToString(NColorizer::BLUE)), "\\x1B[0m\\x1B[0;34m"); + { + TString str; + TStringOutput sink{str}; + + sink << NColorizer::BLUE << "foo!" << NColorizer::RESET; + + UNIT_ASSERT_STRINGS_EQUAL(EscapeC(str), "foo!"); // TStringOutput is not tty + } + { + TString str; + TStringOutput sink{str}; + + // Enable this for test purposes. If you're making output of the `AutoColors` constant and this + // test does not compile, you're free to remove it. + NColorizer::AutoColors(sink).Enable(); + + sink << NColorizer::BLUE << "foo!" << NColorizer::RESET; + + UNIT_ASSERT_STRINGS_EQUAL(EscapeC(str), "\\x1B[0m\\x1B[0;34mfoo!\\x1B[0m"); // TStringOutput is not tty + } + } + + Y_UNIT_TEST(EscapeCodeLen) { + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some text"), 0); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0m text"), 4); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("\033[0msome text"), 4); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some text\033[0m"), 4); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0;1;2;3m text"), 10); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[0;1;2;3 text"), 0); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\0330;1;2;3m text"), 0); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some [0;1;2;3m text"), 0); + UNIT_ASSERT_VALUES_EQUAL(NColorizer::TotalAnsiEscapeCodeLen("some\033[m text"), 3); + } } diff --git a/library/cpp/getopt/last_getopt_demo/demo.cpp b/library/cpp/getopt/last_getopt_demo/demo.cpp index 79426a9cc9..9dc8ceae85 100644 --- a/library/cpp/getopt/last_getopt_demo/demo.cpp +++ b/library/cpp/getopt/last_getopt_demo/demo.cpp @@ -2,241 +2,241 @@ #include <library/cpp/getopt/modchooser.h> #include <library/cpp/colorizer/colors.h> -// For the sake of this example, let's implement Wget - -Y_COMPLETER(HeaderCompleter) { - AddCompletion("Host"); - AddCompletion("Referer"); - - bool addPostHeaders = false; - - for (int i = 0; i < argc; ++i) { +// For the sake of this example, let's implement Wget + +Y_COMPLETER(HeaderCompleter) { + AddCompletion("Host"); + AddCompletion("Referer"); + + bool addPostHeaders = false; + + for (int i = 0; i < argc; ++i) { if (argv[i] == TStringBuf("--method") && i + 1 < argc) { - auto method = TString(argv[i + 1]); - method.to_upper(); - addPostHeaders = method == "POST" || method == "PUT"; - break; + auto method = TString(argv[i + 1]); + method.to_upper(); + addPostHeaders = method == "POST" || method == "PUT"; + break; } else if (argv[i] == TStringBuf("--post-data") || argv[i] == TStringBuf("--post-file")) { - addPostHeaders = true; - break; - } - } - - if (addPostHeaders) { - AddCompletion("Content-Type"); - AddCompletion("Content-Encoding"); - AddCompletion("Content-Language"); - AddCompletion("Content-Length"); - AddCompletion("Content-Location"); - AddCompletion("Content-MD5"); - AddCompletion("Content-Range"); - } + addPostHeaders = true; + break; + } + } + + if (addPostHeaders) { + AddCompletion("Content-Type"); + AddCompletion("Content-Encoding"); + AddCompletion("Content-Language"); + AddCompletion("Content-Length"); + AddCompletion("Content-Location"); + AddCompletion("Content-MD5"); + AddCompletion("Content-Range"); + } } -class TMain: public TMainClassArgs { - bool Background_; - size_t Timeout_; - TString ExplicitMethod_; - TString ImplicitMethod_ = "GET"; - TString UserAgent_; - TMaybe<TString> PostData_; - TMaybe<TString> PostFile_; - TVector<TString> Headers_; - -protected: - void RegisterOptions(NLastGetopt::TOpts& opts) override { +class TMain: public TMainClassArgs { + bool Background_; + size_t Timeout_; + TString ExplicitMethod_; + TString ImplicitMethod_ = "GET"; + TString UserAgent_; + TMaybe<TString> PostData_; + TMaybe<TString> PostFile_; + TVector<TString> Headers_; + +protected: + void RegisterOptions(NLastGetopt::TOpts& opts) override { // Brief description for the whole program, will appear in the beginning of a help message. - opts.SetTitle("last_getopt_demo -- like wget, but doesn't actually do anything"); - - // Built-in options. - opts.AddHelpOption('h'); - opts.AddCompletionOption("last_getopt_demo"); - - // Custom options. - - opts.AddLongOption('V', "version") - .Help("print version and exit") - .IfPresentDisableCompletion() - .NoArgument() - .Handler([]() { - Cerr << "last_getopt_demo 1.0.0" << Endl; - exit(0); - }); - - opts.AddLongOption('b', "background") - .Help("go to background immediately after startup") - .StoreTrue(&Background_); - - opts.AddLongOption("timeout") - .RequiredArgument("timeout") - .DefaultValue("60000") - .Help("specify timeout in milliseconds for each request") - .CompletionHelp("specify timeout for each request") - .CompletionArgHelp("timeout (ms)") - .StoreResult(&Timeout_) - .Completer(NLastGetopt::NComp::Choice({{"1000"}, {"5000"}, {"10000"}, {"60000"}})); - - opts.AddLongOption("method") - .RequiredArgument("http-method") - .Help("specify HTTP method") - .CompletionArgHelp("http method") - .StoreResult(&ExplicitMethod_) - .Completer( - NLastGetopt::NComp::Choice( - {{"GET", "request representation of the specified resource"}, - {"HEAD", "request response identical to that of GET, but without response body"}, - {"POST", "submit an entry to the specified resource"}, - {"PUT", "replace representation of the specified resource with the request body"}, - {"DELETE", "delete the specified resource"}, - {"CONNECT", "establish a tunnel to the server identified by the target resource"}, - {"OPTIONS", "describe the communication options for the target resource"}, - {"TRACE", "perform a message loop-back test"}, - {"PATCH", "apply partial modifications to the specified resource"}})); - - opts.AddLongOption('U', "user-agent") - .RequiredArgument("agent-string") - .DefaultValue("LastGetoptDemo/1.0.0") - .Help("identify as `agent-string` to the HTTP server") - .CompletionHelp("set custom user agent for each HTTP request") - .CompletionArgHelp("user agent string") - .StoreResult(&UserAgent_); - - opts.AddLongOption("post-data") - .RequiredArgument("string") - .Help("use POST method and send the specified data in the request body (cannot be used with --post-file)") - .CompletionHelp("use POST method and send the specified data in the request body") - .CompletionArgHelp("POST data string") - .StoreResultT<TString>(&PostData_) - .Handler0([this]() { - ImplicitMethod_ = "POST"; - }); - - opts.AddLongOption("post-file") - .RequiredArgument("file") - .Help("use POST method and send contents of the specified file in the request body (cannot be used with --post-data)") - .CompletionHelp("use POST method and send contents of the specified file in the request body") - .CompletionArgHelp("POST file") - .StoreResultT<TString>(&PostFile_) - .Handler0([this]() { - ImplicitMethod_ = "POST"; - }) - .Completer(NLastGetopt::NComp::File()); - - // These two options can't be together. - opts.MutuallyExclusive("post-file", "post-data"); - - opts.AddLongOption("header") - .RequiredArgument("header-line") - .Help("send `header-line` along with the rest of the headers in each HTTP request") - .CompletionHelp("add header to each HTTP request") - .CompletionArgHelp("header string") - .AppendTo(&Headers_) - .AllowMultipleCompletion() - .Completer(NLastGetopt::NComp::LaunchSelf(HeaderCompleter)); - - // Setting up free arguments. - - // We are going to have one mandatory argument and unlimited number of optional arguments. - opts.SetFreeArgsMin(1); - opts.SetFreeArgsMax(NLastGetopt::TOpts::UNLIMITED_ARGS); - - // Configuration for the first argument. - opts.GetFreeArgSpec(0) - .Title("URL") - .Help("URL for download") - .CompletionArgHelp("URL for download") - .Completer(NLastGetopt::NComp::Url()); - - // Configuration for optional arguments. - opts.GetTrailingArgSpec() - .Title("URL") - .CompletionArgHelp("URL for download") - .Completer(NLastGetopt::NComp::Url()); - + opts.SetTitle("last_getopt_demo -- like wget, but doesn't actually do anything"); + + // Built-in options. + opts.AddHelpOption('h'); + opts.AddCompletionOption("last_getopt_demo"); + + // Custom options. + + opts.AddLongOption('V', "version") + .Help("print version and exit") + .IfPresentDisableCompletion() + .NoArgument() + .Handler([]() { + Cerr << "last_getopt_demo 1.0.0" << Endl; + exit(0); + }); + + opts.AddLongOption('b', "background") + .Help("go to background immediately after startup") + .StoreTrue(&Background_); + + opts.AddLongOption("timeout") + .RequiredArgument("timeout") + .DefaultValue("60000") + .Help("specify timeout in milliseconds for each request") + .CompletionHelp("specify timeout for each request") + .CompletionArgHelp("timeout (ms)") + .StoreResult(&Timeout_) + .Completer(NLastGetopt::NComp::Choice({{"1000"}, {"5000"}, {"10000"}, {"60000"}})); + + opts.AddLongOption("method") + .RequiredArgument("http-method") + .Help("specify HTTP method") + .CompletionArgHelp("http method") + .StoreResult(&ExplicitMethod_) + .Completer( + NLastGetopt::NComp::Choice( + {{"GET", "request representation of the specified resource"}, + {"HEAD", "request response identical to that of GET, but without response body"}, + {"POST", "submit an entry to the specified resource"}, + {"PUT", "replace representation of the specified resource with the request body"}, + {"DELETE", "delete the specified resource"}, + {"CONNECT", "establish a tunnel to the server identified by the target resource"}, + {"OPTIONS", "describe the communication options for the target resource"}, + {"TRACE", "perform a message loop-back test"}, + {"PATCH", "apply partial modifications to the specified resource"}})); + + opts.AddLongOption('U', "user-agent") + .RequiredArgument("agent-string") + .DefaultValue("LastGetoptDemo/1.0.0") + .Help("identify as `agent-string` to the HTTP server") + .CompletionHelp("set custom user agent for each HTTP request") + .CompletionArgHelp("user agent string") + .StoreResult(&UserAgent_); + + opts.AddLongOption("post-data") + .RequiredArgument("string") + .Help("use POST method and send the specified data in the request body (cannot be used with --post-file)") + .CompletionHelp("use POST method and send the specified data in the request body") + .CompletionArgHelp("POST data string") + .StoreResultT<TString>(&PostData_) + .Handler0([this]() { + ImplicitMethod_ = "POST"; + }); + + opts.AddLongOption("post-file") + .RequiredArgument("file") + .Help("use POST method and send contents of the specified file in the request body (cannot be used with --post-data)") + .CompletionHelp("use POST method and send contents of the specified file in the request body") + .CompletionArgHelp("POST file") + .StoreResultT<TString>(&PostFile_) + .Handler0([this]() { + ImplicitMethod_ = "POST"; + }) + .Completer(NLastGetopt::NComp::File()); + + // These two options can't be together. + opts.MutuallyExclusive("post-file", "post-data"); + + opts.AddLongOption("header") + .RequiredArgument("header-line") + .Help("send `header-line` along with the rest of the headers in each HTTP request") + .CompletionHelp("add header to each HTTP request") + .CompletionArgHelp("header string") + .AppendTo(&Headers_) + .AllowMultipleCompletion() + .Completer(NLastGetopt::NComp::LaunchSelf(HeaderCompleter)); + + // Setting up free arguments. + + // We are going to have one mandatory argument and unlimited number of optional arguments. + opts.SetFreeArgsMin(1); + opts.SetFreeArgsMax(NLastGetopt::TOpts::UNLIMITED_ARGS); + + // Configuration for the first argument. + opts.GetFreeArgSpec(0) + .Title("URL") + .Help("URL for download") + .CompletionArgHelp("URL for download") + .Completer(NLastGetopt::NComp::Url()); + + // Configuration for optional arguments. + opts.GetTrailingArgSpec() + .Title("URL") + .CompletionArgHelp("URL for download") + .Completer(NLastGetopt::NComp::Url()); + // Let's add more text to our help. A nice description and examples. - - opts.AddSection( - "Description", - + + opts.AddSection( + "Description", + "LastGetoptDemo is a showcase of library/cpp/getopt capabilities. It mimics interface of Wget " - "but doesn't actually do anything." - "\n\n" - "GNU Wget, on the other hand, is a free utility for non-interactive download of files from the Web." - "It supports HTTP, HTTPS, and FTP protocols, as well as retrieval through HTTP proxies." - "\n\n" - "Wget is non-interactive, meaning that it can work in the background, while the user is not logged on. " - "This allows you to start a retrieval and disconnect from the system, letting Wget finish the work. " - "By contrast, most of the Web browsers require constant user's presence, " - "which can be a great hindrance when transferring a lot of data." - "\n\n" - "Wget can follow links in HTML, XHTML, and CSS pages, to create local versions of remote web sites, " - "fully recreating the directory structure of the original site. " - "This is sometimes referred to as \"recursive downloading.\" " - "While doing that, Wget respects the Robot Exclusion Standard (/robots.txt). " - "Wget can be instructed to convert the links in downloaded files to point at the local files, " - "for offline viewing." - "\n\n" - "Wget has been designed for robustness over slow or unstable network connections; " - "if a download fails due to a network problem, " - "it will keep retrying until the whole file has been retrieved. " - "If the server supports regetting, " - "it will instruct the server to continue the download from where it left off." - "\n\n" - "Wget does not support Client Revocation Lists (CRLs) so the HTTPS certificate " - "you are connecting to might be revoked by the siteowner."); - - // We will use colors for this one. - auto& colors = NColorizer::StdErr(); - opts.AddSection( - "Examples", - - TStringBuilder() - << "Download a file:" - << "\n" - << colors.Cyan() - << " $ last_getopt_demo https://wordpress.org/latest.zip" - << colors.Reset() - << "\n" - << "Download a file in background, set custom user agent:" - << "\n" - << colors.Cyan() - << " $ last_getopt_demo -b -U 'Wget/1.0.0' https://wordpress.org/latest.zip" - << colors.Reset()); - } - - int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { - using namespace NColorizer; - - TString method = ExplicitMethod_ ? ExplicitMethod_ : ImplicitMethod_; - - Cerr << ST_LIGHT << "Settings:" << RESET << Endl; - Cerr << GREEN << " Background: " << RESET << Background_ << Endl; - Cerr << GREEN << " Timeout: " << RESET << Timeout_ << Endl; - Cerr << GREEN << " Method: " << RESET << method.Quote() << Endl; - Cerr << GREEN << " UserAgent: " << RESET << UserAgent_.Quote() << Endl; - Cerr << GREEN << " PostData: " << RESET << (PostData_ ? PostData_->Quote() : "Nothing") << Endl; - Cerr << GREEN << " PostFile: " << RESET << (PostFile_ ? PostFile_->Quote() : "Nothing") << Endl; - - Cerr << ST_LIGHT << "Headers:" << RESET << Endl; - for (auto& header : Headers_) { - Cerr << " " << header.Quote() << Endl; - } - if (!Headers_) { - Cerr << GREEN << " no headers" << RESET << Endl; - } - - Cerr << ST_LIGHT << "Will download the following URLs:" << RESET << Endl; - for (auto& arg : parsedOptions.GetFreeArgs()) { - Cerr << " " << arg.Quote() << Endl; - } - if (!parsedOptions.GetFreeArgs()) { - Cerr << " no urls" << Endl; - } - return 0; + "but doesn't actually do anything." + "\n\n" + "GNU Wget, on the other hand, is a free utility for non-interactive download of files from the Web." + "It supports HTTP, HTTPS, and FTP protocols, as well as retrieval through HTTP proxies." + "\n\n" + "Wget is non-interactive, meaning that it can work in the background, while the user is not logged on. " + "This allows you to start a retrieval and disconnect from the system, letting Wget finish the work. " + "By contrast, most of the Web browsers require constant user's presence, " + "which can be a great hindrance when transferring a lot of data." + "\n\n" + "Wget can follow links in HTML, XHTML, and CSS pages, to create local versions of remote web sites, " + "fully recreating the directory structure of the original site. " + "This is sometimes referred to as \"recursive downloading.\" " + "While doing that, Wget respects the Robot Exclusion Standard (/robots.txt). " + "Wget can be instructed to convert the links in downloaded files to point at the local files, " + "for offline viewing." + "\n\n" + "Wget has been designed for robustness over slow or unstable network connections; " + "if a download fails due to a network problem, " + "it will keep retrying until the whole file has been retrieved. " + "If the server supports regetting, " + "it will instruct the server to continue the download from where it left off." + "\n\n" + "Wget does not support Client Revocation Lists (CRLs) so the HTTPS certificate " + "you are connecting to might be revoked by the siteowner."); + + // We will use colors for this one. + auto& colors = NColorizer::StdErr(); + opts.AddSection( + "Examples", + + TStringBuilder() + << "Download a file:" + << "\n" + << colors.Cyan() + << " $ last_getopt_demo https://wordpress.org/latest.zip" + << colors.Reset() + << "\n" + << "Download a file in background, set custom user agent:" + << "\n" + << colors.Cyan() + << " $ last_getopt_demo -b -U 'Wget/1.0.0' https://wordpress.org/latest.zip" + << colors.Reset()); } -}; -int main(int argc, const char** argv) { - NLastGetopt::NComp::TCustomCompleter::FireCustomCompleter(argc, argv); - TMain().Run(argc, argv); + int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { + using namespace NColorizer; + + TString method = ExplicitMethod_ ? ExplicitMethod_ : ImplicitMethod_; + + Cerr << ST_LIGHT << "Settings:" << RESET << Endl; + Cerr << GREEN << " Background: " << RESET << Background_ << Endl; + Cerr << GREEN << " Timeout: " << RESET << Timeout_ << Endl; + Cerr << GREEN << " Method: " << RESET << method.Quote() << Endl; + Cerr << GREEN << " UserAgent: " << RESET << UserAgent_.Quote() << Endl; + Cerr << GREEN << " PostData: " << RESET << (PostData_ ? PostData_->Quote() : "Nothing") << Endl; + Cerr << GREEN << " PostFile: " << RESET << (PostFile_ ? PostFile_->Quote() : "Nothing") << Endl; + + Cerr << ST_LIGHT << "Headers:" << RESET << Endl; + for (auto& header : Headers_) { + Cerr << " " << header.Quote() << Endl; + } + if (!Headers_) { + Cerr << GREEN << " no headers" << RESET << Endl; + } + + Cerr << ST_LIGHT << "Will download the following URLs:" << RESET << Endl; + for (auto& arg : parsedOptions.GetFreeArgs()) { + Cerr << " " << arg.Quote() << Endl; + } + if (!parsedOptions.GetFreeArgs()) { + Cerr << " no urls" << Endl; + } + return 0; + } +}; + +int main(int argc, const char** argv) { + NLastGetopt::NComp::TCustomCompleter::FireCustomCompleter(argc, argv); + TMain().Run(argc, argv); } diff --git a/library/cpp/getopt/last_getopt_demo/ya.make b/library/cpp/getopt/last_getopt_demo/ya.make index 53f1cfc122..b9fd815bdc 100644 --- a/library/cpp/getopt/last_getopt_demo/ya.make +++ b/library/cpp/getopt/last_getopt_demo/ya.make @@ -1,6 +1,6 @@ PROGRAM(last_getopt_demo) -OWNER(amatanhead) +OWNER(amatanhead) PEERDIR( library/cpp/getopt diff --git a/library/cpp/getopt/small/completer.cpp b/library/cpp/getopt/small/completer.cpp index 3fff684adb..0536e4cccf 100644 --- a/library/cpp/getopt/small/completer.cpp +++ b/library/cpp/getopt/small/completer.cpp @@ -1,367 +1,367 @@ -#include "completer.h" - -#include "completion_generator.h" - -#include <util/string/cast.h> -#include <util/generic/fwd.h> - -using NLastGetopt::NEscaping::Q; -using NLastGetopt::NEscaping::QQ; -using NLastGetopt::NEscaping::C; -using NLastGetopt::NEscaping::CC; -using NLastGetopt::NEscaping::S; -using NLastGetopt::NEscaping::SS; -using NLastGetopt::NEscaping::B; -using NLastGetopt::NEscaping::BB; - -namespace NLastGetopt::NComp { -#define L out.Line() -#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() - - TCompleterManager::TCompleterManager(TStringBuf command) - : Command_(command) - , Id_(0) - { - } - - TStringBuf TCompleterManager::GetCompleterID(const ICompleter* completer) { - return Queue_.emplace_back(TStringBuilder() << "_" << Command_ << "__completer_" << ++Id_, completer).first; - } - - void TCompleterManager::GenerateZsh(TFormattedOutput& out) { - while (!Queue_.empty()) { - auto[name, completer] = Queue_.back(); - Queue_.pop_back(); - - L << "(( $+functions[" << name << "] )) ||"; - L << name << "() {"; - { - I; - completer->GenerateZsh(out, *this); - } - L << "}"; - L; - } - } - - class TAlternativeCompleter: public ICompleter { - public: - TAlternativeCompleter(TVector<TAlternative> alternatives) - : Alternatives_(std::move(alternatives)) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - for (auto& alternative: Alternatives_) { - if (alternative.Completer != nullptr) { - alternative.Completer->GenerateBash(out); - } - } - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const override { - // We should use '_alternative' here, but it doesn't process escape codes in group descriptions, - // so we dispatch alternatives ourselves. - - L << "local expl action"; - - size_t i = 0; - for (auto& alternative: Alternatives_) { - auto tag = "alt-" + ToString(++i); - auto action = alternative.Completer ? alternative.Completer->GenerateZshAction(manager) : TStringBuf(); - - L; - - if (action.empty()) { - L << "_message -e " << SS(tag) << " " << SS(alternative.Description); - } else if (action.StartsWith("((") && action.EndsWith("))")) { - L << "action=" << action.substr(1, action.size() - 2); - L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; - } else if (action.StartsWith("(") && action.EndsWith(")")) { - L << "action=" << action << ""; - L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; - } else if (action.StartsWith(' ')) { - L << action.substr(1); - } else { - L << "_description " << SS(tag) << " expl " << SS(alternative.Description); - TStringBuf word, args; - action.Split(' ', word, args); - L << word << " \"${expl[@]}\" " << args; - } - } - } - - private: - TVector<TAlternative> Alternatives_; - }; - - ICompleterPtr Alternative(TVector<TAlternative> alternatives) { - return MakeSimpleShared<TAlternativeCompleter>(std::move(alternatives)); - } - - class TSimpleCompleter: public ICompleter { - public: - TSimpleCompleter(TString bashCode, TString action) - : BashCode(std::move(bashCode)) - , Action(std::move(action)) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - if (BashCode) { - L << BashCode; - } - } - - TStringBuf GenerateZshAction(TCompleterManager&) const override { - return Action; - } - - void GenerateZsh(TFormattedOutput&, TCompleterManager&) const override { - Y_FAIL("unreachable"); - } - - private: - TString BashCode; - TString Action; - }; - - ICompleterPtr Choice(TVector<TChoice> choices) { - auto bash = TStringBuilder() << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& choice : choices) { - bash << sep << B(choice.Choice); - sep = " "; - } - bash << "' -- ${cur}) )"; - - auto action = TStringBuilder(); - action << "(("; - for (auto& choice: choices) { - action << " " << SS(choice.Choice); - if (choice.Description) {{ - action << ":" << SS(choice.Description); - }} - } - action << "))"; - return MakeSimpleShared<TSimpleCompleter>(bash, action); - } - - TString Compgen(TStringBuf flags) { - return TStringBuilder() << "COMPREPLY+=( $(compgen " << flags << " -- ${cur}) )"; - } - - ICompleterPtr Default() { - return MakeSimpleShared<TSimpleCompleter>("", "_default"); - } - - ICompleterPtr File(TString pattern) { - if (pattern) { - pattern = " -g " + SS(pattern); - } - return MakeSimpleShared<TSimpleCompleter>("", "_files" + pattern); - } - - ICompleterPtr Directory() { - return MakeSimpleShared<TSimpleCompleter>("", "_files -/"); - } - - ICompleterPtr Host() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A hostname"), "_hosts"); - } - - ICompleterPtr Pid() { - return MakeSimpleShared<TSimpleCompleter>("", "_pids"); - } - - ICompleterPtr User() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A user"), "_users"); - } - - ICompleterPtr Group() { - // For some reason, OSX freezes when trying to perform completion for groups. - // You can try removing this ifdef and debugging it, but be prepared to force-shutdown your machine - // (and possibly reinstall OSX if force-shutdown breaks anything). -#ifdef _darwin_ - return MakeSimpleShared<TSimpleCompleter>("", ""); -#else - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A group"), "_groups"); -#endif - } - - ICompleterPtr Url() { - return MakeSimpleShared<TSimpleCompleter>("", "_urls"); - } - - ICompleterPtr Tty() { - return MakeSimpleShared<TSimpleCompleter>("", "_ttys"); - } - - ICompleterPtr NetInterface() { - return MakeSimpleShared<TSimpleCompleter>("", "_net_interfaces"); - } - - ICompleterPtr TimeZone() { - return MakeSimpleShared<TSimpleCompleter>("", "_time_zone"); - } - - ICompleterPtr Signal() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A signal"), "_signals"); - } - - ICompleterPtr Domain() { - return MakeSimpleShared<TSimpleCompleter>("", "_domains"); - } - - namespace { - TCustomCompleter* Head = nullptr; +#include "completer.h" + +#include "completion_generator.h" + +#include <util/string/cast.h> +#include <util/generic/fwd.h> + +using NLastGetopt::NEscaping::Q; +using NLastGetopt::NEscaping::QQ; +using NLastGetopt::NEscaping::C; +using NLastGetopt::NEscaping::CC; +using NLastGetopt::NEscaping::S; +using NLastGetopt::NEscaping::SS; +using NLastGetopt::NEscaping::B; +using NLastGetopt::NEscaping::BB; + +namespace NLastGetopt::NComp { +#define L out.Line() +#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() + + TCompleterManager::TCompleterManager(TStringBuf command) + : Command_(command) + , Id_(0) + { + } + + TStringBuf TCompleterManager::GetCompleterID(const ICompleter* completer) { + return Queue_.emplace_back(TStringBuilder() << "_" << Command_ << "__completer_" << ++Id_, completer).first; + } + + void TCompleterManager::GenerateZsh(TFormattedOutput& out) { + while (!Queue_.empty()) { + auto[name, completer] = Queue_.back(); + Queue_.pop_back(); + + L << "(( $+functions[" << name << "] )) ||"; + L << name << "() {"; + { + I; + completer->GenerateZsh(out, *this); + } + L << "}"; + L; + } + } + + class TAlternativeCompleter: public ICompleter { + public: + TAlternativeCompleter(TVector<TAlternative> alternatives) + : Alternatives_(std::move(alternatives)) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + for (auto& alternative: Alternatives_) { + if (alternative.Completer != nullptr) { + alternative.Completer->GenerateBash(out); + } + } + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const override { + // We should use '_alternative' here, but it doesn't process escape codes in group descriptions, + // so we dispatch alternatives ourselves. + + L << "local expl action"; + + size_t i = 0; + for (auto& alternative: Alternatives_) { + auto tag = "alt-" + ToString(++i); + auto action = alternative.Completer ? alternative.Completer->GenerateZshAction(manager) : TStringBuf(); + + L; + + if (action.empty()) { + L << "_message -e " << SS(tag) << " " << SS(alternative.Description); + } else if (action.StartsWith("((") && action.EndsWith("))")) { + L << "action=" << action.substr(1, action.size() - 2); + L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; + } else if (action.StartsWith("(") && action.EndsWith(")")) { + L << "action=" << action << ""; + L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; + } else if (action.StartsWith(' ')) { + L << action.substr(1); + } else { + L << "_description " << SS(tag) << " expl " << SS(alternative.Description); + TStringBuf word, args; + action.Split(' ', word, args); + L << word << " \"${expl[@]}\" " << args; + } + } + } + + private: + TVector<TAlternative> Alternatives_; + }; + + ICompleterPtr Alternative(TVector<TAlternative> alternatives) { + return MakeSimpleShared<TAlternativeCompleter>(std::move(alternatives)); + } + + class TSimpleCompleter: public ICompleter { + public: + TSimpleCompleter(TString bashCode, TString action) + : BashCode(std::move(bashCode)) + , Action(std::move(action)) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + if (BashCode) { + L << BashCode; + } + } + + TStringBuf GenerateZshAction(TCompleterManager&) const override { + return Action; + } + + void GenerateZsh(TFormattedOutput&, TCompleterManager&) const override { + Y_FAIL("unreachable"); + } + + private: + TString BashCode; + TString Action; + }; + + ICompleterPtr Choice(TVector<TChoice> choices) { + auto bash = TStringBuilder() << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& choice : choices) { + bash << sep << B(choice.Choice); + sep = " "; + } + bash << "' -- ${cur}) )"; + + auto action = TStringBuilder(); + action << "(("; + for (auto& choice: choices) { + action << " " << SS(choice.Choice); + if (choice.Description) {{ + action << ":" << SS(choice.Description); + }} + } + action << "))"; + return MakeSimpleShared<TSimpleCompleter>(bash, action); + } + + TString Compgen(TStringBuf flags) { + return TStringBuilder() << "COMPREPLY+=( $(compgen " << flags << " -- ${cur}) )"; + } + + ICompleterPtr Default() { + return MakeSimpleShared<TSimpleCompleter>("", "_default"); + } + + ICompleterPtr File(TString pattern) { + if (pattern) { + pattern = " -g " + SS(pattern); + } + return MakeSimpleShared<TSimpleCompleter>("", "_files" + pattern); + } + + ICompleterPtr Directory() { + return MakeSimpleShared<TSimpleCompleter>("", "_files -/"); + } + + ICompleterPtr Host() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A hostname"), "_hosts"); + } + + ICompleterPtr Pid() { + return MakeSimpleShared<TSimpleCompleter>("", "_pids"); + } + + ICompleterPtr User() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A user"), "_users"); + } + + ICompleterPtr Group() { + // For some reason, OSX freezes when trying to perform completion for groups. + // You can try removing this ifdef and debugging it, but be prepared to force-shutdown your machine + // (and possibly reinstall OSX if force-shutdown breaks anything). +#ifdef _darwin_ + return MakeSimpleShared<TSimpleCompleter>("", ""); +#else + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A group"), "_groups"); +#endif + } + + ICompleterPtr Url() { + return MakeSimpleShared<TSimpleCompleter>("", "_urls"); + } + + ICompleterPtr Tty() { + return MakeSimpleShared<TSimpleCompleter>("", "_ttys"); + } + + ICompleterPtr NetInterface() { + return MakeSimpleShared<TSimpleCompleter>("", "_net_interfaces"); + } + + ICompleterPtr TimeZone() { + return MakeSimpleShared<TSimpleCompleter>("", "_time_zone"); + } + + ICompleterPtr Signal() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A signal"), "_signals"); + } + + ICompleterPtr Domain() { + return MakeSimpleShared<TSimpleCompleter>("", "_domains"); + } + + namespace { + TCustomCompleter* Head = nullptr; TStringBuf SpecialFlag = "---CUSTOM-COMPLETION---"; - } - - void TCustomCompleter::FireCustomCompleter(int argc, const char** argv) { - if (!argc) { - return; - } - - for (int i = 1; i < argc - 4; ++i) { - if (SpecialFlag == argv[i]) { - auto name = TStringBuf(argv[i + 1]); - auto curIdx = FromString<int>(argv[i + 2]); - auto prefix = TStringBuf(argv[i + 3]); - auto suffix = TStringBuf(argv[i + 4]); - - auto cur = TStringBuf(); - if (0 <= curIdx && curIdx < i) { - cur = TStringBuf(argv[curIdx]); - } - if (cur && !prefix && !suffix) { - prefix = cur; // bash does not send prefix and suffix - } - - auto head = Head; - while (head) { - if (head->GetUniqueName() == name) { - head->GenerateCompletions(i, argv, curIdx, cur, prefix, suffix); - } - head = head->Next_; - } - - exit(0); - } - } - } - - void TCustomCompleter::RegisterCustomCompleter(TCustomCompleter* completer) noexcept { - Y_VERIFY(completer); - completer->Next_ = Head; - Head = completer; - } - - void TCustomCompleter::AddCompletion(TStringBuf completion) { - Cout << completion << Endl; // this was easy =) - // TODO: support option descriptions and messages - } - - void TMultipartCustomCompleter::GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) { - auto root = TStringBuf(); - if (prefix.Contains(Sep_)) { - auto tmp = TStringBuf(); - prefix.RSplit(Sep_, root, tmp); - } - - if (root) { - Cout << root << Sep_ << Endl; - } else { - Cout << Endl; - } - - Cout << Sep_ << Endl; - - GenerateCompletionParts(argc, argv, curIdx, cur, prefix, suffix, root); - } - - class TLaunchSelf: public ICompleter { - public: - TLaunchSelf(TCustomCompleter* completer) - : Completer_(completer) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - L << "IFS=$'\\n'"; + } + + void TCustomCompleter::FireCustomCompleter(int argc, const char** argv) { + if (!argc) { + return; + } + + for (int i = 1; i < argc - 4; ++i) { + if (SpecialFlag == argv[i]) { + auto name = TStringBuf(argv[i + 1]); + auto curIdx = FromString<int>(argv[i + 2]); + auto prefix = TStringBuf(argv[i + 3]); + auto suffix = TStringBuf(argv[i + 4]); + + auto cur = TStringBuf(); + if (0 <= curIdx && curIdx < i) { + cur = TStringBuf(argv[curIdx]); + } + if (cur && !prefix && !suffix) { + prefix = cur; // bash does not send prefix and suffix + } + + auto head = Head; + while (head) { + if (head->GetUniqueName() == name) { + head->GenerateCompletions(i, argv, curIdx, cur, prefix, suffix); + } + head = head->Next_; + } + + exit(0); + } + } + } + + void TCustomCompleter::RegisterCustomCompleter(TCustomCompleter* completer) noexcept { + Y_VERIFY(completer); + completer->Next_ = Head; + Head = completer; + } + + void TCustomCompleter::AddCompletion(TStringBuf completion) { + Cout << completion << Endl; // this was easy =) + // TODO: support option descriptions and messages + } + + void TMultipartCustomCompleter::GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) { + auto root = TStringBuf(); + if (prefix.Contains(Sep_)) { + auto tmp = TStringBuf(); + prefix.RSplit(Sep_, root, tmp); + } + + if (root) { + Cout << root << Sep_ << Endl; + } else { + Cout << Endl; + } + + Cout << Sep_ << Endl; + + GenerateCompletionParts(argc, argv, curIdx, cur, prefix, suffix, root); + } + + class TLaunchSelf: public ICompleter { + public: + TLaunchSelf(TCustomCompleter* completer) + : Completer_(completer) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + L << "IFS=$'\\n'"; L << "COMPREPLY+=( $(compgen -W \"$(${words[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${cword}\" \"\" \"\" 2> /dev/null)\" -- ${cur}) )"; - L << "IFS=$' \\t\\n'"; - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { + L << "IFS=$' \\t\\n'"; + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { L << "compadd ${@} ${expl[@]} -- \"${(@f)$(${words_orig[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${current_orig}\" \"${prefix_orig}\" \"${suffix_orig}\" 2> /dev/null)}\""; - } - - private: - TCustomCompleter* Completer_; - }; - - ICompleterPtr LaunchSelf(TCustomCompleter& completer) { - return MakeSimpleShared<TLaunchSelf>(&completer); - } - - class TLaunchSelfMultiPart: public ICompleter { - public: - TLaunchSelfMultiPart(TCustomCompleter* completer) - : Completer_(completer) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - L << "IFS=$'\\n'"; + } + + private: + TCustomCompleter* Completer_; + }; + + ICompleterPtr LaunchSelf(TCustomCompleter& completer) { + return MakeSimpleShared<TLaunchSelf>(&completer); + } + + class TLaunchSelfMultiPart: public ICompleter { + public: + TLaunchSelfMultiPart(TCustomCompleter* completer) + : Completer_(completer) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + L << "IFS=$'\\n'"; L << "items=( $(${words[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${cword}\" \"\" \"\" 2> /dev/null) )"; - L << "candidates=$(compgen -W \"${items[*]:1}\" -- \"$cur\")"; - L << "COMPREPLY+=( $candidates )"; - L << "[[ $candidates == *\"${items[1]}\" ]] && need_space=\"\""; - L << "IFS=$' \\t\\n'"; - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { + L << "candidates=$(compgen -W \"${items[*]:1}\" -- \"$cur\")"; + L << "COMPREPLY+=( $candidates )"; + L << "[[ $candidates == *\"${items[1]}\" ]] && need_space=\"\""; + L << "IFS=$' \\t\\n'"; + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { L << "local items=( \"${(@f)$(${words_orig[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${current_orig}\" \"${prefix_orig}\" \"${suffix_orig}\" 2> /dev/null)}\" )"; - L; - L << "local rempat=${items[1]}"; - L << "shift items"; - L; - L << "local sep=${items[1]}"; - L << "shift items"; - L; - L << "local files=( ${items:#*\"${sep}\"} )"; - L << "local filenames=( ${files#\"${rempat}\"} )"; - L << "local dirs=( ${(M)items:#*\"${sep}\"} )"; - L << "local dirnames=( ${dirs#\"${rempat}\"} )"; - L; - L << "local need_suf"; - L << "compset -S \"${sep}*\" || need_suf=\"1\""; - L; - L << "compadd ${@} ${expl[@]} -d filenames -- ${(q)files}"; - L << "compadd ${@} ${expl[@]} ${need_suf:+-S\"${sep}\"} -q -d dirnames -- ${(q)dirs%\"${sep}\"}"; - } - - private: - TCustomCompleter* Completer_; - }; - - ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer) { - return MakeSimpleShared<TLaunchSelfMultiPart>(&completer); - } - -#undef I -#undef L -} + L; + L << "local rempat=${items[1]}"; + L << "shift items"; + L; + L << "local sep=${items[1]}"; + L << "shift items"; + L; + L << "local files=( ${items:#*\"${sep}\"} )"; + L << "local filenames=( ${files#\"${rempat}\"} )"; + L << "local dirs=( ${(M)items:#*\"${sep}\"} )"; + L << "local dirnames=( ${dirs#\"${rempat}\"} )"; + L; + L << "local need_suf"; + L << "compset -S \"${sep}*\" || need_suf=\"1\""; + L; + L << "compadd ${@} ${expl[@]} -d filenames -- ${(q)files}"; + L << "compadd ${@} ${expl[@]} ${need_suf:+-S\"${sep}\"} -q -d dirnames -- ${(q)dirs%\"${sep}\"}"; + } + + private: + TCustomCompleter* Completer_; + }; + + ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer) { + return MakeSimpleShared<TLaunchSelfMultiPart>(&completer); + } + +#undef I +#undef L +} diff --git a/library/cpp/getopt/small/completer.h b/library/cpp/getopt/small/completer.h index 4136f13add..6a2fdbbbf4 100644 --- a/library/cpp/getopt/small/completer.h +++ b/library/cpp/getopt/small/completer.h @@ -1,306 +1,306 @@ -#pragma once - -#include "formatted_output.h" - -#include <util/generic/strbuf.h> -#include <util/generic/hash.h> - -#include <utility> -#include <util/generic/fwd.h> - -namespace NLastGetopt::NComp { - class ICompleter; - - class TCompleterManager { - public: - TCompleterManager(TStringBuf command); - - /// Register new completer and get its function name. - TStringBuf GetCompleterID(const ICompleter* completer); - - /// Generate zsh code for all collected completers. - void GenerateZsh(TFormattedOutput& out); - - private: - TStringBuf Command_; - size_t Id_; - TVector<std::pair<TString, const ICompleter*>> Queue_; - }; - - class ICompleter { - public: - virtual ~ICompleter() = default; - - public: - /// Generate arbitrary bash code that modifies `COMPREPLY`. - virtual void GenerateBash(TFormattedOutput& out) const = 0; - - /// Generate action that will be used with `_arguments`. If this completer requires a separate function, - /// register it in the given manager and return function name assigned by manager. - /// Supported forms are '()', '(items...)', '((items...))', 'command ...' and ' command ...'. - /// Other forms, such as '{eval-string}', '->state', '=action' are not supported. - virtual TStringBuf GenerateZshAction(TCompleterManager& manager) const = 0; - - /// Generate body of a zsh function (if Action points to a custom function). - virtual void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const = 0; - }; - - using ICompleterPtr = TSimpleSharedPtr<ICompleter>; - - /// Generate default completions. - /// Output of this completer depends on shell settings. - /// Usually ut generates file paths. - ICompleterPtr Default(); - - struct TAlternative { - /// Description for this group of completions. Leave empty to use completer's default description. - TString Description; - - /// Completer that generates values - ICompleterPtr Completer; - - TAlternative(ICompleterPtr completer) - : Description("") - , Completer(std::move(completer)) - { - } - - TAlternative(TString description, ICompleterPtr completer) - : Description(std::move(description)) - , Completer(std::move(completer)) - { - } - }; - - /// Run multiple completers and unite their output. - /// Each completer's output placed in a separate group with its own description. - ICompleterPtr Alternative(TVector<TAlternative> alternatives); - - struct TChoice { - /// Option value. - TString Choice; - - /// Description for a value. - TString Description = ""; - - TChoice(TString choice) - : Choice(std::move(choice)) - { - } - - TChoice(TString choice, TString description) - : Choice(std::move(choice)) - , Description(std::move(description)) - { - } - }; - - /// Complete items from a predefined list of choices. - ICompleterPtr Choice(TVector<TChoice> choices); - - /// Complete files and directories. May filter results by pattern, e.g. `*.txt`. - ICompleterPtr File(TString pattern= ""); - - /// Complete directories. - ICompleterPtr Directory(); - - /// Complete hosts. - ICompleterPtr Host(); - - /// Complete process IDs. - ICompleterPtr Pid(); - - /// Complete users that're found in the system. - ICompleterPtr User(); - - /// Complete user groups that're found in the system. - /// N: for some reason, - ICompleterPtr Group(); - - /// Complete URLs. - ICompleterPtr Url(); - - /// Complete TTY interfaces. - ICompleterPtr Tty(); - - /// Complete network interfaces. - ICompleterPtr NetInterface(); - - /// Complete timezone identifiers. - ICompleterPtr TimeZone(); - - /// Complete unix signal identifiers, e.g. `ABRT` or `KILL`. - ICompleterPtr Signal(); - - /// Complete domains. - ICompleterPtr Domain(); - - /// Custom completer. See `LaunchSelf` below. - class TCustomCompleter { - public: - static void FireCustomCompleter(int argc, const char** argv); - static void RegisterCustomCompleter(TCustomCompleter* completer) noexcept; - - struct TReg { - TReg(TCustomCompleter* completer) noexcept { - TCustomCompleter::RegisterCustomCompleter(completer); - } - }; - - public: - virtual ~TCustomCompleter() = default; - - public: - /// @param argc total number of command line arguments. - /// @param argv array of command line arguments. - /// @param curIdx index of the currently completed argument, may be equal to `argc` if cursor is at the end - /// of line. - /// @param cur currently completed argument. - /// @param prefix part of the currently completed argument before the cursor. - /// @param suffix part of the currently completed argument after the cursor. - virtual void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) = 0; - virtual TStringBuf GetUniqueName() const = 0; - - protected: - void AddCompletion(TStringBuf completion); - - private: - TCustomCompleter* Next_ = nullptr; - }; - - /// Custom completer for objects that consist of multiple parts split by a common separator, such as file paths. - class TMultipartCustomCompleter: public TCustomCompleter { - public: - TMultipartCustomCompleter(TStringBuf sep) - : Sep_(sep) - { - Y_VERIFY(!Sep_.empty()); - } - - public: - void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) final; - - public: - /// @param argc same as in `GenerateCompletions`. - /// @param argv same as in `GenerateCompletions`. - /// @param curIdx same as in `GenerateCompletions`. - /// @param cur same as in `GenerateCompletions`. - /// @param prefix same as in `GenerateCompletions`. - /// @param suffix same as in `GenerateCompletions`. - /// @param root part of the currently completed argument before the last separator. - virtual void GenerateCompletionParts(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix, TStringBuf root) = 0; - - protected: - TStringBuf Sep_; - }; - -#define Y_COMPLETER(N) \ -class T##N: public ::NLastGetopt::NComp::TCustomCompleter { \ - public: \ - void GenerateCompletions(int, const char**, int, TStringBuf, TStringBuf, TStringBuf) override; \ - TStringBuf GetUniqueName() const override { return #N; } \ - }; \ - T##N N = T##N(); \ - ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ - void T##N::GenerateCompletions( \ - Y_DECLARE_UNUSED int argc, \ - Y_DECLARE_UNUSED const char** argv, \ - Y_DECLARE_UNUSED int curIdx, \ - Y_DECLARE_UNUSED TStringBuf cur, \ - Y_DECLARE_UNUSED TStringBuf prefix, \ - Y_DECLARE_UNUSED TStringBuf suffix) - -#define Y_MULTIPART_COMPLETER(N, SEP) \ -class T##N: public ::NLastGetopt::NComp::TMultipartCustomCompleter { \ - public: \ - T##N() : ::NLastGetopt::NComp::TMultipartCustomCompleter(SEP) {} \ - void GenerateCompletionParts(int, const char**, int, TStringBuf, TStringBuf, TStringBuf, TStringBuf) override; \ - TStringBuf GetUniqueName() const override { return #N; } \ - }; \ - T##N N = T##N(); \ - ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ - void T##N::GenerateCompletionParts( \ - Y_DECLARE_UNUSED int argc, \ - Y_DECLARE_UNUSED const char** argv, \ - Y_DECLARE_UNUSED int curIdx, \ - Y_DECLARE_UNUSED TStringBuf cur, \ - Y_DECLARE_UNUSED TStringBuf prefix, \ - Y_DECLARE_UNUSED TStringBuf suffix, \ - Y_DECLARE_UNUSED TStringBuf root) - - /// Launches this binary with a specially formed flags and retrieves completions from stdout. - /// - /// Your application must be set up in a certain way for this to work. - /// - /// First, create a custom completer: - /// - /// ``` - /// Y_COMPLETER(SomeUniqueName) { - /// AddCompletion("foo"); - /// AddCompletion("bar"); - /// AddCompletion("baz"); - /// } - /// ``` - /// - /// Then, use it with this function. - /// - /// On completion attempt, completer will call your binary with some special arguments. - /// - /// In your main, before any other logic, call `TCustomCompleter::FireCustomCompleter`. This function will - /// check for said special arguments and invoke the right completer: - /// - /// ``` - /// int main(int argc, const char** argv) { - /// TCustomCompleter::FireCustomCompleter(argc, argv); - /// ... - /// } - /// ``` - ICompleterPtr LaunchSelf(TCustomCompleter& completer); - - /// Launches this binary with a specially formed flags and retrieves completions from stdout. - /// - /// Your application must be set up in a certain way for this to work. See `LaunchSelf` for more info. - /// - /// Multipart completion is designed for objects that consist of multiple parts split by a common separator. - /// It is ideal for completing remote file paths, for example. - /// - /// Multipart completers format stdout in the following way. - /// - /// On the first line, they print a common prefix that should be stripped from all completions. For example, - /// if you complete paths, this prefix would be a common directory. - /// - /// On the second line, they print a separator. If some completion ends with this separator, shell will not add - /// a whitespace after the item is completed. For example, if you complete paths, the separator would be a slash. - /// - /// On the following lines, they print completions, as formed by the `AddCompletion` function. - /// - /// For example, if a user invokes completion like this: - /// - /// ``` - /// $ program //home/logs/<tab><tab> - /// ``` - /// - /// The stdout might look like this: - /// - /// ``` - /// //home/logs/ - /// / - /// //home/logs/access-log - /// //home/logs/redir-log - /// //home/logs/blockstat-log - /// ``` - /// - /// Then autocompletion will look like this: - /// - /// ``` - /// $ program //home/logs/<tab><tab> - /// -- yt path -- - /// access-log redir-log blockstat-log - /// ``` - /// - /// Note: stdout lines with completion suggestions *must* be formatted by the `AddCompletion` function - /// because their format may change in the future. - /// - /// Note: we recommend using `Y_MULTIPART_COMPLETER` because it will handle all stdout printing for you. - ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer); -} +#pragma once + +#include "formatted_output.h" + +#include <util/generic/strbuf.h> +#include <util/generic/hash.h> + +#include <utility> +#include <util/generic/fwd.h> + +namespace NLastGetopt::NComp { + class ICompleter; + + class TCompleterManager { + public: + TCompleterManager(TStringBuf command); + + /// Register new completer and get its function name. + TStringBuf GetCompleterID(const ICompleter* completer); + + /// Generate zsh code for all collected completers. + void GenerateZsh(TFormattedOutput& out); + + private: + TStringBuf Command_; + size_t Id_; + TVector<std::pair<TString, const ICompleter*>> Queue_; + }; + + class ICompleter { + public: + virtual ~ICompleter() = default; + + public: + /// Generate arbitrary bash code that modifies `COMPREPLY`. + virtual void GenerateBash(TFormattedOutput& out) const = 0; + + /// Generate action that will be used with `_arguments`. If this completer requires a separate function, + /// register it in the given manager and return function name assigned by manager. + /// Supported forms are '()', '(items...)', '((items...))', 'command ...' and ' command ...'. + /// Other forms, such as '{eval-string}', '->state', '=action' are not supported. + virtual TStringBuf GenerateZshAction(TCompleterManager& manager) const = 0; + + /// Generate body of a zsh function (if Action points to a custom function). + virtual void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const = 0; + }; + + using ICompleterPtr = TSimpleSharedPtr<ICompleter>; + + /// Generate default completions. + /// Output of this completer depends on shell settings. + /// Usually ut generates file paths. + ICompleterPtr Default(); + + struct TAlternative { + /// Description for this group of completions. Leave empty to use completer's default description. + TString Description; + + /// Completer that generates values + ICompleterPtr Completer; + + TAlternative(ICompleterPtr completer) + : Description("") + , Completer(std::move(completer)) + { + } + + TAlternative(TString description, ICompleterPtr completer) + : Description(std::move(description)) + , Completer(std::move(completer)) + { + } + }; + + /// Run multiple completers and unite their output. + /// Each completer's output placed in a separate group with its own description. + ICompleterPtr Alternative(TVector<TAlternative> alternatives); + + struct TChoice { + /// Option value. + TString Choice; + + /// Description for a value. + TString Description = ""; + + TChoice(TString choice) + : Choice(std::move(choice)) + { + } + + TChoice(TString choice, TString description) + : Choice(std::move(choice)) + , Description(std::move(description)) + { + } + }; + + /// Complete items from a predefined list of choices. + ICompleterPtr Choice(TVector<TChoice> choices); + + /// Complete files and directories. May filter results by pattern, e.g. `*.txt`. + ICompleterPtr File(TString pattern= ""); + + /// Complete directories. + ICompleterPtr Directory(); + + /// Complete hosts. + ICompleterPtr Host(); + + /// Complete process IDs. + ICompleterPtr Pid(); + + /// Complete users that're found in the system. + ICompleterPtr User(); + + /// Complete user groups that're found in the system. + /// N: for some reason, + ICompleterPtr Group(); + + /// Complete URLs. + ICompleterPtr Url(); + + /// Complete TTY interfaces. + ICompleterPtr Tty(); + + /// Complete network interfaces. + ICompleterPtr NetInterface(); + + /// Complete timezone identifiers. + ICompleterPtr TimeZone(); + + /// Complete unix signal identifiers, e.g. `ABRT` or `KILL`. + ICompleterPtr Signal(); + + /// Complete domains. + ICompleterPtr Domain(); + + /// Custom completer. See `LaunchSelf` below. + class TCustomCompleter { + public: + static void FireCustomCompleter(int argc, const char** argv); + static void RegisterCustomCompleter(TCustomCompleter* completer) noexcept; + + struct TReg { + TReg(TCustomCompleter* completer) noexcept { + TCustomCompleter::RegisterCustomCompleter(completer); + } + }; + + public: + virtual ~TCustomCompleter() = default; + + public: + /// @param argc total number of command line arguments. + /// @param argv array of command line arguments. + /// @param curIdx index of the currently completed argument, may be equal to `argc` if cursor is at the end + /// of line. + /// @param cur currently completed argument. + /// @param prefix part of the currently completed argument before the cursor. + /// @param suffix part of the currently completed argument after the cursor. + virtual void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) = 0; + virtual TStringBuf GetUniqueName() const = 0; + + protected: + void AddCompletion(TStringBuf completion); + + private: + TCustomCompleter* Next_ = nullptr; + }; + + /// Custom completer for objects that consist of multiple parts split by a common separator, such as file paths. + class TMultipartCustomCompleter: public TCustomCompleter { + public: + TMultipartCustomCompleter(TStringBuf sep) + : Sep_(sep) + { + Y_VERIFY(!Sep_.empty()); + } + + public: + void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) final; + + public: + /// @param argc same as in `GenerateCompletions`. + /// @param argv same as in `GenerateCompletions`. + /// @param curIdx same as in `GenerateCompletions`. + /// @param cur same as in `GenerateCompletions`. + /// @param prefix same as in `GenerateCompletions`. + /// @param suffix same as in `GenerateCompletions`. + /// @param root part of the currently completed argument before the last separator. + virtual void GenerateCompletionParts(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix, TStringBuf root) = 0; + + protected: + TStringBuf Sep_; + }; + +#define Y_COMPLETER(N) \ +class T##N: public ::NLastGetopt::NComp::TCustomCompleter { \ + public: \ + void GenerateCompletions(int, const char**, int, TStringBuf, TStringBuf, TStringBuf) override; \ + TStringBuf GetUniqueName() const override { return #N; } \ + }; \ + T##N N = T##N(); \ + ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ + void T##N::GenerateCompletions( \ + Y_DECLARE_UNUSED int argc, \ + Y_DECLARE_UNUSED const char** argv, \ + Y_DECLARE_UNUSED int curIdx, \ + Y_DECLARE_UNUSED TStringBuf cur, \ + Y_DECLARE_UNUSED TStringBuf prefix, \ + Y_DECLARE_UNUSED TStringBuf suffix) + +#define Y_MULTIPART_COMPLETER(N, SEP) \ +class T##N: public ::NLastGetopt::NComp::TMultipartCustomCompleter { \ + public: \ + T##N() : ::NLastGetopt::NComp::TMultipartCustomCompleter(SEP) {} \ + void GenerateCompletionParts(int, const char**, int, TStringBuf, TStringBuf, TStringBuf, TStringBuf) override; \ + TStringBuf GetUniqueName() const override { return #N; } \ + }; \ + T##N N = T##N(); \ + ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ + void T##N::GenerateCompletionParts( \ + Y_DECLARE_UNUSED int argc, \ + Y_DECLARE_UNUSED const char** argv, \ + Y_DECLARE_UNUSED int curIdx, \ + Y_DECLARE_UNUSED TStringBuf cur, \ + Y_DECLARE_UNUSED TStringBuf prefix, \ + Y_DECLARE_UNUSED TStringBuf suffix, \ + Y_DECLARE_UNUSED TStringBuf root) + + /// Launches this binary with a specially formed flags and retrieves completions from stdout. + /// + /// Your application must be set up in a certain way for this to work. + /// + /// First, create a custom completer: + /// + /// ``` + /// Y_COMPLETER(SomeUniqueName) { + /// AddCompletion("foo"); + /// AddCompletion("bar"); + /// AddCompletion("baz"); + /// } + /// ``` + /// + /// Then, use it with this function. + /// + /// On completion attempt, completer will call your binary with some special arguments. + /// + /// In your main, before any other logic, call `TCustomCompleter::FireCustomCompleter`. This function will + /// check for said special arguments and invoke the right completer: + /// + /// ``` + /// int main(int argc, const char** argv) { + /// TCustomCompleter::FireCustomCompleter(argc, argv); + /// ... + /// } + /// ``` + ICompleterPtr LaunchSelf(TCustomCompleter& completer); + + /// Launches this binary with a specially formed flags and retrieves completions from stdout. + /// + /// Your application must be set up in a certain way for this to work. See `LaunchSelf` for more info. + /// + /// Multipart completion is designed for objects that consist of multiple parts split by a common separator. + /// It is ideal for completing remote file paths, for example. + /// + /// Multipart completers format stdout in the following way. + /// + /// On the first line, they print a common prefix that should be stripped from all completions. For example, + /// if you complete paths, this prefix would be a common directory. + /// + /// On the second line, they print a separator. If some completion ends with this separator, shell will not add + /// a whitespace after the item is completed. For example, if you complete paths, the separator would be a slash. + /// + /// On the following lines, they print completions, as formed by the `AddCompletion` function. + /// + /// For example, if a user invokes completion like this: + /// + /// ``` + /// $ program //home/logs/<tab><tab> + /// ``` + /// + /// The stdout might look like this: + /// + /// ``` + /// //home/logs/ + /// / + /// //home/logs/access-log + /// //home/logs/redir-log + /// //home/logs/blockstat-log + /// ``` + /// + /// Then autocompletion will look like this: + /// + /// ``` + /// $ program //home/logs/<tab><tab> + /// -- yt path -- + /// access-log redir-log blockstat-log + /// ``` + /// + /// Note: stdout lines with completion suggestions *must* be formatted by the `AddCompletion` function + /// because their format may change in the future. + /// + /// Note: we recommend using `Y_MULTIPART_COMPLETER` because it will handle all stdout printing for you. + ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer); +} diff --git a/library/cpp/getopt/small/completer_command.cpp b/library/cpp/getopt/small/completer_command.cpp index 5e593eec7e..1a0c793bd5 100644 --- a/library/cpp/getopt/small/completer_command.cpp +++ b/library/cpp/getopt/small/completer_command.cpp @@ -1,165 +1,165 @@ -#include "completer_command.h" - -#include "completion_generator.h" -#include "last_getopt.h" -#include "wrap.h" - +#include "completer_command.h" + +#include "completion_generator.h" +#include "last_getopt.h" +#include "wrap.h" + #include <library/cpp/colorizer/colors.h> - -#include <util/string/subst.h> - -namespace NLastGetopt { - TString MakeInfo(TStringBuf command, TStringBuf flag) { - TString info = ( - "This command generates shell script with completion function and prints it to `stdout`, " - "allowing one to re-direct the output to the file of their choosing. " - "Where you place the file will depend on which shell and operating system you are using." - "\n" - "\n" - "\n" - "{B}BASH (Linux){R}:" - "\n" - "\n" - "For system-wide commands, completion files are stored in `/etc/bash_completion.d/`. " - "For user commands, they are stored in `~/.local/share/bash-completion/completions`. " - "So, pipe output of this script to a file in one of these directories:" - "\n" - "\n" - " $ mkdir -p ~/.local/share/bash-completion/completions" - "\n" - " $ {command} {completion} bash >" - "\n" - " ~/.local/share/bash-completion/completions/{command}" - "\n" - "\n" - "You'll need to restart your shell for changes to take effect." - "\n" - "\n" - "\n" - "{B}BASH (OSX){R}:" - "\n" - "\n" - "You'll need `bash-completion` (or `bash-completion@2` if you're using non-default, newer bash) " - "homebrew formula. Completion files are stored in `/usr/local/etc/bash_completion.d`:" - "\n" - "\n" - " $ mkdir -p $(brew --prefix)/etc/bash_completion.d" - "\n" - " $ {command} {completion} bash >" - "\n" - " $(brew --prefix)/etc/bash_completion.d/{command}" - "\n" - "\n" - "Alternatively, just source the script in your `~/.bash_profile`." - "\n" - "\n" - "You'll need to restart your shell for changes to take effect." - "\n" - "\n" - "\n" - "{B}ZSH{R}:" - "\n" - "\n" - "Zsh looks for completion scripts in any directory listed in `$fpath` variable. We recommend placing " - "completions to `~/.zfunc`:" - "\n" - "\n" - " $ mkdir -p ~/.zfunc" - "\n" - " $ {command} {completion} zsh > ~/.zfunc/_{command}" - "\n" - "\n" - "Add the following lines to your `.zshrc` just before `compinit`:" - "\n" - "\n" - " fpath+=~/.zfunc" - "\n" - "\n" - "You'll need to restart your shell for changes to take effect."); - SubstGlobal(info, "{command}", command); - SubstGlobal(info, "{completion}", flag); - SubstGlobal(info, "{B}", NColorizer::StdErr().LightDefault()); - SubstGlobal(info, "{R}", NColorizer::StdErr().Reset()); - return info; - } - - NComp::ICompleterPtr ShellChoiceCompleter() { - return NComp::Choice({{"zsh"}, {"bash"}}); - } - - TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString name) { - return TOpt() - .AddLongName(name) - .Help("generate tab completion script for zsh or bash") - .CompletionHelp("generate tab completion script") - .OptionalArgument("shell-syntax") - .CompletionArgHelp("shell syntax for completion script") - .IfPresentDisableCompletion() - .Completer(ShellChoiceCompleter()) - .Handler1T<TString>([opts, command, name](TStringBuf shell) { - if (shell.empty()) { - Cerr << Wrap(80, MakeInfo(command, "--" + name)) << Endl; - } else if (shell == "bash") { - TBashCompletionGenerator(opts).Generate(command, Cout); - } else if (shell == "zsh") { - TZshCompletionGenerator(opts).Generate(command, Cout); - } else { + +#include <util/string/subst.h> + +namespace NLastGetopt { + TString MakeInfo(TStringBuf command, TStringBuf flag) { + TString info = ( + "This command generates shell script with completion function and prints it to `stdout`, " + "allowing one to re-direct the output to the file of their choosing. " + "Where you place the file will depend on which shell and operating system you are using." + "\n" + "\n" + "\n" + "{B}BASH (Linux){R}:" + "\n" + "\n" + "For system-wide commands, completion files are stored in `/etc/bash_completion.d/`. " + "For user commands, they are stored in `~/.local/share/bash-completion/completions`. " + "So, pipe output of this script to a file in one of these directories:" + "\n" + "\n" + " $ mkdir -p ~/.local/share/bash-completion/completions" + "\n" + " $ {command} {completion} bash >" + "\n" + " ~/.local/share/bash-completion/completions/{command}" + "\n" + "\n" + "You'll need to restart your shell for changes to take effect." + "\n" + "\n" + "\n" + "{B}BASH (OSX){R}:" + "\n" + "\n" + "You'll need `bash-completion` (or `bash-completion@2` if you're using non-default, newer bash) " + "homebrew formula. Completion files are stored in `/usr/local/etc/bash_completion.d`:" + "\n" + "\n" + " $ mkdir -p $(brew --prefix)/etc/bash_completion.d" + "\n" + " $ {command} {completion} bash >" + "\n" + " $(brew --prefix)/etc/bash_completion.d/{command}" + "\n" + "\n" + "Alternatively, just source the script in your `~/.bash_profile`." + "\n" + "\n" + "You'll need to restart your shell for changes to take effect." + "\n" + "\n" + "\n" + "{B}ZSH{R}:" + "\n" + "\n" + "Zsh looks for completion scripts in any directory listed in `$fpath` variable. We recommend placing " + "completions to `~/.zfunc`:" + "\n" + "\n" + " $ mkdir -p ~/.zfunc" + "\n" + " $ {command} {completion} zsh > ~/.zfunc/_{command}" + "\n" + "\n" + "Add the following lines to your `.zshrc` just before `compinit`:" + "\n" + "\n" + " fpath+=~/.zfunc" + "\n" + "\n" + "You'll need to restart your shell for changes to take effect."); + SubstGlobal(info, "{command}", command); + SubstGlobal(info, "{completion}", flag); + SubstGlobal(info, "{B}", NColorizer::StdErr().LightDefault()); + SubstGlobal(info, "{R}", NColorizer::StdErr().Reset()); + return info; + } + + NComp::ICompleterPtr ShellChoiceCompleter() { + return NComp::Choice({{"zsh"}, {"bash"}}); + } + + TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString name) { + return TOpt() + .AddLongName(name) + .Help("generate tab completion script for zsh or bash") + .CompletionHelp("generate tab completion script") + .OptionalArgument("shell-syntax") + .CompletionArgHelp("shell syntax for completion script") + .IfPresentDisableCompletion() + .Completer(ShellChoiceCompleter()) + .Handler1T<TString>([opts, command, name](TStringBuf shell) { + if (shell.empty()) { + Cerr << Wrap(80, MakeInfo(command, "--" + name)) << Endl; + } else if (shell == "bash") { + TBashCompletionGenerator(opts).Generate(command, Cout); + } else if (shell == "zsh") { + TZshCompletionGenerator(opts).Generate(command, Cout); + } else { Cerr << "Unknown shell name " << TString{shell}.Quote() << Endl; - exit(1); - } - exit(0); - }); - } - - class TCompleterMode: public TMainClassArgs { - public: - TCompleterMode(const TModChooser* modChooser, TString command, TString modName) - : Command_(std::move(command)) - , Modes_(modChooser) - , ModName_(std::move(modName)) - { - } - - protected: - void RegisterOptions(NLastGetopt::TOpts& opts) override { - TMainClassArgs::RegisterOptions(opts); - - opts.SetTitle("Generate tab completion scripts for zsh or bash"); - - opts.AddSection("Description", MakeInfo(Command_, ModName_)); - - opts.SetFreeArgsNum(1); - opts.GetFreeArgSpec(0) - .Title("<shell-syntax>") - .Help("shell syntax for completion script (bash or zsh)") - .CompletionArgHelp("shell syntax for completion script") - .Completer(ShellChoiceCompleter()); - } - - int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { - auto arg = parsedOptions.GetFreeArgs()[0]; - arg.to_lower(); - - if (arg == "bash") { - TBashCompletionGenerator(Modes_).Generate(Command_, Cout); - } else if (arg == "zsh") { - TZshCompletionGenerator(Modes_).Generate(Command_, Cout); - } else { - Cerr << "Unknown shell name " << arg.Quote() << Endl; - parsedOptions.PrintUsage(); - return 1; - } - - return 0; - } - - private: - TString Command_; - const TModChooser* Modes_; - TString ModName_; - }; - - THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName) { + exit(1); + } + exit(0); + }); + } + + class TCompleterMode: public TMainClassArgs { + public: + TCompleterMode(const TModChooser* modChooser, TString command, TString modName) + : Command_(std::move(command)) + , Modes_(modChooser) + , ModName_(std::move(modName)) + { + } + + protected: + void RegisterOptions(NLastGetopt::TOpts& opts) override { + TMainClassArgs::RegisterOptions(opts); + + opts.SetTitle("Generate tab completion scripts for zsh or bash"); + + opts.AddSection("Description", MakeInfo(Command_, ModName_)); + + opts.SetFreeArgsNum(1); + opts.GetFreeArgSpec(0) + .Title("<shell-syntax>") + .Help("shell syntax for completion script (bash or zsh)") + .CompletionArgHelp("shell syntax for completion script") + .Completer(ShellChoiceCompleter()); + } + + int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { + auto arg = parsedOptions.GetFreeArgs()[0]; + arg.to_lower(); + + if (arg == "bash") { + TBashCompletionGenerator(Modes_).Generate(Command_, Cout); + } else if (arg == "zsh") { + TZshCompletionGenerator(Modes_).Generate(Command_, Cout); + } else { + Cerr << "Unknown shell name " << arg.Quote() << Endl; + parsedOptions.PrintUsage(); + return 1; + } + + return 0; + } + + private: + TString Command_; + const TModChooser* Modes_; + TString ModName_; + }; + + THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName) { return MakeHolder<TCompleterMode>(modChooser, std::move(command), std::move(modName)); - } -} + } +} diff --git a/library/cpp/getopt/small/completer_command.h b/library/cpp/getopt/small/completer_command.h index 974cc4617c..4aba6dbfde 100644 --- a/library/cpp/getopt/small/completer_command.h +++ b/library/cpp/getopt/small/completer_command.h @@ -1,11 +1,11 @@ -#pragma once - -#include "modchooser.h" - -namespace NLastGetopt { - /// Create an option that generates completion. - TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString optName = "completion"); - - /// Create a mode that generates completion. - THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName = "completion"); -} +#pragma once + +#include "modchooser.h" + +namespace NLastGetopt { + /// Create an option that generates completion. + TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString optName = "completion"); + + /// Create a mode that generates completion. + THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName = "completion"); +} diff --git a/library/cpp/getopt/small/completion_generator.cpp b/library/cpp/getopt/small/completion_generator.cpp index ac41988217..0ff83e1c07 100644 --- a/library/cpp/getopt/small/completion_generator.cpp +++ b/library/cpp/getopt/small/completion_generator.cpp @@ -1,791 +1,791 @@ -#include "completion_generator.h" - +#include "completion_generator.h" + #include <util/generic/overloaded.h> - -#include <util/string/ascii.h> -#include <util/generic/hash_set.h> - -#include "last_getopt_parse_result.h" - -using NLastGetopt::NEscaping::Q; -using NLastGetopt::NEscaping::QQ; -using NLastGetopt::NEscaping::C; -using NLastGetopt::NEscaping::CC; -using NLastGetopt::NEscaping::S; -using NLastGetopt::NEscaping::SS; -using NLastGetopt::NEscaping::B; -using NLastGetopt::NEscaping::BB; - -namespace NLastGetopt { - -#define L out.Line() -#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() - - TCompletionGenerator::TCompletionGenerator(const TModChooser* modChooser) - : Options_(modChooser) - { - Y_VERIFY(modChooser != nullptr); - } - - TCompletionGenerator::TCompletionGenerator(const TOpts* opts) - : Options_(opts) - { - Y_VERIFY(opts != nullptr); - } - - void TZshCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { - TFormattedOutput out; - NComp::TCompleterManager manager{command}; - - L << "#compdef " << command; - L; - L << "_" << command << "() {"; - { - I; - L << "local state line desc modes context curcontext=\"$curcontext\" ret=1"; - L << "local words_orig=(\"${words[@]}\")"; - L << "local current_orig=\"$((CURRENT - 1))\""; - L << "local prefix_orig=\"$PREFIX\""; - L << "local suffix_orig=\"$SUFFIX\""; - L; + +#include <util/string/ascii.h> +#include <util/generic/hash_set.h> + +#include "last_getopt_parse_result.h" + +using NLastGetopt::NEscaping::Q; +using NLastGetopt::NEscaping::QQ; +using NLastGetopt::NEscaping::C; +using NLastGetopt::NEscaping::CC; +using NLastGetopt::NEscaping::S; +using NLastGetopt::NEscaping::SS; +using NLastGetopt::NEscaping::B; +using NLastGetopt::NEscaping::BB; + +namespace NLastGetopt { + +#define L out.Line() +#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() + + TCompletionGenerator::TCompletionGenerator(const TModChooser* modChooser) + : Options_(modChooser) + { + Y_VERIFY(modChooser != nullptr); + } + + TCompletionGenerator::TCompletionGenerator(const TOpts* opts) + : Options_(opts) + { + Y_VERIFY(opts != nullptr); + } + + void TZshCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { + TFormattedOutput out; + NComp::TCompleterManager manager{command}; + + L << "#compdef " << command; + L; + L << "_" << command << "() {"; + { + I; + L << "local state line desc modes context curcontext=\"$curcontext\" ret=1"; + L << "local words_orig=(\"${words[@]}\")"; + L << "local current_orig=\"$((CURRENT - 1))\""; + L << "local prefix_orig=\"$PREFIX\""; + L << "local suffix_orig=\"$SUFFIX\""; + L; std::visit(TOverloaded{ - [&out, &manager](const TModChooser* modChooser) { - GenerateModesCompletion(out, *modChooser, manager); - }, - [&out, &manager](const TOpts* opts) { - GenerateOptsCompletion(out, *opts, manager); - } - }, Options_); - L; - L << "return ret"; - } - L << "}"; - L; - manager.GenerateZsh(out); - - out.Print(stream); - } - - void TZshCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager) { - auto modes = chooser.GetUnsortedModes(); - - L << "_arguments -C \\"; - L << " '(- : *)'{-h,--help}'[show help information]' \\"; - if (chooser.GetVersionHandler() != nullptr) { - L << " '(- : *)'{-v,--version}'[display version information]' \\"; - } - if (!chooser.IsSvnRevisionOptionDisabled()) { - L << " '(- : *)--svnrevision[show build information]' \\"; - } - L << " '(-v --version -h --help --svnrevision)1: :->modes' \\"; - L << " '(-v --version -h --help --svnrevision)*:: :->args' \\"; - L << " && ret=0"; - L; - L << "case $state in"; - { - I; - - L << "modes)"; - { - I; - - size_t tag = 0; - bool empty = true; - - L << "desc='modes'"; - L << "modes=("; - for (auto& mode : modes) { - if (mode->Hidden) { - continue; - } - if (!mode->Name.empty()) { - I; - if (!mode->Description.empty()) { - L << QQ(mode->Name) << ":" << QQ(mode->Description); - } else { - L << QQ(mode->Name); - } - empty = false; - } else { - L << ")"; - if (!empty) { - L << "_describe -t 'mode-group-" << tag << "' $desc modes"; - } - L; - if (mode->Description.empty()) { - L << "desc='modes'"; - } else { - L << "desc=" << SS(mode->Description); - } - L << "modes=("; - empty = true; - ++tag; - } - } - L << ")"; - if (!empty) { - L << "_describe -t 'mode-group-" << tag << "' $desc modes"; - } - L; - - L << ";;"; - } - - L << "args)"; - { - I; - - L << "case $line[1] in"; - { - I; - - for (auto& mode : modes) { - if (mode->Name.empty() || mode->Hidden) { - continue; - } - - auto& line = L << SS(mode->Name); - for (auto& alias : mode->Aliases) { - line << "|" << SS(alias); - } - line << ")"; - - { - I; - - if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { - GenerateOptsCompletion(out, mainArgs->GetOptions(), manager); - } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { - GenerateModesCompletion(out, mainModes->GetSubModes(), manager); - } else { - GenerateDefaultOptsCompletion(out, manager); - } - - L << ";;"; - } - } - } - L << "esac"; - L << ";;"; - } - } - L << "esac"; - } - - void TZshCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager) { - L << "_arguments -s \\"; - { - I; - - if (opts.ArgPermutation_ == EArgPermutation::REQUIRE_ORDER) { - L << "-S \\"; - } - - for (auto opt: opts.GetOpts()) { - if (!opt->Hidden_) { - GenerateOptCompletion(out, opts, *opt, manager); - } - } - - auto argSpecs = opts.GetFreeArgSpecs(); - size_t numFreeArgs = opts.GetFreeArgsMax(); - bool unlimitedArgs = false; - if (numFreeArgs == TOpts::UNLIMITED_ARGS) { - numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); - unlimitedArgs = true; - } - - for (size_t i = 0; i < numFreeArgs; ++i) { - auto& spec = argSpecs[i]; - auto& line = L << "'" << (i + 1) << ":"; - if (spec.IsOptional()) { - line << ":"; - } - auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); - if (argHelp) { - line << Q(argHelp); - } else { - line << " "; - } - line << ":"; - if (spec.Completer_) { - line << spec.Completer_->GenerateZshAction(manager); - } else { - line << "_default"; - } - line << "' \\"; - } - - if (unlimitedArgs) { - auto& spec = opts.GetTrailingArgSpec(); - auto& line = L << "'*:"; - auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); - if (argHelp) { - line << Q(argHelp); - } else { - line << " "; - } - line << ":"; - if (spec.Completer_) { - line << spec.Completer_->GenerateZshAction(manager); - } else { - line << "_default"; - } - line << "' \\"; - } - - L << "&& ret=0"; - } - } - - void TZshCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { - L << "_arguments \\"; - L << " '(- *)'{-h,--help}'[show help information]' \\"; - L << " '(- *)--svnrevision[show build information]' \\"; - L << " '(-h --help --svnrevision)*: :_files' \\"; - L << " && ret=0"; - } - - void TZshCompletionGenerator::GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager) { - auto& line = L; - - THashSet<TString> disableOptions; - if (opt.DisableCompletionForOptions_) { - disableOptions.insert("-"); - } else { - if (!opt.AllowMultipleCompletion_) { - for (auto shortName: opt.GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: opt.GetLongNames()) { - disableOptions.insert("--" + longName); - } - } - for (auto disabledShortName : opt.DisableCompletionForChar_) { - auto disabledOpt = opts.FindCharOption(disabledShortName); - if (disabledOpt) { - for (auto shortName: disabledOpt->GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: disabledOpt->GetLongNames()) { - disableOptions.insert("--" + longName); - } - } else { - disableOptions.insert(TString("-") + disabledShortName); - } - } - for (auto& disabledLongName : opt.DisableCompletionForLongName_) { - auto disabledOpt = opts.FindLongOption(disabledLongName); - if (disabledOpt) { - for (auto shortName: disabledOpt->GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: disabledOpt->GetLongNames()) { - disableOptions.insert("--" + longName); - } - } else { - disableOptions.insert("--" + disabledLongName); - } - } - } - if (opt.DisableCompletionForFreeArgs_) { - disableOptions.insert(":"); - disableOptions.insert("*"); - } else { - for (auto i : opt.DisableCompletionForFreeArg_) { - disableOptions.insert(ToString(i + 1)); - } - } - - TStringBuf sep = ""; - - if (!disableOptions.empty()) { - line << "'("; - for (auto& disableOption : disableOptions) { - line << sep << disableOption; - sep = " "; - } - line << ")"; - } - - sep = ""; - TStringBuf mul = ""; - TStringBuf quot = ""; - - if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { - if (!disableOptions.empty()) { - line << "'"; - } - line << "{"; - quot = "'"; - } else { - if (disableOptions.empty()) { - line << "'"; - } - } - - if (opt.AllowMultipleCompletion_) { - mul = "*"; - } - - for (auto& flag : opt.GetShortNames()) { - line << sep << quot << mul << "-" << Q(TStringBuf(&flag, 1)) << quot; - sep = ","; - } - - for (auto& flag : opt.GetLongNames()) { - line << sep << quot << mul << "--" << Q(flag) << quot; - sep = ","; - } - - if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { - line << "}'"; - } - - if (opt.GetCompletionHelp()) { - line << "["; - line << Q(opt.GetCompletionHelp()); - line << "]"; - } - - if (opt.HasArg_ != EHasArg::NO_ARGUMENT) { - if (opt.HasArg_ == EHasArg::OPTIONAL_ARGUMENT) { - line << ":"; - } - - line << ":"; - - if (opt.GetCompletionArgHelp()) { - line << C(opt.GetCompletionArgHelp()); - } else { - line << " "; - } - - line << ":"; - - if (opt.Completer_) { - line << C(opt.Completer_->GenerateZshAction(manager)); - } else { - line << "_default"; - } - } - - line << "' \\"; - } - - void TBashCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { - TFormattedOutput out; - NComp::TCompleterManager manager{command}; - - L << "_" << command << "() {"; - { - I; - L << "COMPREPLY=()"; - L; - L << "local i args opts items candidates"; - L; - L << "local cur prev words cword"; - L << "_get_comp_words_by_ref -n \"\\\"'><=;|&(:\" cur prev words cword"; - L; - L << "local need_space=\"1\""; - L << "local IFS=$' \\t\\n'"; - L; + [&out, &manager](const TModChooser* modChooser) { + GenerateModesCompletion(out, *modChooser, manager); + }, + [&out, &manager](const TOpts* opts) { + GenerateOptsCompletion(out, *opts, manager); + } + }, Options_); + L; + L << "return ret"; + } + L << "}"; + L; + manager.GenerateZsh(out); + + out.Print(stream); + } + + void TZshCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager) { + auto modes = chooser.GetUnsortedModes(); + + L << "_arguments -C \\"; + L << " '(- : *)'{-h,--help}'[show help information]' \\"; + if (chooser.GetVersionHandler() != nullptr) { + L << " '(- : *)'{-v,--version}'[display version information]' \\"; + } + if (!chooser.IsSvnRevisionOptionDisabled()) { + L << " '(- : *)--svnrevision[show build information]' \\"; + } + L << " '(-v --version -h --help --svnrevision)1: :->modes' \\"; + L << " '(-v --version -h --help --svnrevision)*:: :->args' \\"; + L << " && ret=0"; + L; + L << "case $state in"; + { + I; + + L << "modes)"; + { + I; + + size_t tag = 0; + bool empty = true; + + L << "desc='modes'"; + L << "modes=("; + for (auto& mode : modes) { + if (mode->Hidden) { + continue; + } + if (!mode->Name.empty()) { + I; + if (!mode->Description.empty()) { + L << QQ(mode->Name) << ":" << QQ(mode->Description); + } else { + L << QQ(mode->Name); + } + empty = false; + } else { + L << ")"; + if (!empty) { + L << "_describe -t 'mode-group-" << tag << "' $desc modes"; + } + L; + if (mode->Description.empty()) { + L << "desc='modes'"; + } else { + L << "desc=" << SS(mode->Description); + } + L << "modes=("; + empty = true; + ++tag; + } + } + L << ")"; + if (!empty) { + L << "_describe -t 'mode-group-" << tag << "' $desc modes"; + } + L; + + L << ";;"; + } + + L << "args)"; + { + I; + + L << "case $line[1] in"; + { + I; + + for (auto& mode : modes) { + if (mode->Name.empty() || mode->Hidden) { + continue; + } + + auto& line = L << SS(mode->Name); + for (auto& alias : mode->Aliases) { + line << "|" << SS(alias); + } + line << ")"; + + { + I; + + if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { + GenerateOptsCompletion(out, mainArgs->GetOptions(), manager); + } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { + GenerateModesCompletion(out, mainModes->GetSubModes(), manager); + } else { + GenerateDefaultOptsCompletion(out, manager); + } + + L << ";;"; + } + } + } + L << "esac"; + L << ";;"; + } + } + L << "esac"; + } + + void TZshCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager) { + L << "_arguments -s \\"; + { + I; + + if (opts.ArgPermutation_ == EArgPermutation::REQUIRE_ORDER) { + L << "-S \\"; + } + + for (auto opt: opts.GetOpts()) { + if (!opt->Hidden_) { + GenerateOptCompletion(out, opts, *opt, manager); + } + } + + auto argSpecs = opts.GetFreeArgSpecs(); + size_t numFreeArgs = opts.GetFreeArgsMax(); + bool unlimitedArgs = false; + if (numFreeArgs == TOpts::UNLIMITED_ARGS) { + numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); + unlimitedArgs = true; + } + + for (size_t i = 0; i < numFreeArgs; ++i) { + auto& spec = argSpecs[i]; + auto& line = L << "'" << (i + 1) << ":"; + if (spec.IsOptional()) { + line << ":"; + } + auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); + if (argHelp) { + line << Q(argHelp); + } else { + line << " "; + } + line << ":"; + if (spec.Completer_) { + line << spec.Completer_->GenerateZshAction(manager); + } else { + line << "_default"; + } + line << "' \\"; + } + + if (unlimitedArgs) { + auto& spec = opts.GetTrailingArgSpec(); + auto& line = L << "'*:"; + auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); + if (argHelp) { + line << Q(argHelp); + } else { + line << " "; + } + line << ":"; + if (spec.Completer_) { + line << spec.Completer_->GenerateZshAction(manager); + } else { + line << "_default"; + } + line << "' \\"; + } + + L << "&& ret=0"; + } + } + + void TZshCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { + L << "_arguments \\"; + L << " '(- *)'{-h,--help}'[show help information]' \\"; + L << " '(- *)--svnrevision[show build information]' \\"; + L << " '(-h --help --svnrevision)*: :_files' \\"; + L << " && ret=0"; + } + + void TZshCompletionGenerator::GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager) { + auto& line = L; + + THashSet<TString> disableOptions; + if (opt.DisableCompletionForOptions_) { + disableOptions.insert("-"); + } else { + if (!opt.AllowMultipleCompletion_) { + for (auto shortName: opt.GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: opt.GetLongNames()) { + disableOptions.insert("--" + longName); + } + } + for (auto disabledShortName : opt.DisableCompletionForChar_) { + auto disabledOpt = opts.FindCharOption(disabledShortName); + if (disabledOpt) { + for (auto shortName: disabledOpt->GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: disabledOpt->GetLongNames()) { + disableOptions.insert("--" + longName); + } + } else { + disableOptions.insert(TString("-") + disabledShortName); + } + } + for (auto& disabledLongName : opt.DisableCompletionForLongName_) { + auto disabledOpt = opts.FindLongOption(disabledLongName); + if (disabledOpt) { + for (auto shortName: disabledOpt->GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: disabledOpt->GetLongNames()) { + disableOptions.insert("--" + longName); + } + } else { + disableOptions.insert("--" + disabledLongName); + } + } + } + if (opt.DisableCompletionForFreeArgs_) { + disableOptions.insert(":"); + disableOptions.insert("*"); + } else { + for (auto i : opt.DisableCompletionForFreeArg_) { + disableOptions.insert(ToString(i + 1)); + } + } + + TStringBuf sep = ""; + + if (!disableOptions.empty()) { + line << "'("; + for (auto& disableOption : disableOptions) { + line << sep << disableOption; + sep = " "; + } + line << ")"; + } + + sep = ""; + TStringBuf mul = ""; + TStringBuf quot = ""; + + if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { + if (!disableOptions.empty()) { + line << "'"; + } + line << "{"; + quot = "'"; + } else { + if (disableOptions.empty()) { + line << "'"; + } + } + + if (opt.AllowMultipleCompletion_) { + mul = "*"; + } + + for (auto& flag : opt.GetShortNames()) { + line << sep << quot << mul << "-" << Q(TStringBuf(&flag, 1)) << quot; + sep = ","; + } + + for (auto& flag : opt.GetLongNames()) { + line << sep << quot << mul << "--" << Q(flag) << quot; + sep = ","; + } + + if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { + line << "}'"; + } + + if (opt.GetCompletionHelp()) { + line << "["; + line << Q(opt.GetCompletionHelp()); + line << "]"; + } + + if (opt.HasArg_ != EHasArg::NO_ARGUMENT) { + if (opt.HasArg_ == EHasArg::OPTIONAL_ARGUMENT) { + line << ":"; + } + + line << ":"; + + if (opt.GetCompletionArgHelp()) { + line << C(opt.GetCompletionArgHelp()); + } else { + line << " "; + } + + line << ":"; + + if (opt.Completer_) { + line << C(opt.Completer_->GenerateZshAction(manager)); + } else { + line << "_default"; + } + } + + line << "' \\"; + } + + void TBashCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { + TFormattedOutput out; + NComp::TCompleterManager manager{command}; + + L << "_" << command << "() {"; + { + I; + L << "COMPREPLY=()"; + L; + L << "local i args opts items candidates"; + L; + L << "local cur prev words cword"; + L << "_get_comp_words_by_ref -n \"\\\"'><=;|&(:\" cur prev words cword"; + L; + L << "local need_space=\"1\""; + L << "local IFS=$' \\t\\n'"; + L; std::visit(TOverloaded{ - [&out, &manager](const TModChooser* modChooser) { - GenerateModesCompletion(out, *modChooser, manager, 1); - }, - [&out, &manager](const TOpts* opts) { - GenerateOptsCompletion(out, *opts, manager, 1); - } - }, Options_); - L; - L; - L << "__ltrim_colon_completions \"$cur\""; - L; - L << "IFS=$'\\n'"; - L << "if [ ${#COMPREPLY[@]} -ne 0 ]; then"; - { - I; - L << "if [[ -z $need_space ]]; then"; - { - I; - L << "COMPREPLY=( $(printf \"%q\\n\" \"${COMPREPLY[@]}\") )"; - } - L << "else"; - { - I; - L << "COMPREPLY=( $(printf \"%q \\n\" \"${COMPREPLY[@]}\") )"; - } - L << "fi"; - } - L << "fi"; - L; - L << "return 0"; - } - L << "}"; - L; - L << "complete -o nospace -o default -F _" << command << " " << command; - - out.Print(stream); - } - - void TBashCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level) { - auto modes = chooser.GetUnsortedModes(); - - L << "if [[ ${cword} == " << level << " ]] ; then"; - { - I; - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '-h --help"; - if (chooser.GetVersionHandler() != nullptr) { - line << " -v --version"; - } - if (!chooser.IsSvnRevisionOptionDisabled()) { - line << " --svnrevision"; - } - line << "' -- ${cur}) )"; - } - L << "else"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& mode : modes) { + [&out, &manager](const TModChooser* modChooser) { + GenerateModesCompletion(out, *modChooser, manager, 1); + }, + [&out, &manager](const TOpts* opts) { + GenerateOptsCompletion(out, *opts, manager, 1); + } + }, Options_); + L; + L; + L << "__ltrim_colon_completions \"$cur\""; + L; + L << "IFS=$'\\n'"; + L << "if [ ${#COMPREPLY[@]} -ne 0 ]; then"; + { + I; + L << "if [[ -z $need_space ]]; then"; + { + I; + L << "COMPREPLY=( $(printf \"%q\\n\" \"${COMPREPLY[@]}\") )"; + } + L << "else"; + { + I; + L << "COMPREPLY=( $(printf \"%q \\n\" \"${COMPREPLY[@]}\") )"; + } + L << "fi"; + } + L << "fi"; + L; + L << "return 0"; + } + L << "}"; + L; + L << "complete -o nospace -o default -F _" << command << " " << command; + + out.Print(stream); + } + + void TBashCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level) { + auto modes = chooser.GetUnsortedModes(); + + L << "if [[ ${cword} == " << level << " ]] ; then"; + { + I; + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '-h --help"; + if (chooser.GetVersionHandler() != nullptr) { + line << " -v --version"; + } + if (!chooser.IsSvnRevisionOptionDisabled()) { + line << " --svnrevision"; + } + line << "' -- ${cur}) )"; + } + L << "else"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& mode : modes) { if (!mode->Hidden && !mode->NoCompletion) { line << sep << B(mode->Name); sep = " "; } - } - line << "' -- ${cur}) )"; - } - L << "fi"; - } - L << "else"; - { - I; - L << "case \"${words[" << level << "]}\" in"; - { - I; - - for (auto& mode : modes) { + } + line << "' -- ${cur}) )"; + } + L << "fi"; + } + L << "else"; + { + I; + L << "case \"${words[" << level << "]}\" in"; + { + I; + + for (auto& mode : modes) { if (mode->Name.empty() || mode->Hidden || mode->NoCompletion) { - continue; - } - - auto& line = L << BB(mode->Name); - for (auto& alias : mode->Aliases) { - line << "|" << BB(alias); - } - line << ")"; - - { - I; - - if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { - GenerateOptsCompletion(out, mainArgs->GetOptions(), manager, level + 1); - } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { - GenerateModesCompletion(out, mainModes->GetSubModes(), manager, level + 1); - } else { - GenerateDefaultOptsCompletion(out, manager); - } - - L << ";;"; - } - } - } - L << "esac"; - } - L << "fi"; - } - - void TBashCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager&, size_t level) { - auto unorderedOpts = opts.GetOpts(); - - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& opt : unorderedOpts) { + continue; + } + + auto& line = L << BB(mode->Name); + for (auto& alias : mode->Aliases) { + line << "|" << BB(alias); + } + line << ")"; + + { + I; + + if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { + GenerateOptsCompletion(out, mainArgs->GetOptions(), manager, level + 1); + } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { + GenerateModesCompletion(out, mainModes->GetSubModes(), manager, level + 1); + } else { + GenerateDefaultOptsCompletion(out, manager); + } + + L << ";;"; + } + } + } + L << "esac"; + } + L << "fi"; + } + + void TBashCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager&, size_t level) { + auto unorderedOpts = opts.GetOpts(); + + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& opt : unorderedOpts) { if (opt->IsHidden()) { continue; } - for (auto& shortName : opt->GetShortNames()) { - line << sep << "-" << B(TStringBuf(&shortName, 1)); - sep = " "; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "--" << B(longName); - sep = " "; - } - } - line << "' -- ${cur}) )"; - } - L << "else"; - { - I; - L << "case ${prev} in"; - { - I; - for (auto& opt : unorderedOpts) { + for (auto& shortName : opt->GetShortNames()) { + line << sep << "-" << B(TStringBuf(&shortName, 1)); + sep = " "; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "--" << B(longName); + sep = " "; + } + } + line << "' -- ${cur}) )"; + } + L << "else"; + { + I; + L << "case ${prev} in"; + { + I; + for (auto& opt : unorderedOpts) { if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) { - continue; - } - - auto& line = L; - TStringBuf sep = ""; - for (auto& shortName : opt->GetShortNames()) { - line << sep << "'-" << B(TStringBuf(&shortName, 1)) << "'"; - sep = "|"; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "'--" << B(longName) << "'"; - sep = "|"; - } - line << ")"; - { - I; - if (opt->Completer_ != nullptr) { - opt->Completer_->GenerateBash(out); - } - L << ";;"; - } - } - - L << "*)"; - { - I; - - L << "args=0"; - auto& line = L << "opts='@("; - TStringBuf sep = ""; - for (auto& opt : unorderedOpts) { + continue; + } + + auto& line = L; + TStringBuf sep = ""; + for (auto& shortName : opt->GetShortNames()) { + line << sep << "'-" << B(TStringBuf(&shortName, 1)) << "'"; + sep = "|"; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "'--" << B(longName) << "'"; + sep = "|"; + } + line << ")"; + { + I; + if (opt->Completer_ != nullptr) { + opt->Completer_->GenerateBash(out); + } + L << ";;"; + } + } + + L << "*)"; + { + I; + + L << "args=0"; + auto& line = L << "opts='@("; + TStringBuf sep = ""; + for (auto& opt : unorderedOpts) { if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) { - continue; - } - for (auto& shortName : opt->GetShortNames()) { - line << sep << "-" << B(TStringBuf(&shortName, 1)); - sep = "|"; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "--" << B(longName); - sep = "|"; - } - } - line << ")'"; - L << "for (( i=" << level << "; i < cword; i++ )); do"; - { - I; - L << "if [[ ${words[i]} != -* && ${words[i-1]} != $opts ]]; then"; - { - I; - L << "(( args++ ))"; - } - L << "fi"; - } - L << "done"; - L; - - auto argSpecs = opts.GetFreeArgSpecs(); - size_t numFreeArgs = opts.GetFreeArgsMax(); - bool unlimitedArgs = false; - if (numFreeArgs == TOpts::UNLIMITED_ARGS) { - numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); - unlimitedArgs = true; - } - - L << "case ${args} in"; - { - I; - - for (size_t i = 0; i < numFreeArgs; ++i) { - L << i << ")"; - { - I; - auto& spec = argSpecs[i]; - if (spec.Completer_ != nullptr) { - spec.Completer_->GenerateBash(out); - } - L << ";;"; - } - } - if (unlimitedArgs) { - L << "*)"; - { - I; - auto& spec = opts.GetTrailingArgSpec(); - if (spec.Completer_ != nullptr) { - spec.Completer_->GenerateBash(out); - } - L << ";;"; - } - } - } - L << "esac"; - L << ";;"; - } - } - L << "esac"; - } - L << "fi"; - } - - void TBashCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - L << "COMPREPLY+=( $(compgen -W '-h --help --svnrevision' -- ${cur}) )"; - } - L << "fi"; - } - -#undef I -#undef L - - TString NEscaping::Q(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size()); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\\': - out << "\\\\"; - break; - case '\'': - out << "''"; - break; - case '\"': - out << "\\\""; - break; - case '[': - out << "\\["; - break; - case ']': - out << "\\]"; - break; - case ':': - out << "\\:"; - break; - case '+': - out << "\\+"; - break; - case '=': - out << "\\="; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::QQ(TStringBuf string) { - auto q = Q(string); - return "'" + q + "'"; - } - - TString NEscaping::C(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "''"; - break; - case ':': - out << "\\:"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::CC(TStringBuf string) { - auto c = C(string); - return "'" + c + "'"; - } - - TString NEscaping::S(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "''"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::SS(TStringBuf string) { - auto s = S(string); - return "'" + s + "'"; - } - - TString NEscaping::B(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "'\"'\"'"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::BB(TStringBuf string) { - auto b = B(string); - return "'" + b + "'"; - } -} + continue; + } + for (auto& shortName : opt->GetShortNames()) { + line << sep << "-" << B(TStringBuf(&shortName, 1)); + sep = "|"; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "--" << B(longName); + sep = "|"; + } + } + line << ")'"; + L << "for (( i=" << level << "; i < cword; i++ )); do"; + { + I; + L << "if [[ ${words[i]} != -* && ${words[i-1]} != $opts ]]; then"; + { + I; + L << "(( args++ ))"; + } + L << "fi"; + } + L << "done"; + L; + + auto argSpecs = opts.GetFreeArgSpecs(); + size_t numFreeArgs = opts.GetFreeArgsMax(); + bool unlimitedArgs = false; + if (numFreeArgs == TOpts::UNLIMITED_ARGS) { + numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); + unlimitedArgs = true; + } + + L << "case ${args} in"; + { + I; + + for (size_t i = 0; i < numFreeArgs; ++i) { + L << i << ")"; + { + I; + auto& spec = argSpecs[i]; + if (spec.Completer_ != nullptr) { + spec.Completer_->GenerateBash(out); + } + L << ";;"; + } + } + if (unlimitedArgs) { + L << "*)"; + { + I; + auto& spec = opts.GetTrailingArgSpec(); + if (spec.Completer_ != nullptr) { + spec.Completer_->GenerateBash(out); + } + L << ";;"; + } + } + } + L << "esac"; + L << ";;"; + } + } + L << "esac"; + } + L << "fi"; + } + + void TBashCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + L << "COMPREPLY+=( $(compgen -W '-h --help --svnrevision' -- ${cur}) )"; + } + L << "fi"; + } + +#undef I +#undef L + + TString NEscaping::Q(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size()); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\\': + out << "\\\\"; + break; + case '\'': + out << "''"; + break; + case '\"': + out << "\\\""; + break; + case '[': + out << "\\["; + break; + case ']': + out << "\\]"; + break; + case ':': + out << "\\:"; + break; + case '+': + out << "\\+"; + break; + case '=': + out << "\\="; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::QQ(TStringBuf string) { + auto q = Q(string); + return "'" + q + "'"; + } + + TString NEscaping::C(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "''"; + break; + case ':': + out << "\\:"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::CC(TStringBuf string) { + auto c = C(string); + return "'" + c + "'"; + } + + TString NEscaping::S(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "''"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::SS(TStringBuf string) { + auto s = S(string); + return "'" + s + "'"; + } + + TString NEscaping::B(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "'\"'\"'"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::BB(TStringBuf string) { + auto b = B(string); + return "'" + b + "'"; + } +} diff --git a/library/cpp/getopt/small/completion_generator.h b/library/cpp/getopt/small/completion_generator.h index 4241bb7d6c..ac076e4d90 100644 --- a/library/cpp/getopt/small/completion_generator.h +++ b/library/cpp/getopt/small/completion_generator.h @@ -1,69 +1,69 @@ -#pragma once - -#include "completer.h" -#include "formatted_output.h" -#include "last_getopt_opts.h" -#include "modchooser.h" - -#include <util/generic/variant.h> -#include <util/string/builder.h> - -namespace NLastGetopt { - class TCompletionGenerator { - public: - explicit TCompletionGenerator(const TModChooser* modChooser); - explicit TCompletionGenerator(const TOpts* opts); - virtual ~TCompletionGenerator() = default; - - public: - virtual void Generate(TStringBuf command, IOutputStream& stream) = 0; - - protected: +#pragma once + +#include "completer.h" +#include "formatted_output.h" +#include "last_getopt_opts.h" +#include "modchooser.h" + +#include <util/generic/variant.h> +#include <util/string/builder.h> + +namespace NLastGetopt { + class TCompletionGenerator { + public: + explicit TCompletionGenerator(const TModChooser* modChooser); + explicit TCompletionGenerator(const TOpts* opts); + virtual ~TCompletionGenerator() = default; + + public: + virtual void Generate(TStringBuf command, IOutputStream& stream) = 0; + + protected: std::variant<const TModChooser*, const TOpts*> Options_; - }; - - class TZshCompletionGenerator: public TCompletionGenerator { - public: - using TCompletionGenerator::TCompletionGenerator; - - public: - void Generate(TStringBuf command, IOutputStream& stream) override; - - private: - static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager); - static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager); - static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); - static void GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager); - }; - - class TBashCompletionGenerator: public TCompletionGenerator { - public: - using TCompletionGenerator::TCompletionGenerator; - - public: - void Generate(TStringBuf command, IOutputStream& stream) override; - - private: - static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level); - static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager, size_t level); - static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); - }; - - namespace NEscaping { - /// Escape ':', '-', '=', '[', ']' for use in zsh _arguments - TString Q(TStringBuf string); - TString QQ(TStringBuf string); - - /// Escape colons for use in zsh _alternative and _arguments - TString C(TStringBuf string); - TString CC(TStringBuf string); - - /// Simple escape for use in zsh single-quoted strings - TString S(TStringBuf string); - TString SS(TStringBuf string); - - /// Simple escape for use in bash single-quoted strings - TString B(TStringBuf string); - TString BB(TStringBuf string); - } -} + }; + + class TZshCompletionGenerator: public TCompletionGenerator { + public: + using TCompletionGenerator::TCompletionGenerator; + + public: + void Generate(TStringBuf command, IOutputStream& stream) override; + + private: + static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager); + static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager); + static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); + static void GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager); + }; + + class TBashCompletionGenerator: public TCompletionGenerator { + public: + using TCompletionGenerator::TCompletionGenerator; + + public: + void Generate(TStringBuf command, IOutputStream& stream) override; + + private: + static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level); + static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager, size_t level); + static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); + }; + + namespace NEscaping { + /// Escape ':', '-', '=', '[', ']' for use in zsh _arguments + TString Q(TStringBuf string); + TString QQ(TStringBuf string); + + /// Escape colons for use in zsh _alternative and _arguments + TString C(TStringBuf string); + TString CC(TStringBuf string); + + /// Simple escape for use in zsh single-quoted strings + TString S(TStringBuf string); + TString SS(TStringBuf string); + + /// Simple escape for use in bash single-quoted strings + TString B(TStringBuf string); + TString BB(TStringBuf string); + } +} diff --git a/library/cpp/getopt/small/formatted_output.cpp b/library/cpp/getopt/small/formatted_output.cpp index bf1b366f25..fe355c2723 100644 --- a/library/cpp/getopt/small/formatted_output.cpp +++ b/library/cpp/getopt/small/formatted_output.cpp @@ -1,36 +1,36 @@ -#include "formatted_output.h" - -namespace NLastGetopt { - - TFormattedOutput::IndentGuard::IndentGuard(TFormattedOutput* output) - : Output(output) - { - Output->IndentLevel_ += 2; - } - - TFormattedOutput::IndentGuard::~IndentGuard() { - Output->IndentLevel_ -= 2; - } - - TFormattedOutput::IndentGuard TFormattedOutput::Indent() { - return IndentGuard(this); - } - - TStringBuilder& TFormattedOutput::Line() { - return Lines_.emplace_back(IndentLevel_, TStringBuilder()).second; - } - - void TFormattedOutput::Print(IOutputStream& out) { - for (auto&[indent, line] : Lines_) { - if (indent && !line.empty()) { - TTempBuf buf(indent); - Fill(buf.Data(), buf.Data() + indent, ' '); - out.Write(buf.Data(), indent); - } - out << line; - if (!line.EndsWith('\n')) { - out << Endl; - } - } - } -} +#include "formatted_output.h" + +namespace NLastGetopt { + + TFormattedOutput::IndentGuard::IndentGuard(TFormattedOutput* output) + : Output(output) + { + Output->IndentLevel_ += 2; + } + + TFormattedOutput::IndentGuard::~IndentGuard() { + Output->IndentLevel_ -= 2; + } + + TFormattedOutput::IndentGuard TFormattedOutput::Indent() { + return IndentGuard(this); + } + + TStringBuilder& TFormattedOutput::Line() { + return Lines_.emplace_back(IndentLevel_, TStringBuilder()).second; + } + + void TFormattedOutput::Print(IOutputStream& out) { + for (auto&[indent, line] : Lines_) { + if (indent && !line.empty()) { + TTempBuf buf(indent); + Fill(buf.Data(), buf.Data() + indent, ' '); + out.Write(buf.Data(), indent); + } + out << line; + if (!line.EndsWith('\n')) { + out << Endl; + } + } + } +} diff --git a/library/cpp/getopt/small/formatted_output.h b/library/cpp/getopt/small/formatted_output.h index 6fd16b73f9..f239dd7fdb 100644 --- a/library/cpp/getopt/small/formatted_output.h +++ b/library/cpp/getopt/small/formatted_output.h @@ -1,32 +1,32 @@ -#pragma once - -#include <util/generic/algorithm.h> -#include <util/generic/vector.h> -#include <util/string/builder.h> -#include <util/memory/tempbuf.h> - -namespace NLastGetopt { - /// Utility for printing indented lines. Used by completion generators. - class TFormattedOutput { - public: - struct IndentGuard { - explicit IndentGuard(TFormattedOutput* output); - virtual ~IndentGuard(); - TFormattedOutput* Output; - }; - - public: - /// Increase indentation and return a RAII object that'll decrease it back automatically. - IndentGuard Indent(); - - /// Append a new indented line to the stream. - TStringBuilder& Line(); - - /// Collect all lines into a stream. - void Print(IOutputStream& out); - - private: - int IndentLevel_ = 0; - TVector<std::pair<int, TStringBuilder>> Lines_; - }; -} +#pragma once + +#include <util/generic/algorithm.h> +#include <util/generic/vector.h> +#include <util/string/builder.h> +#include <util/memory/tempbuf.h> + +namespace NLastGetopt { + /// Utility for printing indented lines. Used by completion generators. + class TFormattedOutput { + public: + struct IndentGuard { + explicit IndentGuard(TFormattedOutput* output); + virtual ~IndentGuard(); + TFormattedOutput* Output; + }; + + public: + /// Increase indentation and return a RAII object that'll decrease it back automatically. + IndentGuard Indent(); + + /// Append a new indented line to the stream. + TStringBuilder& Line(); + + /// Collect all lines into a stream. + void Print(IOutputStream& out); + + private: + int IndentLevel_ = 0; + TVector<std::pair<int, TStringBuilder>> Lines_; + }; +} diff --git a/library/cpp/getopt/small/last_getopt.h b/library/cpp/getopt/small/last_getopt.h index 07687bc914..0540f69313 100644 --- a/library/cpp/getopt/small/last_getopt.h +++ b/library/cpp/getopt/small/last_getopt.h @@ -40,7 +40,7 @@ namespace NLastGetopt { TValue last = mutableValue ? NPrivate::OptFromString<TValue>(mutableValue, parser->CurOpt()) : first; if (last < first) { - throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(parser->CurOpt()) << " value " << TString(val).Quote() << ": the second argument is less than the first one"; + throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(parser->CurOpt()) << " value " << TString(val).Quote() << ": the second argument is less than the first one"; } for (++last; first < last; ++first) { @@ -100,7 +100,7 @@ namespace NLastGetopt { if (curval.IsInited()) { TStringBuf key, value; if (!curval.TrySplit(KVDelim, key, value)) { - throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(curOpt) + throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(curOpt) << " value " << TString(curval).Quote() << ": expected key" << KVDelim << "value format"; } Func(NPrivate::OptFromString<TKey>(key, curOpt), NPrivate::OptFromString<TValue>(value, curOpt)); @@ -119,10 +119,10 @@ namespace NLastGetopt { const TpArg& arg = curval.IsInited() ? OptFromString<TpArg>(curval, parser->CurOpt()) : Def_; try { Func_(arg); - } catch (const TUsageException&) { - throw; + } catch (const TUsageException&) { + throw; } catch (...) { - throw TUsageException() << "failed to handle opt " << OptToString(parser->CurOpt()) + throw TUsageException() << "failed to handle opt " << OptToString(parser->CurOpt()) << " value " << TString(curval).Quote() << ": " << CurrentExceptionMessage(); } } diff --git a/library/cpp/getopt/small/last_getopt_opt.cpp b/library/cpp/getopt/small/last_getopt_opt.cpp index 9a99437f4b..2839cca53f 100644 --- a/library/cpp/getopt/small/last_getopt_opt.cpp +++ b/library/cpp/getopt/small/last_getopt_opt.cpp @@ -54,7 +54,7 @@ namespace NLastGetopt { TOpt& TOpt::AddShortName(unsigned char c) { ; if (!IsAllowedShortName(c)) - throw TUsageException() << "option char '" << c << "' is not allowed"; + throw TUsageException() << "option char '" << c << "' is not allowed"; Chars_.push_back(c); return *this; } @@ -75,7 +75,7 @@ namespace NLastGetopt { ; unsigned char c = 0; if (!IsAllowedLongName(name, &c)) - throw TUsageException() << "option char '" << c + throw TUsageException() << "option char '" << c << "' in long '" << name << "' is not allowed"; LongNames_.push_back(name); return *this; @@ -102,12 +102,12 @@ namespace NLastGetopt { } } - TOpt& TOpt::IfPresentDisableCompletionFor(const TOpt& opt) { - if (opt.GetLongNames()) { - IfPresentDisableCompletionFor(opt.GetName()); - } else { - IfPresentDisableCompletionFor(opt.GetChar()); - } - return *this; - } + TOpt& TOpt::IfPresentDisableCompletionFor(const TOpt& opt) { + if (opt.GetLongNames()) { + IfPresentDisableCompletionFor(opt.GetName()); + } else { + IfPresentDisableCompletionFor(opt.GetChar()); + } + return *this; + } } diff --git a/library/cpp/getopt/small/last_getopt_opt.h b/library/cpp/getopt/small/last_getopt_opt.h index a8dd5adca9..a4e97e6a0a 100644 --- a/library/cpp/getopt/small/last_getopt_opt.h +++ b/library/cpp/getopt/small/last_getopt_opt.h @@ -1,6 +1,6 @@ #pragma once -#include "completer.h" +#include "completer.h" #include "last_getopt_handlers.h" #include <util/string/split.h> @@ -54,24 +54,24 @@ namespace NLastGetopt { typedef TVector<TSimpleSharedPtr<IOptHandler>> TOptHandlers; public: - bool Hidden_ = false; // is visible in help - TString ArgTitle_; // the name of argument in help output - TString Help_; // the help string - TString CompletionHelp_; // the help string that's used in completion script, a shorter version of Help_ - TString CompletionArgHelp_; // the description of argument in completion script + bool Hidden_ = false; // is visible in help + TString ArgTitle_; // the name of argument in help output + TString Help_; // the help string + TString CompletionHelp_; // the help string that's used in completion script, a shorter version of Help_ + TString CompletionArgHelp_; // the description of argument in completion script EHasArg HasArg_ = DEFAULT_HAS_ARG; // the argument parsing politics bool Required_ = false; // option existence politics - bool AllowMultipleCompletion_ = false; // let the completer know that this option can occur more than once - - bool DisableCompletionForOptions_ = false; - bool DisableCompletionForFreeArgs_ = false; - TShortNames DisableCompletionForChar_; - TLongNames DisableCompletionForLongName_; - TVector<size_t> DisableCompletionForFreeArg_; - NComp::ICompleterPtr Completer_; - + bool AllowMultipleCompletion_ = false; // let the completer know that this option can occur more than once + + bool DisableCompletionForOptions_ = false; + bool DisableCompletionForFreeArgs_ = false; + TShortNames DisableCompletionForChar_; + TLongNames DisableCompletionForLongName_; + TVector<size_t> DisableCompletionForFreeArg_; + NComp::ICompleterPtr Completer_; + private: //Handlers information const void* UserValue_ = nullptr; @@ -335,37 +335,37 @@ namespace NLastGetopt { } /** - * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used - * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't - * end it with a full stop. - * - * Note that `Help`, `CompletionHelp` and `CompletionArgHelp` are not the same. `Help` is printed in program - * usage (when you call `program --help`), `CompletionHelp` is printed when completer lists available - * options, and `CompletionArgHelp` is printed when completer shows available values for the option. - * - * Example of good help message: - * - * ``` - * opts.AddLongOption('t', "timeout") - * .Help("specify query timeout in milliseconds") - * .CompletionHelp("specify query timeout") - * .CompletionArgHelp("query timeout (ms) [default=500]"); - * ``` - * - * Notice how `Help` and `CompletionArgHelp` have units in them, but `CompletionHelp` don't. - * - * Another good example is the help option: - * - * ``` - * opts.AddLongOption('h', "help") - * .Help("print this message and exit") - * .CompletionHelp("print help message and exit"); - * ``` - * - * Notice how `Help` mentions 'this message', but `CompletionHelp` mentions just 'help message'. - * - * See more on completion descriptions codestyle: - * https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide#L43 + * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used + * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't + * end it with a full stop. + * + * Note that `Help`, `CompletionHelp` and `CompletionArgHelp` are not the same. `Help` is printed in program + * usage (when you call `program --help`), `CompletionHelp` is printed when completer lists available + * options, and `CompletionArgHelp` is printed when completer shows available values for the option. + * + * Example of good help message: + * + * ``` + * opts.AddLongOption('t', "timeout") + * .Help("specify query timeout in milliseconds") + * .CompletionHelp("specify query timeout") + * .CompletionArgHelp("query timeout (ms) [default=500]"); + * ``` + * + * Notice how `Help` and `CompletionArgHelp` have units in them, but `CompletionHelp` don't. + * + * Another good example is the help option: + * + * ``` + * opts.AddLongOption('h', "help") + * .Help("print this message and exit") + * .CompletionHelp("print help message and exit"); + * ``` + * + * Notice how `Help` mentions 'this message', but `CompletionHelp` mentions just 'help message'. + * + * See more on completion descriptions codestyle: + * https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide#L43 */ TOpt& Help(const TString& help) { Help_ = help; @@ -373,178 +373,178 @@ namespace NLastGetopt { } /** - * Get help string. + * Get help string. */ - const TString& GetHelp() const { + const TString& GetHelp() const { return Help_; } /** - * Set help string that appears when argument completer lists available options. - * - * See `Help` function for info on how this is different from setting `Help` and `CompletionArgHelp`. - * - * Use shorter messages for this message. Don't start them with a capital letter and don't end them - * with a full stop. De aware that argument name and default value will not be printed by completer. - * - * In zsh, these messages will look like this: - * - * ``` - * $ program -<tab><tab> - * -- option -- - * --help -h -- print help message and exit - * --timeout -t -- specify query timeout - * ``` - */ - TOpt& CompletionHelp(const TString& help) { - CompletionHelp_ = help; - return *this; - } - - /** - * Get help string that appears when argument completer lists available options. - */ - const TString& GetCompletionHelp() const { - return CompletionHelp_ ? CompletionHelp_ : Help_; - } - - /** - * Set help string that appears when completer suggests available values. - * - * See `Help` function for info on how this is different from setting `Help` and `CompletionHelp`. - * - * In zsh, these messages will look like this: - * - * ``` - * $ program --timeout <tab><tab> - * -- query timeout (ms) [default=500] -- - * 50 100 250 500 1000 - * ``` - */ - TOpt& CompletionArgHelp(const TString& help) { - CompletionArgHelp_ = help; - return *this; - } - - /** - * @return argument help string for use in completion script. - */ - const TString& GetCompletionArgHelp() const { - return CompletionArgHelp_ ? CompletionArgHelp_ : ArgTitle_; - } - - /** - * Let the completer know that this option can occur more than once. - */ - TOpt& AllowMultipleCompletion(bool allowMultipleCompletion = true) { - AllowMultipleCompletion_ = allowMultipleCompletion; - return *this; - } - - /** - * @return true if completer will offer completion for this option multiple times. - */ - bool MultipleCompletionAllowed() const { - return AllowMultipleCompletion_; - } - - /** - * Tell the completer to disable further completion if this option is present. - * This is useful for options like `--help`. - * - * Note: this only works in zsh. - * - * @return self - */ - TOpt& IfPresentDisableCompletion(bool value = true) { - IfPresentDisableCompletionForOptions(value); - IfPresentDisableCompletionForFreeArgs(value); - return *this; - } - - /** - * Tell the completer to disable completion for all options if this option is already present in the input. - * Free arguments will still be completed. - * - * Note: this only works in zsh. - * - * @return self - */ - TOpt& IfPresentDisableCompletionForOptions(bool value = true) { - DisableCompletionForOptions_ = value; - return *this; - } - - /** - * Tell the completer to disable option `c` if this option is already present in the input. - * For example, if you have two options `-a` and `-r` that are mutually exclusive, disable `-r` for `-a` and - * disable `-a` for `-r`, like this: - * - * ``` - * opts.AddLongOption('a', "acquire").IfPresentDisableCompletionFor('r'); - * opts.AddLongOption('r', "release").IfPresentDisableCompletionFor('a'); - * ``` - * - * This way, if user enabled option `-a`, completer will not suggest option `-r`. - * - * Note that we don't have to disable all flags for a single option. That is, disabling `-r` in the above - * example disables `--release` automatically. - * - * Note: this only works in zsh. - * - * @param c char option that should be disabled when completer hits this option. - */ - TOpt& IfPresentDisableCompletionFor(char c) { - DisableCompletionForChar_.push_back(c); - return *this; - } - - /** - * Like `IfPresentDisableCompletionFor(char c)`, but for long options. - */ - TOpt& IfPresentDisableCompletionFor(const TString& name) { - DisableCompletionForLongName_.push_back(name); - return *this; - } - - /** - * Like `IfPresentDisableCompletionFor(char c)`, but for long options. - */ - TOpt& IfPresentDisableCompletionFor(const TOpt& opt); - - /** - * Tell the completer to disable completion for the given free argument if this option is present. - * - * Note: this only works in zsh. - * - * @param arg index of free arg - */ - TOpt& IfPresentDisableCompletionForFreeArg(size_t index) { - DisableCompletionForFreeArg_.push_back(index); - return *this; - } - - /** - * Assign a completer for this option. - */ - TOpt& Completer(NComp::ICompleterPtr completer) { - Completer_ = std::move(completer); - return *this; - } - - /** - * Tell the completer to disable completion for the all free arguments if this option is present. - * - * Note: this only works in zsh. - */ - TOpt& IfPresentDisableCompletionForFreeArgs(bool value = true) { - DisableCompletionForFreeArgs_ = value; - return *this; - } - - /** - * Run handlers for this option. - */ + * Set help string that appears when argument completer lists available options. + * + * See `Help` function for info on how this is different from setting `Help` and `CompletionArgHelp`. + * + * Use shorter messages for this message. Don't start them with a capital letter and don't end them + * with a full stop. De aware that argument name and default value will not be printed by completer. + * + * In zsh, these messages will look like this: + * + * ``` + * $ program -<tab><tab> + * -- option -- + * --help -h -- print help message and exit + * --timeout -t -- specify query timeout + * ``` + */ + TOpt& CompletionHelp(const TString& help) { + CompletionHelp_ = help; + return *this; + } + + /** + * Get help string that appears when argument completer lists available options. + */ + const TString& GetCompletionHelp() const { + return CompletionHelp_ ? CompletionHelp_ : Help_; + } + + /** + * Set help string that appears when completer suggests available values. + * + * See `Help` function for info on how this is different from setting `Help` and `CompletionHelp`. + * + * In zsh, these messages will look like this: + * + * ``` + * $ program --timeout <tab><tab> + * -- query timeout (ms) [default=500] -- + * 50 100 250 500 1000 + * ``` + */ + TOpt& CompletionArgHelp(const TString& help) { + CompletionArgHelp_ = help; + return *this; + } + + /** + * @return argument help string for use in completion script. + */ + const TString& GetCompletionArgHelp() const { + return CompletionArgHelp_ ? CompletionArgHelp_ : ArgTitle_; + } + + /** + * Let the completer know that this option can occur more than once. + */ + TOpt& AllowMultipleCompletion(bool allowMultipleCompletion = true) { + AllowMultipleCompletion_ = allowMultipleCompletion; + return *this; + } + + /** + * @return true if completer will offer completion for this option multiple times. + */ + bool MultipleCompletionAllowed() const { + return AllowMultipleCompletion_; + } + + /** + * Tell the completer to disable further completion if this option is present. + * This is useful for options like `--help`. + * + * Note: this only works in zsh. + * + * @return self + */ + TOpt& IfPresentDisableCompletion(bool value = true) { + IfPresentDisableCompletionForOptions(value); + IfPresentDisableCompletionForFreeArgs(value); + return *this; + } + + /** + * Tell the completer to disable completion for all options if this option is already present in the input. + * Free arguments will still be completed. + * + * Note: this only works in zsh. + * + * @return self + */ + TOpt& IfPresentDisableCompletionForOptions(bool value = true) { + DisableCompletionForOptions_ = value; + return *this; + } + + /** + * Tell the completer to disable option `c` if this option is already present in the input. + * For example, if you have two options `-a` and `-r` that are mutually exclusive, disable `-r` for `-a` and + * disable `-a` for `-r`, like this: + * + * ``` + * opts.AddLongOption('a', "acquire").IfPresentDisableCompletionFor('r'); + * opts.AddLongOption('r', "release").IfPresentDisableCompletionFor('a'); + * ``` + * + * This way, if user enabled option `-a`, completer will not suggest option `-r`. + * + * Note that we don't have to disable all flags for a single option. That is, disabling `-r` in the above + * example disables `--release` automatically. + * + * Note: this only works in zsh. + * + * @param c char option that should be disabled when completer hits this option. + */ + TOpt& IfPresentDisableCompletionFor(char c) { + DisableCompletionForChar_.push_back(c); + return *this; + } + + /** + * Like `IfPresentDisableCompletionFor(char c)`, but for long options. + */ + TOpt& IfPresentDisableCompletionFor(const TString& name) { + DisableCompletionForLongName_.push_back(name); + return *this; + } + + /** + * Like `IfPresentDisableCompletionFor(char c)`, but for long options. + */ + TOpt& IfPresentDisableCompletionFor(const TOpt& opt); + + /** + * Tell the completer to disable completion for the given free argument if this option is present. + * + * Note: this only works in zsh. + * + * @param arg index of free arg + */ + TOpt& IfPresentDisableCompletionForFreeArg(size_t index) { + DisableCompletionForFreeArg_.push_back(index); + return *this; + } + + /** + * Assign a completer for this option. + */ + TOpt& Completer(NComp::ICompleterPtr completer) { + Completer_ = std::move(completer); + return *this; + } + + /** + * Tell the completer to disable completion for the all free arguments if this option is present. + * + * Note: this only works in zsh. + */ + TOpt& IfPresentDisableCompletionForFreeArgs(bool value = true) { + DisableCompletionForFreeArgs_ = value; + return *this; + } + + /** + * Run handlers for this option. + */ void FireHandlers(const TOptsParser* parser) const; private: @@ -716,97 +716,97 @@ namespace NLastGetopt { */ struct TFreeArgSpec { TFreeArgSpec() = default; - TFreeArgSpec(const TString& title, const TString& help = TString(), bool optional = false) - : Title_(title) - , Help_(help) - , Optional_(optional) + TFreeArgSpec(const TString& title, const TString& help = TString(), bool optional = false) + : Title_(title) + , Help_(help) + , Optional_(optional) { } - TString Title_; - TString Help_; - TString CompletionArgHelp_; - - bool Optional_ = false; - NComp::ICompleterPtr Completer_ = nullptr; - - /** - * Check if this argument have default values for its title and help. - */ - bool IsDefault() const { - return Title_.empty() && Help_.empty(); - } - - /** - * Set argument title. - */ - TFreeArgSpec& Title(TString title) { - Title_ = std::move(title); - return *this; - } - - /** - * Get argument title. If title is empty, returns a default one. - */ - TStringBuf GetTitle(TStringBuf defaultTitle) const { - return Title_ ? TStringBuf(Title_) : defaultTitle; - } - - /** - * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used - * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't - * end it with a full stop. - * - * See `TOpt::Help` function for more on how `Help` and `CompletionArgHelp` differ one from another. - */ - TFreeArgSpec& Help(TString help) { - Help_ = std::move(help); - return *this; - } - - /** - * Get help string that appears with `--help`. - */ - TStringBuf GetHelp() const { - return Help_; - } - - /** - * Set help string that appears when completer suggests values fot this argument. - */ - TFreeArgSpec& CompletionArgHelp(TString completionArgHelp) { - CompletionArgHelp_ = std::move(completionArgHelp); - return *this; - } - - /** - * Get help string that appears when completer suggests values fot this argument. - */ - TStringBuf GetCompletionArgHelp(TStringBuf defaultTitle) const { - return CompletionArgHelp_ ? TStringBuf(CompletionArgHelp_) : GetTitle(defaultTitle); - } - - /** - * Mark this argument as optional. This setting only affects help printing, it doesn't affect parsing. - */ - TFreeArgSpec& Optional(bool optional = true) { - Optional_ = optional; - return *this; - } - - /** - * Check if this argument is optional. - */ - bool IsOptional() const { - return Optional_; - } - - /** - * Set completer for this argument. - */ - TFreeArgSpec& Completer(NComp::ICompleterPtr completer) { - Completer_ = std::move(completer); - return *this; - } + TString Title_; + TString Help_; + TString CompletionArgHelp_; + + bool Optional_ = false; + NComp::ICompleterPtr Completer_ = nullptr; + + /** + * Check if this argument have default values for its title and help. + */ + bool IsDefault() const { + return Title_.empty() && Help_.empty(); + } + + /** + * Set argument title. + */ + TFreeArgSpec& Title(TString title) { + Title_ = std::move(title); + return *this; + } + + /** + * Get argument title. If title is empty, returns a default one. + */ + TStringBuf GetTitle(TStringBuf defaultTitle) const { + return Title_ ? TStringBuf(Title_) : defaultTitle; + } + + /** + * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used + * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't + * end it with a full stop. + * + * See `TOpt::Help` function for more on how `Help` and `CompletionArgHelp` differ one from another. + */ + TFreeArgSpec& Help(TString help) { + Help_ = std::move(help); + return *this; + } + + /** + * Get help string that appears with `--help`. + */ + TStringBuf GetHelp() const { + return Help_; + } + + /** + * Set help string that appears when completer suggests values fot this argument. + */ + TFreeArgSpec& CompletionArgHelp(TString completionArgHelp) { + CompletionArgHelp_ = std::move(completionArgHelp); + return *this; + } + + /** + * Get help string that appears when completer suggests values fot this argument. + */ + TStringBuf GetCompletionArgHelp(TStringBuf defaultTitle) const { + return CompletionArgHelp_ ? TStringBuf(CompletionArgHelp_) : GetTitle(defaultTitle); + } + + /** + * Mark this argument as optional. This setting only affects help printing, it doesn't affect parsing. + */ + TFreeArgSpec& Optional(bool optional = true) { + Optional_ = optional; + return *this; + } + + /** + * Check if this argument is optional. + */ + bool IsOptional() const { + return Optional_; + } + + /** + * Set completer for this argument. + */ + TFreeArgSpec& Completer(NComp::ICompleterPtr completer) { + Completer_ = std::move(completer); + return *this; + } }; } diff --git a/library/cpp/getopt/small/last_getopt_opts.cpp b/library/cpp/getopt/small/last_getopt_opts.cpp index 03c432849f..936489c200 100644 --- a/library/cpp/getopt/small/last_getopt_opts.cpp +++ b/library/cpp/getopt/small/last_getopt_opts.cpp @@ -1,12 +1,12 @@ -#include "completer_command.h" +#include "completer_command.h" #include "last_getopt_opts.h" -#include "wrap.h" -#include "last_getopt_parser.h" +#include "wrap.h" +#include "last_getopt_parser.h" #include <library/cpp/colorizer/colors.h> #include <util/stream/format.h> -#include <util/charset/utf8.h> +#include <util/charset/utf8.h> #include <stdlib.h> @@ -70,7 +70,7 @@ namespace NLastGetopt { , AllowUnknownCharOptions_(false) , AllowUnknownLongOptions_(false) , FreeArgsMin_(0) - , FreeArgsMax_(UNLIMITED_ARGS) + , FreeArgsMax_(UNLIMITED_ARGS) { if (!optstring.empty()) { AddCharOptions(optstring); @@ -225,33 +225,33 @@ namespace NLastGetopt { return *Opts_.back(); } - TOpt& TOpts::AddCompletionOption(TString command, TString longName) { - if (TOpt* o = FindLongOption(longName)) { - return *o; - } - - return AddOption(MakeCompletionOpt(this, std::move(command), std::move(longName))); - } - - namespace { - auto MutuallyExclusiveHandler(const TOpt* cur, const TOpt* other) { - return [cur, other](const TOptsParser* p) { - if (p->Seen(other)) { - throw TUsageException() - << "option " << cur->ToShortString() - << " can't appear together with option " << other->ToShortString(); - } - }; - } - } - - void TOpts::MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2) { - opt1.Handler1(MutuallyExclusiveHandler(&opt1, &opt2)) - .IfPresentDisableCompletionFor(opt2); - opt2.Handler1(MutuallyExclusiveHandler(&opt2, &opt1)) - .IfPresentDisableCompletionFor(opt1); - } - + TOpt& TOpts::AddCompletionOption(TString command, TString longName) { + if (TOpt* o = FindLongOption(longName)) { + return *o; + } + + return AddOption(MakeCompletionOpt(this, std::move(command), std::move(longName))); + } + + namespace { + auto MutuallyExclusiveHandler(const TOpt* cur, const TOpt* other) { + return [cur, other](const TOptsParser* p) { + if (p->Seen(other)) { + throw TUsageException() + << "option " << cur->ToShortString() + << " can't appear together with option " << other->ToShortString(); + } + }; + } + } + + void TOpts::MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2) { + opt1.Handler1(MutuallyExclusiveHandler(&opt1, &opt2)) + .IfPresentDisableCompletionFor(opt2); + opt2.Handler1(MutuallyExclusiveHandler(&opt2, &opt1)) + .IfPresentDisableCompletionFor(opt1); + } + size_t TOpts::IndexOf(const TOpt* opt) const { TOptsVector::const_iterator it = std::find(Opts_.begin(), Opts_.end(), opt); if (it == Opts_.end()) @@ -259,19 +259,19 @@ namespace NLastGetopt { return it - Opts_.begin(); } - TStringBuf TOpts::GetFreeArgTitle(size_t pos) const { + TStringBuf TOpts::GetFreeArgTitle(size_t pos) const { if (FreeArgSpecs_.contains(pos)) { - return FreeArgSpecs_.at(pos).GetTitle(DefaultFreeArgTitle_); + return FreeArgSpecs_.at(pos).GetTitle(DefaultFreeArgTitle_); } - return DefaultFreeArgTitle_; + return DefaultFreeArgTitle_; } - void TOpts::SetFreeArgTitle(size_t pos, const TString& title, const TString& help, bool optional) { - FreeArgSpecs_[pos] = TFreeArgSpec(title, help, optional); + void TOpts::SetFreeArgTitle(size_t pos, const TString& title, const TString& help, bool optional) { + FreeArgSpecs_[pos] = TFreeArgSpec(title, help, optional); } - TFreeArgSpec& TOpts::GetFreeArgSpec(size_t pos) { - return FreeArgSpecs_[pos]; + TFreeArgSpec& TOpts::GetFreeArgSpec(size_t pos) { + return FreeArgSpecs_[pos]; } static TString FormatOption(const TOpt* option, const NColorizer::TColors& colors) { @@ -325,29 +325,29 @@ namespace NLastGetopt { } os << "[OPTIONS]"; - ui32 numDescribedFlags = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; - ui32 numArgsToShow = Max(FreeArgsMin_, FreeArgsMax_ == UNLIMITED_ARGS ? numDescribedFlags : FreeArgsMax_); - - for (ui32 i = 0, nonOptionalFlagsPrinted = 0; i < numArgsToShow; ++i) { - bool isOptional = nonOptionalFlagsPrinted >= FreeArgsMin_ || FreeArgSpecs_.Value(i, TFreeArgSpec()).Optional_; - - nonOptionalFlagsPrinted += !isOptional; - - os << " "; - - if (isOptional) - os << "["; - - os << GetFreeArgTitle(i); - - if (isOptional) - os << "]"; - } - - if (FreeArgsMax_ == UNLIMITED_ARGS) { - os << " [" << TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_) << "]..."; - } - + ui32 numDescribedFlags = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; + ui32 numArgsToShow = Max(FreeArgsMin_, FreeArgsMax_ == UNLIMITED_ARGS ? numDescribedFlags : FreeArgsMax_); + + for (ui32 i = 0, nonOptionalFlagsPrinted = 0; i < numArgsToShow; ++i) { + bool isOptional = nonOptionalFlagsPrinted >= FreeArgsMin_ || FreeArgSpecs_.Value(i, TFreeArgSpec()).Optional_; + + nonOptionalFlagsPrinted += !isOptional; + + os << " "; + + if (isOptional) + os << "["; + + os << GetFreeArgTitle(i); + + if (isOptional) + os << "]"; + } + + if (FreeArgsMax_ == UNLIMITED_ARGS) { + os << " [" << TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_) << "]..."; + } + os << Endl; } @@ -361,7 +361,7 @@ namespace NLastGetopt { TVector<TString> leftColumn(Opts_.size()); TVector<size_t> leftColumnSizes(leftColumn.size()); - const size_t kMaxLeftWidth = 25; + const size_t kMaxLeftWidth = 25; size_t leftWidth = 0; size_t requiredOptionsCount = 0; NColorizer::TColors disabledColors(false); @@ -371,14 +371,14 @@ namespace NLastGetopt { if (opt->IsHidden()) continue; leftColumn[i] = FormatOption(opt, colors); - size_t leftColumnSize = leftColumn[i].size(); - if (colors.IsTTY()) { - leftColumnSize -= NColorizer::TotalAnsiEscapeCodeLen(leftColumn[i]); - } + size_t leftColumnSize = leftColumn[i].size(); + if (colors.IsTTY()) { + leftColumnSize -= NColorizer::TotalAnsiEscapeCodeLen(leftColumn[i]); + } leftColumnSizes[i] = leftColumnSize; - if (leftColumnSize <= kMaxLeftWidth) { - leftWidth = Max(leftWidth, leftColumnSize); - } + if (leftColumnSize <= kMaxLeftWidth) { + leftWidth = Max(leftWidth, leftColumnSize); + } if (opt->IsRequired()) requiredOptionsCount++; } @@ -410,59 +410,59 @@ namespace NLastGetopt { continue; if (leftColumnSizes[i] > leftWidth && !opt->GetHelp().empty()) { - os << SPad << leftColumn[i] << Endl << SPad << leftPadding << ' '; + os << SPad << leftColumn[i] << Endl << SPad << leftPadding << ' '; } else { os << SPad << leftColumn[i] << ' '; if (leftColumnSizes[i] < leftWidth) os << TStringBuf(leftPadding.data(), leftWidth - leftColumnSizes[i]); } - TStringBuf help = opt->GetHelp(); - while (help && isspace(help.back())) { - help.Chop(1); - } - size_t lastLineLength = 0; - bool helpHasParagraphs = false; - if (help) { - os << Wrap(Wrap_, help, SPad + leftPadding + " ", &lastLineLength, &helpHasParagraphs); + TStringBuf help = opt->GetHelp(); + while (help && isspace(help.back())) { + help.Chop(1); } + size_t lastLineLength = 0; + bool helpHasParagraphs = false; + if (help) { + os << Wrap(Wrap_, help, SPad + leftPadding + " ", &lastLineLength, &helpHasParagraphs); + } if (opt->HasDefaultValue()) { - auto quotedDef = QuoteForHelp(opt->GetDefaultValue()); - if (helpHasParagraphs) { - os << Endl << Endl << SPad << leftPadding << " "; - os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; - } else if (help.EndsWith('.')) { - os << Endl << SPad << leftPadding << " "; - os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; - } else if (help) { - if (SPad.size() + leftWidth + 1 + lastLineLength + 12 + quotedDef.size() > Wrap_) { - os << Endl << SPad << leftPadding << " "; - } else { - os << " "; - } - os << "(default: " << colors.CyanColor() << quotedDef << colors.OldColor() << ")"; - } else { - os << "default: " << colors.CyanColor() << quotedDef << colors.OldColor(); - } + auto quotedDef = QuoteForHelp(opt->GetDefaultValue()); + if (helpHasParagraphs) { + os << Endl << Endl << SPad << leftPadding << " "; + os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; + } else if (help.EndsWith('.')) { + os << Endl << SPad << leftPadding << " "; + os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; + } else if (help) { + if (SPad.size() + leftWidth + 1 + lastLineLength + 12 + quotedDef.size() > Wrap_) { + os << Endl << SPad << leftPadding << " "; + } else { + os << " "; + } + os << "(default: " << colors.CyanColor() << quotedDef << colors.OldColor() << ")"; + } else { + os << "default: " << colors.CyanColor() << quotedDef << colors.OldColor(); + } } os << Endl; - - if (helpHasParagraphs) { - os << Endl; - } + + if (helpHasParagraphs) { + os << Endl; + } } } - + PrintFreeArgsDesc(os, colors); - - for (auto& [heading, text] : Sections) { - os << Endl << colors.BoldColor() << heading << colors.OldColor() << ":" << Endl; - - os << SPad << Wrap(Wrap_, text, SPad) << Endl; - } - + + for (auto& [heading, text] : Sections) { + os << Endl << colors.BoldColor() << heading << colors.OldColor() << ":" << Endl; + + os << SPad << Wrap(Wrap_, text, SPad) << Endl; + } + osIn << os.Str(); } @@ -479,8 +479,8 @@ namespace NLastGetopt { leftFreeWidth = Max(leftFreeWidth, GetFreeArgTitle(i).size()); } - if (!TrailingArgSpec_.IsDefault()) { - leftFreeWidth = Max(leftFreeWidth, TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_).size()); + if (!TrailingArgSpec_.IsDefault()) { + leftFreeWidth = Max(leftFreeWidth, TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_).size()); } leftFreeWidth = Min(leftFreeWidth, size_t(30)); @@ -488,32 +488,32 @@ namespace NLastGetopt { os << " min: " << colors.GreenColor() << FreeArgsMin_ << colors.OldColor() << ","; os << " max: " << colors.GreenColor(); - if (FreeArgsMax_ != UNLIMITED_ARGS) { + if (FreeArgsMax_ != UNLIMITED_ARGS) { os << FreeArgsMax_; } else { os << "unlimited"; } - os << colors.OldColor() << Endl; + os << colors.OldColor() << Endl; const size_t limit = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first; for (size_t i = 0; i <= limit; ++i) { - if (!FreeArgSpecs_.contains(i)) { - continue; - } - - if (auto help = FreeArgSpecs_.at(i).GetHelp()) { - auto title = GetFreeArgTitle(i); - os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() - << SPad << help << Endl; - } - } - - if (FreeArgsMax_ == UNLIMITED_ARGS) { - auto title = TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_); - if (auto help = TrailingArgSpec_.GetHelp()) { - os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() - << SPad << help << Endl; - } + if (!FreeArgSpecs_.contains(i)) { + continue; + } + + if (auto help = FreeArgSpecs_.at(i).GetHelp()) { + auto title = GetFreeArgTitle(i); + os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() + << SPad << help << Endl; + } + } + + if (FreeArgsMax_ == UNLIMITED_ARGS) { + auto title = TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_); + if (auto help = TrailingArgSpec_.GetHelp()) { + os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() + << SPad << help << Endl; + } } } } diff --git a/library/cpp/getopt/small/last_getopt_opts.h b/library/cpp/getopt/small/last_getopt_opts.h index 825b99c871..fe4f3b9a7d 100644 --- a/library/cpp/getopt/small/last_getopt_opts.h +++ b/library/cpp/getopt/small/last_getopt_opts.h @@ -15,32 +15,32 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOpts is a storage of program options' parse rules. - * It contains information about all options, free args, some parsing options - * and rules about interaction between options. - * - * The main point for defining program options. - * - * The parsing rules determined by the following parts: - * - Arguments permutation. It is expected free args be after named args. - * This point adjusts how to treat breaking this expectation. - * if REQUIRE_ORDER is choosen, the exception during parsing will be raised, - * the special string " -- " will be treated as end of named - * options: all options after it will be parsed as free args - * if PERMUTE is choosen, arguments will be rearranged in correct order, - * if RETURN_IN_ORDER is choosen, all free args will be ommited (TODO: looks very strange) - * - Using '+' as a prefix instead '--' for long names - * - Using "-" as a prefix for both short and long names - * - Allowing unknown options - * - */ + * NLastGetopt::TOpts is a storage of program options' parse rules. + * It contains information about all options, free args, some parsing options + * and rules about interaction between options. + * + * The main point for defining program options. + * + * The parsing rules determined by the following parts: + * - Arguments permutation. It is expected free args be after named args. + * This point adjusts how to treat breaking this expectation. + * if REQUIRE_ORDER is choosen, the exception during parsing will be raised, + * the special string " -- " will be treated as end of named + * options: all options after it will be parsed as free args + * if PERMUTE is choosen, arguments will be rearranged in correct order, + * if RETURN_IN_ORDER is choosen, all free args will be ommited (TODO: looks very strange) + * - Using '+' as a prefix instead '--' for long names + * - Using "-" as a prefix for both short and long names + * - Allowing unknown options + * + */ class TOpts { friend class TOptsParseResult; friend class TOptsParser; public: - static constexpr const ui32 UNLIMITED_ARGS = Max<ui32>(); - + static constexpr const ui32 UNLIMITED_ARGS = Max<ui32>(); + typedef TVector<TSimpleSharedPtr<TOpt>> TOptsVector; TOptsVector Opts_; // infomation about named (short and long) options TVector<std::function<void(TStringBuf)>> ArgBindings_; @@ -53,32 +53,32 @@ namespace NLastGetopt { bool AllowUnknownCharOptions_ = false; bool AllowUnknownLongOptions_ = false; - ui32 Wrap_ = 80; - + ui32 Wrap_ = 80; + private: ui32 FreeArgsMin_; // minimal number of free args ui32 FreeArgsMax_; // maximal number of free args - TMap<ui32, TFreeArgSpec> FreeArgSpecs_; // mapping [free arg position] -> [free arg specification] - TFreeArgSpec TrailingArgSpec_; // spec for the trailing argument (when arguments are unlimited) - TString DefaultFreeArgTitle_ = "ARG"; // title that's used for free args without a title + TMap<ui32, TFreeArgSpec> FreeArgSpecs_; // mapping [free arg position] -> [free arg specification] + TFreeArgSpec TrailingArgSpec_; // spec for the trailing argument (when arguments are unlimited) + TString DefaultFreeArgTitle_ = "ARG"; // title that's used for free args without a title TString Title; // title of the help string TString CustomCmdLineDescr; // user defined help string TString CustomUsage; // user defined usage string - TVector<std::pair<TString, TString>> Sections; // additional help entries to print after usage - + TVector<std::pair<TString, TString>> Sections; // additional help entries to print after usage + public: /** - * Constructs TOpts from string as in getopt(3) - */ + * Constructs TOpts from string as in getopt(3) + */ TOpts(const TStringBuf& optstring = TStringBuf()); /** - * Constructs TOpts from string as in getopt(3) and - * additionally adds help option (for '?') and svn-verstion option (for 'V') - */ + * Constructs TOpts from string as in getopt(3) and + * additionally adds help option (for '?') and svn-verstion option (for 'V') + */ static TOpts Default(const TStringBuf& optstring = TStringBuf()) { TOpts opts(optstring); opts.AddHelpOption(); @@ -87,161 +87,161 @@ namespace NLastGetopt { } /** - * Checks correctness of options' descriptions. - * Throws TConfException if validation failed. - * Check consist of: - * -not intersecting of names - * -compability of settings, that responsable for freeArgs parsing - */ + * Checks correctness of options' descriptions. + * Throws TConfException if validation failed. + * Check consist of: + * -not intersecting of names + * -compability of settings, that responsable for freeArgs parsing + */ void Validate() const; /** - * Search for the option with given long name - * @param name long name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ptr on result (nullptr if not found) + */ const TOpt* FindLongOption(const TStringBuf& name) const; /** - * Search for the option with given short name - * @param c short name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ptr on result (nullptr if not found) + */ const TOpt* FindCharOption(char c) const; /** - * Search for the option with given long name - * @param name long name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ptr on result (nullptr if not found) + */ TOpt* FindLongOption(const TStringBuf& name); /** - * Search for the option with given short name - * @param c short name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ptr on result (nullptr if not found) + */ TOpt* FindCharOption(char c); /** - * Search for the option with given name - * @param name name for search - * @return ptr on result (nullptr if not found) - */ - /// @{ - - const TOpt* FindOption(const TStringBuf& name) const { - return FindLongOption(name); - } - - TOpt* FindOption(const TStringBuf& name) { - return FindLongOption(name); - } - - const TOpt* FindOption(char c) const { - return FindCharOption(c); - } - - TOpt* FindOption(char c) { - return FindCharOption(c); - } - - /// @} - - /** - * Sets title of the help string - * @param title title to set - */ + * Search for the option with given name + * @param name name for search + * @return ptr on result (nullptr if not found) + */ + /// @{ + + const TOpt* FindOption(const TStringBuf& name) const { + return FindLongOption(name); + } + + TOpt* FindOption(const TStringBuf& name) { + return FindLongOption(name); + } + + const TOpt* FindOption(char c) const { + return FindCharOption(c); + } + + TOpt* FindOption(char c) { + return FindCharOption(c); + } + + /// @} + + /** + * Sets title of the help string + * @param title title to set + */ void SetTitle(const TString& title) { Title = title; } /** - * @return true if there is an option with given long name - * - * @param name long name for search - */ + * @return true if there is an option with given long name + * + * @param name long name for search + */ bool HasLongOption(const TString& name) const { return FindLongOption(name) != nullptr; } /** - * @return true if there is an option with given short name - * - * @param char short name for search - */ + * @return true if there is an option with given short name + * + * @param char short name for search + */ bool HasCharOption(char c) const { return FindCharOption(c) != nullptr; } /** - * Search for the option with given long name - * @param name long name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ref on result (throw exception if not found) + */ const TOpt& GetLongOption(const TStringBuf& name) const; /** - * Search for the option with given long name - * @param name long name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ref on result (throw exception if not found) + */ TOpt& GetLongOption(const TStringBuf& name); /** - * Search for the option with given short name - * @param c short name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ref on result (throw exception if not found) + */ const TOpt& GetCharOption(char c) const; /** - * Search for the option with given short name - * @param c short name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ref on result (throw exception if not found) + */ TOpt& GetCharOption(char c); /** - * Search for the option with given name - * @param name name for search - * @return ref on result (throw exception if not found) - */ - /// @{ - - const TOpt& GetOption(const TStringBuf& name) const { - return GetLongOption(name); - } - - TOpt& GetOption(const TStringBuf& name) { - return GetLongOption(name); - } - - const TOpt& GetOption(char c) const { - return GetCharOption(c); - } - - TOpt& GetOption(char c) { - return GetCharOption(c); - } - - /// @} - - /** - * @return true if short options exist - */ + * Search for the option with given name + * @param name name for search + * @return ref on result (throw exception if not found) + */ + /// @{ + + const TOpt& GetOption(const TStringBuf& name) const { + return GetLongOption(name); + } + + TOpt& GetOption(const TStringBuf& name) { + return GetLongOption(name); + } + + const TOpt& GetOption(char c) const { + return GetCharOption(c); + } + + TOpt& GetOption(char c) { + return GetCharOption(c); + } + + /// @} + + /** + * @return true if short options exist + */ bool HasAnyShortOption() const; /** - * @return true if long options exist - */ + * @return true if long options exist + */ bool HasAnyLongOption() const; /** - * Creates new [option description (TOpt)] as a copy of given one - * @param option source - * @return reference for created option - */ + * Creates new [option description (TOpt)] as a copy of given one + * @param option source + * @return reference for created option + */ TOpt& AddOption(const TOpt& option); /** @@ -261,30 +261,30 @@ namespace NLastGetopt { } /** - * Creates options list from string as in getopt(3) - * - * @param optstring source - */ + * Creates options list from string as in getopt(3) + * + * @param optstring source + */ void AddCharOptions(const TStringBuf& optstring); /** - * Creates new [option description (TOpt)] with given short name and given help string - * - * @param c short name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given short name and given help string + * + * @param c short name + * @param help help string + * @return reference for created option + */ TOpt& AddCharOption(char c, const TString& help = "") { return AddCharOption(c, DEFAULT_HAS_ARG, help); } /** - * Creates new [option description (TOpt)] with given short name and given help string - * - * @param c short name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given short name and given help string + * + * @param c short name + * @param help help string + * @return reference for created option + */ TOpt& AddCharOption(char c, EHasArg hasArg, const TString& help = "") { TOpt option; option.AddShortName(c); @@ -294,24 +294,24 @@ namespace NLastGetopt { } /** - * Creates new [option description (TOpt)] with given long name and given help string - * - * @param name long name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given long name and given help string + * + * @param name long name + * @param help help string + * @return reference for created option + */ TOpt& AddLongOption(const TString& name, const TString& help = "") { return AddLongOption(0, name, help); } /** - * Creates new [option description (TOpt)] with given long and short names and given help string - * - * @param c short name - * @param name long name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given long and short names and given help string + * + * @param c short name + * @param name long name + * @param help help string + * @return reference for created option + */ TOpt& AddLongOption(char c, const TString& name, const TString& help = "") { TOpt option; if (c != 0) @@ -322,12 +322,12 @@ namespace NLastGetopt { } /** - * Creates new [option description (TOpt)] for help printing, - * adds appropriate handler for it - * If "help" option already exist, will add given short name to it. - * - * @param c new short name for help option - */ + * Creates new [option description (TOpt)] for help printing, + * adds appropriate handler for it + * If "help" option already exist, will add given short name to it. + * + * @param c new short name for help option + */ TOpt& AddHelpOption(char c = '?') { if (TOpt* o = FindLongOption("help")) { if (!o->CharIs(c)) @@ -336,17 +336,17 @@ namespace NLastGetopt { } return AddLongOption(c, "help", "print usage") .HasArg(NO_ARGUMENT) - .IfPresentDisableCompletion() + .IfPresentDisableCompletion() .Handler(&PrintUsageAndExit); } /** - * Creates new [option description (TOpt)] for svn-revision printing, - * adds appropriate handler for it. - * If "svnversion" option already exist, will add given short name to it. - * - * @param c new short name for "svnversion" option - */ + * Creates new [option description (TOpt)] for svn-revision printing, + * adds appropriate handler for it. + * If "svnversion" option already exist, will add given short name to it. + * + * @param c new short name for "svnversion" option + */ TOpt& AddVersionOption(char c = 'V') { if (TOpt* o = FindLongOption("svnrevision")) { if (!o->CharIs(c)) @@ -355,22 +355,22 @@ namespace NLastGetopt { } return AddLongOption(c, "svnrevision", "print svn version") .HasArg(NO_ARGUMENT) - .IfPresentDisableCompletion() + .IfPresentDisableCompletion() .Handler(&PrintVersionAndExit); } /** - * Creates new option for generating completion shell scripts. - * - * @param command name of command that should be completed (typically corresponds to the executable name). - */ - TOpt& AddCompletionOption(TString command, TString longName = "completion"); - - /** - * Creates or finds option with given short name - * - * @param c new short name for search/create - */ + * Creates new option for generating completion shell scripts. + * + * @param command name of command that should be completed (typically corresponds to the executable name). + */ + TOpt& AddCompletionOption(TString command, TString longName = "completion"); + + /** + * Creates or finds option with given short name + * + * @param c new short name for search/create + */ TOpt& CharOption(char c) { const TOpt* opt = FindCharOption(c); if (opt != nullptr) { @@ -382,99 +382,99 @@ namespace NLastGetopt { } /** - * Indicate that some options can't appear together. - * - * Note: this is not transitive. - * - * Note: don't use this on options with default values. If option with default value wasn't specified, - * parser will run handlers for default value, thus triggering a false-positive exclusivity check. - */ - template <typename T1, typename T2> - void MutuallyExclusive(T1&& opt1, T2&& opt2) { - MutuallyExclusiveOpt(GetOption(std::forward<T1>(opt1)), GetOption(std::forward<T2>(opt2))); - } - - /** - * Like `MutuallyExclusive`, but accepts `TOpt`s instead of option names. - */ - void MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2); - - /** - * @return index of option - * - * @param opt pointer of option to search - */ + * Indicate that some options can't appear together. + * + * Note: this is not transitive. + * + * Note: don't use this on options with default values. If option with default value wasn't specified, + * parser will run handlers for default value, thus triggering a false-positive exclusivity check. + */ + template <typename T1, typename T2> + void MutuallyExclusive(T1&& opt1, T2&& opt2) { + MutuallyExclusiveOpt(GetOption(std::forward<T1>(opt1)), GetOption(std::forward<T2>(opt2))); + } + + /** + * Like `MutuallyExclusive`, but accepts `TOpt`s instead of option names. + */ + void MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2); + + /** + * @return index of option + * + * @param opt pointer of option to search + */ size_t IndexOf(const TOpt* opt) const; /** - * Replace help string with given - * - * @param decr new help string - */ + * Replace help string with given + * + * @param decr new help string + */ void SetCmdLineDescr(const TString& descr) { CustomCmdLineDescr = descr; } /** - * Replace usage string with given - * - * @param usage new usage string - */ + * Replace usage string with given + * + * @param usage new usage string + */ void SetCustomUsage(const TString& usage) { CustomUsage = usage; } /** - * Add a section to print after the main usage spec. - */ - void AddSection(TString title, TString text) { - Sections.emplace_back(std::move(title), std::move(text)); - } - - /** - * Add section with examples. - * - * @param examples text of this section - */ - void SetExamples(TString examples) { - AddSection("Examples", std::move(examples)); - } - - /** - * Set minimal number of free args - * - * @param min new value - */ + * Add a section to print after the main usage spec. + */ + void AddSection(TString title, TString text) { + Sections.emplace_back(std::move(title), std::move(text)); + } + + /** + * Add section with examples. + * + * @param examples text of this section + */ + void SetExamples(TString examples) { + AddSection("Examples", std::move(examples)); + } + + /** + * Set minimal number of free args + * + * @param min new value + */ void SetFreeArgsMin(size_t min) { FreeArgsMin_ = ui32(min); } - - /** - * Get current minimal number of free args - */ - ui32 GetFreeArgsMin() const { - return FreeArgsMin_; - } - + /** - * Set maximal number of free args - * + * Get current minimal number of free args + */ + ui32 GetFreeArgsMin() const { + return FreeArgsMin_; + } + + /** + * Set maximal number of free args + * * @param max new value - */ + */ void SetFreeArgsMax(size_t max) { FreeArgsMax_ = ui32(max); FreeArgsMax_ = Max<ui32>(FreeArgsMax_, ArgBindings_.size()); } /** - * Get current maximal number of free args - */ - ui32 GetFreeArgsMax() const { - return FreeArgsMax_; - } - - /** + * Get current maximal number of free args + */ + ui32 GetFreeArgsMax() const { + return FreeArgsMax_; + } + + /** * Get mapping for free args */ const TMap<ui32, TFreeArgSpec>& GetFreeArgSpecs() const { @@ -482,161 +482,161 @@ namespace NLastGetopt { } /** - * Set exact expected number of free args - * - * @param count new value - */ + * Set exact expected number of free args + * + * @param count new value + */ void SetFreeArgsNum(size_t count) { FreeArgsMin_ = ui32(count); FreeArgsMax_ = ui32(count); } /** - * Set minimal and maximal number of free args - * - * @param min new value for minimal - * @param max new value for maximal - */ + * Set minimal and maximal number of free args + * + * @param min new value for minimal + * @param max new value for maximal + */ void SetFreeArgsNum(size_t min, size_t max) { FreeArgsMin_ = ui32(min); FreeArgsMax_ = ui32(max); } /** - * Set title and help string of free argument - * - * @param pos index of argument - * @param title new value for argument title - * @param help new value for help string - * @param optional indicates that the flag's help string should be rendered as for optional flag; - * does not affect actual flags parsing - */ - void SetFreeArgTitle(size_t pos, const TString& title, const TString& help = TString(), bool optional = false); - - /** - * Get free argument's spec for further modification. - */ - TFreeArgSpec& GetFreeArgSpec(size_t pos); - - /** - * Legacy, don't use. Same as `SetTrailingArgTitle`. - * Older versions of lastgetopt didn't have destinction between default title and title - * for the trailing argument. - */ - void SetFreeArgDefaultTitle(const TString& title, const TString& help = TString()) { - SetTrailingArgTitle(title, help); - } - - /** - * Set default title that will be used for all arguments that have no title. - */ - void SetDefaultFreeArgTitle(TString title) { - DefaultFreeArgTitle_ = std::move(title); - } - - /** - * Set default title that will be used for all arguments that have no title. - */ - const TString& GetDefaultFreeArgTitle() const { - return DefaultFreeArgTitle_; - } - - /** - * Set title and help for the trailing argument. - * - * This title and help are used to render the last repeated argument when max number of arguments is unlimited. - */ - /// @{ - void SetTrailingArgTitle(TString title) { - TrailingArgSpec_.Title(std::move(title)); - } - void SetTrailingArgTitle(TString title, TString help) { - TrailingArgSpec_.Title(std::move(title)); - TrailingArgSpec_.Help(std::move(help)); - } - /// @} - - /** - * Get spec for the trailing argument. - * - * This spec is used to render the last repeated argument when max number of arguments is unlimited. - */ - /// @{ - TFreeArgSpec& GetTrailingArgSpec() { - return TrailingArgSpec_; - } - const TFreeArgSpec& GetTrailingArgSpec() const { - return TrailingArgSpec_; - } - /// @} - - /** - * Set the rule of parsing single dash as prefix of long names - * - * @param value new value of the option - */ + * Set title and help string of free argument + * + * @param pos index of argument + * @param title new value for argument title + * @param help new value for help string + * @param optional indicates that the flag's help string should be rendered as for optional flag; + * does not affect actual flags parsing + */ + void SetFreeArgTitle(size_t pos, const TString& title, const TString& help = TString(), bool optional = false); + + /** + * Get free argument's spec for further modification. + */ + TFreeArgSpec& GetFreeArgSpec(size_t pos); + + /** + * Legacy, don't use. Same as `SetTrailingArgTitle`. + * Older versions of lastgetopt didn't have destinction between default title and title + * for the trailing argument. + */ + void SetFreeArgDefaultTitle(const TString& title, const TString& help = TString()) { + SetTrailingArgTitle(title, help); + } + + /** + * Set default title that will be used for all arguments that have no title. + */ + void SetDefaultFreeArgTitle(TString title) { + DefaultFreeArgTitle_ = std::move(title); + } + + /** + * Set default title that will be used for all arguments that have no title. + */ + const TString& GetDefaultFreeArgTitle() const { + return DefaultFreeArgTitle_; + } + + /** + * Set title and help for the trailing argument. + * + * This title and help are used to render the last repeated argument when max number of arguments is unlimited. + */ + /// @{ + void SetTrailingArgTitle(TString title) { + TrailingArgSpec_.Title(std::move(title)); + } + void SetTrailingArgTitle(TString title, TString help) { + TrailingArgSpec_.Title(std::move(title)); + TrailingArgSpec_.Help(std::move(help)); + } + /// @} + + /** + * Get spec for the trailing argument. + * + * This spec is used to render the last repeated argument when max number of arguments is unlimited. + */ + /// @{ + TFreeArgSpec& GetTrailingArgSpec() { + return TrailingArgSpec_; + } + const TFreeArgSpec& GetTrailingArgSpec() const { + return TrailingArgSpec_; + } + /// @} + + /** + * Set the rule of parsing single dash as prefix of long names + * + * @param value new value of the option + */ void SetAllowSingleDashForLong(bool value) { AllowSingleDashForLong_ = value; } /** - * Wrap help text at this number of characters. 0 to disable wrapping. - */ - void SetWrap(ui32 wrap = 80) { - Wrap_ = wrap; - } - - /** - * Print usage string - * - * @param program prefix of result (path to the program) - * @param os destination stream - * @param colors colorizer - */ + * Wrap help text at this number of characters. 0 to disable wrapping. + */ + void SetWrap(ui32 wrap = 80) { + Wrap_ = wrap; + } + + /** + * Print usage string + * + * @param program prefix of result (path to the program) + * @param os destination stream + * @param colors colorizer + */ void PrintUsage(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; /** - * Print usage string - * - * @param program prefix of result (path to the program) - * @param os destination stream - */ + * Print usage string + * + * @param program prefix of result (path to the program) + * @param os destination stream + */ void PrintUsage(const TStringBuf& program, IOutputStream& os = Cout) const; /** - * Get list of options in order of definition. - */ - TVector<const TOpt*> GetOpts() const { - auto ret = TVector<const TOpt*>(Reserve(Opts_.size())); - for (auto& opt : Opts_) { - ret.push_back(opt.Get()); - } - return ret; - } + * Get list of options in order of definition. + */ + TVector<const TOpt*> GetOpts() const { + auto ret = TVector<const TOpt*>(Reserve(Opts_.size())); + for (auto& opt : Opts_) { + ret.push_back(opt.Get()); + } + return ret; + } - private: + private: /** - * @return argument title of a free argument - * - * @param pos position of the argument - */ - TStringBuf GetFreeArgTitle(size_t pos) const; + * @return argument title of a free argument + * + * @param pos position of the argument + */ + TStringBuf GetFreeArgTitle(size_t pos) const; /** - * Print usage helper - * - * @param program prefix of result (path to the program) - * @param os destination stream - * @param colors colorizer - */ + * Print usage helper + * + * @param program prefix of result (path to the program) + * @param os destination stream + * @param colors colorizer + */ void PrintCmdLine(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; /** - * Print usage helper - * - * @param os destination stream - * @param colors colorizer - */ + * Print usage helper + * + * @param os destination stream + * @param colors colorizer + */ void PrintFreeArgsDesc(IOutputStream& os, const NColorizer::TColors& colors) const; }; diff --git a/library/cpp/getopt/small/last_getopt_parse_result.cpp b/library/cpp/getopt/small/last_getopt_parse_result.cpp index f4b5607a90..ac27736a14 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.cpp +++ b/library/cpp/getopt/small/last_getopt_parse_result.cpp @@ -42,7 +42,7 @@ namespace NLastGetopt { const TOptParseResult* r = FindOptParseResult(opt, includeDefault); if (!r || r->Empty()) { try { - throw TUsageException() << "option " << opt->ToShortString() << " is unspecified"; + throw TUsageException() << "option " << opt->ToShortString() << " is unspecified"; } catch (...) { HandleError(); // unreachable @@ -99,10 +99,10 @@ namespace NLastGetopt { return Parser_->ProgramName_; } - void TOptsParseResult::PrintUsage(IOutputStream& os) const { - Parser_->Opts_->PrintUsage(Parser_->ProgramName_, os); - } - + void TOptsParseResult::PrintUsage(IOutputStream& os) const { + Parser_->Opts_->PrintUsage(Parser_->ProgramName_, os); + } + size_t TOptsParseResult::GetFreeArgsPos() const { return Parser_->Pos_; } @@ -147,7 +147,7 @@ namespace NLastGetopt { if (Parser_->Opts_->FindLongOption("help") != nullptr) { Cerr << "Try '" << Parser_->ProgramName_ << " --help' for more information." << Endl; } else { - PrintUsage(); + PrintUsage(); } } exit(1); diff --git a/library/cpp/getopt/small/last_getopt_parse_result.h b/library/cpp/getopt/small/last_getopt_parse_result.h index 1ab6f598c9..a4baf8ca6b 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.h +++ b/library/cpp/getopt/small/last_getopt_parse_result.h @@ -1,15 +1,15 @@ #pragma once #include "last_getopt_opts.h" -#include "last_getopt_parser.h" +#include "last_getopt_parser.h" namespace NLastGetopt { /** - * NLastGetopt::TOptParseResult contains all arguments for exactly one TOpt, - * that have been fetched during parsing - * - * The class is a wraper over a vector of nil-terminated strings. - */ + * NLastGetopt::TOptParseResult contains all arguments for exactly one TOpt, + * that have been fetched during parsing + * + * The class is a wraper over a vector of nil-terminated strings. + */ class TOptParseResult { public: typedef TVector<const char*> TValues; @@ -56,14 +56,14 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOptsParseResult contains result of parsing argc,argv be parser. - * - * In most common case constructed by argc,argv pair and rules (TOpts). - * The instance being constructed validates rules and performs parsing, stores result for futher access. - * - * If error during parsing occures, the program aborts with exit code 1. - * Note, that if PERMUTE mode is on, then data, pointed by argv can be changed. - */ + * NLastGetopt::TOptsParseResult contains result of parsing argc,argv be parser. + * + * In most common case constructed by argc,argv pair and rules (TOpts). + * The instance being constructed validates rules and performs parsing, stores result for futher access. + * + * If error during parsing occures, the program aborts with exit code 1. + * Note, that if PERMUTE mode is on, then data, pointed by argv can be changed. + */ class TOptsParseResult { private: THolder<TOptsParser> Parser_; //The instance of parser. @@ -78,49 +78,49 @@ namespace NLastGetopt { TOptParseResult& OptParseResult(); /** - * Searchs for object in given container - * - * @param vec container - * @param opt ptr for required object - * - * @retunr ptr on corresponding TOptParseResult - */ + * Searchs for object in given container + * + * @param vec container + * @param opt ptr for required object + * + * @retunr ptr on corresponding TOptParseResult + */ static const TOptParseResult* FindParseResult(const TdVec& vec, const TOpt* opt); protected: /** - * Performs parsing of comand line arguments. - */ + * Performs parsing of comand line arguments. + */ void Init(const TOpts* options, int argc, const char** argv); TOptsParseResult() = default; public: /** - * The action in case of parser failure. - * Allows to asjust behavior in derived classes. - * By default prints error string and aborts the program - */ + * The action in case of parser failure. + * Allows to asjust behavior in derived classes. + * By default prints error string and aborts the program + */ virtual void HandleError() const; /** - * Constructs object by parsing arguments with given rules - * - * @param options ptr on parsing rules - * @param argc - * @param argv - */ + * Constructs object by parsing arguments with given rules + * + * @param options ptr on parsing rules + * @param argc + * @param argv + */ TOptsParseResult(const TOpts* options, int argc, const char* argv[]) { Init(options, argc, argv); } /** - * Constructs object by parsing arguments with given rules - * - * @param options ptr on parsing rules - * @param argc - * @param argv - */ + * Constructs object by parsing arguments with given rules + * + * @param options ptr on parsing rules + * @param argc + * @param argv + */ TOptsParseResult(const TOpts* options, int argc, char* argv[]) { Init(options, argc, const_cast<const char**>(argv)); } @@ -128,146 +128,146 @@ namespace NLastGetopt { virtual ~TOptsParseResult() = default; /** - * Search for TOptParseResult that corresponds to given option (TOpt) - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given option (TOpt) + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindOptParseResult(const TOpt* opt, bool includeDefault = false) const; /** - * Search for TOptParseResult that corresponds to given long name - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given long name + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindLongOptParseResult(const TString& name, bool includeDefault = false) const; /** - * Search for TOptParseResult that corresponds to given short name - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given short name + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindCharOptParseResult(char c, bool includeDefault = false) const; /** - * @return argv[0] - */ + * @return argv[0] + */ TString GetProgramName() const; /** - * Print usage string. - */ - void PrintUsage(IOutputStream& os = Cout) const; - - /** - * @return position in [premuted argv] of the first free argument - */ + * Print usage string. + */ + void PrintUsage(IOutputStream& os = Cout) const; + + /** + * @return position in [premuted argv] of the first free argument + */ size_t GetFreeArgsPos() const; /** - * @return number of fetched free arguments - */ + * @return number of fetched free arguments + */ size_t GetFreeArgCount() const; /** - * @return all fetched free arguments - */ + * @return all fetched free arguments + */ TVector<TString> GetFreeArgs() const; /** - * @return true if given option exist in results of parsing - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(const TOpt* opt, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + */ const char* Get(const TOpt* opt, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param opt ptr on required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param opt ptr on required object + * @param defaultValue + */ const char* GetOrElse(const TOpt* opt, const char* defaultValue) const; /** - * @return true if given option exist in results of parsing - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(const TString& name, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + */ const char* Get(const TString& name, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param name long name of required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param name long name of required object + * @param defaultValue + */ const char* GetOrElse(const TString& name, const char* defaultValue) const; /** - * @return true if given option exist in results of parsing - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(char name, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + */ const char* Get(char name, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param c short name of required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param c short name of required object + * @param defaultValue + */ const char* GetOrElse(char name, const char* defaultValue) const; /** - * for givne option return parsed value of the last fetched argument - * if option haven't been fetched, HandleError action is called - * - * @param opt required option (one of: ptr, short name, long name). - * - * @return FromString<T>(last feteched argument) - */ + * for givne option return parsed value of the last fetched argument + * if option haven't been fetched, HandleError action is called + * + * @param opt required option (one of: ptr, short name, long name). + * + * @return FromString<T>(last feteched argument) + */ template <typename T, typename TKey> T Get(const TKey opt) const { const char* value = Get(opt); @@ -280,14 +280,14 @@ namespace NLastGetopt { } /** - * for givne option return parsed value of the last fetched argument - * if option haven't been fetched, given defaultValue will be returned - * - * @param opt required option (one of: ptr, short name, long name). - * @param defaultValue - * - * @return FromString<T>(last feteched argument) - */ + * for givne option return parsed value of the last fetched argument + * if option haven't been fetched, given defaultValue will be returned + * + * @param opt required option (one of: ptr, short name, long name). + * @param defaultValue + * + * @return FromString<T>(last feteched argument) + */ template <typename T, typename TKey> T GetOrElse(const TKey opt, const T& defaultValue) const { if (Has(opt)) @@ -298,11 +298,11 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOptsParseResultException contains result of parsing argc,argv be parser. - * - * Unlike TOptsParseResult, if error during parsing occures, an exception is thrown. - * - */ + * NLastGetopt::TOptsParseResultException contains result of parsing argc,argv be parser. + * + * Unlike TOptsParseResult, if error during parsing occures, an exception is thrown. + * + */ class TOptsParseResultException: public TOptsParseResult { public: TOptsParseResultException(const TOpts* options, int argc, const char* argv[]) { diff --git a/library/cpp/getopt/small/last_getopt_parser.cpp b/library/cpp/getopt/small/last_getopt_parser.cpp index 7668b12a03..10eebc3c45 100644 --- a/library/cpp/getopt/small/last_getopt_parser.cpp +++ b/library/cpp/getopt/small/last_getopt_parser.cpp @@ -11,7 +11,7 @@ namespace NLastGetopt { Opts_ = opts; if (argc < 1) - throw TUsageException() << "argv must have at least one argument"; + throw TUsageException() << "argv must have at least one argument"; Argc_ = argc; Argv_ = argv; @@ -66,11 +66,11 @@ namespace NLastGetopt { Y_ASSERT(!Stopped_); if (Opts_->FreeArgsMin_ == Opts_->FreeArgsMax_ && Argc_ - Pos_ != Opts_->FreeArgsMin_) - throw TUsageException() << "required exactly " << Opts_->FreeArgsMin_ << " free args"; + throw TUsageException() << "required exactly " << Opts_->FreeArgsMin_ << " free args"; else if (Argc_ - Pos_ < Opts_->FreeArgsMin_) - throw TUsageException() << "required at least " << Opts_->FreeArgsMin_ << " free args"; + throw TUsageException() << "required at least " << Opts_->FreeArgsMin_ << " free args"; else if (Argc_ - Pos_ > Opts_->FreeArgsMax_) - throw TUsageException() << "required at most " << Opts_->FreeArgsMax_ << " free args"; + throw TUsageException() << "required at most " << Opts_->FreeArgsMax_ << " free args"; return false; } @@ -83,7 +83,7 @@ namespace NLastGetopt { Y_ASSERT(EIO_NONE != IsOpt(arg)); if (!Opts_->AllowUnknownCharOptions_) - throw TUsageException() << "unknown option '" << EscapeC(arg[sop]) + throw TUsageException() << "unknown option '" << EscapeC(arg[sop]) << "' in '" << arg << "'"; TempCurrentOpt_.Reset(new TOpt); @@ -152,13 +152,13 @@ namespace NLastGetopt { } else if (Opts_->AllowUnknownLongOptions_) { return false; } else { - throw TUsageException() << "unknown option '" << optionName + throw TUsageException() << "unknown option '" << optionName << "' in '" << Argv_[pos] << "'"; } } if (arg.IsInited()) { if (option->GetHasArg() == NO_ARGUMENT) - throw TUsageException() << "option " << optionName << " must have no arg"; + throw TUsageException() << "option " << optionName << " must have no arg"; return Commit(option, arg, pos + 1, 0); } ++pos; @@ -175,7 +175,7 @@ namespace NLastGetopt { } if (pos == Argc_) { if (opt->GetHasArg() == REQUIRED_ARGUMENT) - throw TUsageException() << "option " << opt->ToShortString() << " must have arg"; + throw TUsageException() << "option " << opt->ToShortString() << " must have arg"; return Commit(opt, nullptr, pos, 0); } const TStringBuf arg(Argv_[pos]); diff --git a/library/cpp/getopt/small/last_getopt_parser.h b/library/cpp/getopt/small/last_getopt_parser.h index 2cf8a6c308..19c34dce51 100644 --- a/library/cpp/getopt/small/last_getopt_parser.h +++ b/library/cpp/getopt/small/last_getopt_parser.h @@ -88,26 +88,26 @@ namespace NLastGetopt { /// fetch next argument, false if no more arguments left bool Next(); - bool Seen(const TOpt* opt) const { - return OptsSeen_.contains(opt); - } - - bool Seen(TStringBuf name) const { - if (auto opt = Opts_->FindLongOption(name)) { - return Seen(opt); - } else { - return false; - } - } - - bool Seen(char name) const { - if (auto opt = Opts_->FindCharOption(name)) { - return Seen(opt); - } else { - return false; - } - } - + bool Seen(const TOpt* opt) const { + return OptsSeen_.contains(opt); + } + + bool Seen(TStringBuf name) const { + if (auto opt = Opts_->FindLongOption(name)) { + return Seen(opt); + } else { + return false; + } + } + + bool Seen(char name) const { + if (auto opt = Opts_->FindCharOption(name)) { + return Seen(opt); + } else { + return false; + } + } + const TOpt* CurOpt() const { return CurrentOpt_; } diff --git a/library/cpp/getopt/small/last_getopt_support.h b/library/cpp/getopt/small/last_getopt_support.h index 17bed3e614..d0e0baf77a 100644 --- a/library/cpp/getopt/small/last_getopt_support.h +++ b/library/cpp/getopt/small/last_getopt_support.h @@ -16,13 +16,13 @@ namespace NLastGetopt { class TException: public yexception { }; - /// TOpts configuration is incorrect + /// TOpts configuration is incorrect class TConfException: public TException { }; - /// User passed incorrect arguments, parsing failed - /// Note: use `throw TUsageException()` instead of `ythrow TUsageException()` to prevent appearence of stacktrace - /// and location of the `ythrow` statment in error messages. + /// User passed incorrect arguments, parsing failed + /// Note: use `throw TUsageException()` instead of `ythrow TUsageException()` to prevent appearence of stacktrace + /// and location of the `ythrow` statment in error messages. class TUsageException: public TException { }; @@ -166,7 +166,7 @@ namespace NLastGetopt { try { return OptFromStringImpl<T>(value); } catch (...) { - throw TUsageException() << "failed to parse opt " << OptToString(opt) << " value " << TString(value).Quote() << ": " << CurrentExceptionMessage(); + throw TUsageException() << "failed to parse opt " << OptToString(opt) << " value " << TString(value).Quote() << ": " << CurrentExceptionMessage(); } } diff --git a/library/cpp/getopt/small/modchooser.cpp b/library/cpp/getopt/small/modchooser.cpp index 2fa5cfd070..64d18862be 100644 --- a/library/cpp/getopt/small/modchooser.cpp +++ b/library/cpp/getopt/small/modchooser.cpp @@ -1,7 +1,7 @@ -#include "completer.h" -#include "completer_command.h" -#include "completion_generator.h" -#include "last_getopt.h" +#include "completer.h" +#include "completer_command.h" +#include "completion_generator.h" +#include "last_getopt.h" #include "modchooser.h" #include <library/cpp/colorizer/colors.h> @@ -10,54 +10,54 @@ #include <util/stream/format.h> #include <util/generic/yexception.h> #include <util/generic/ptr.h> -#include <util/string/builder.h> -#include <util/string/join.h> +#include <util/string/builder.h> +#include <util/string/join.h> -class PtrWrapper: public TMainClass { +class PtrWrapper: public TMainClass { public: explicit PtrWrapper(const TMainFunctionPtr main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - return Main(argc, argv); + int operator()(const int argc, const char** argv) override { + return Main(argc, argv); } private: TMainFunctionPtr Main; }; -class PtrvWrapper: public TMainClass { +class PtrvWrapper: public TMainClass { public: explicit PtrvWrapper(const TMainFunctionPtrV main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - TVector<TString> nargv(argv, argv + argc); - return Main(nargv); + int operator()(const int argc, const char** argv) override { + TVector<TString> nargv(argv, argv + argc); + return Main(nargv); } private: TMainFunctionPtrV Main; }; -class ClassWrapper: public TMainClass { +class ClassWrapper: public TMainClass { public: - explicit ClassWrapper(TMainClassV* main) + explicit ClassWrapper(TMainClassV* main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - TVector<TString> nargv(argv, argv + argc); - return (*Main)(nargv); + int operator()(const int argc, const char** argv) override { + TVector<TString> nargv(argv, argv + argc); + return (*Main)(nargv); } private: - TMainClassV* Main; + TMainClassV* Main; }; TModChooser::TMode::TMode(const TString& name, TMainClass* main, const TString& descr, bool hidden, bool noCompletion) @@ -83,7 +83,7 @@ TModChooser::~TModChooser() = default; void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtr func, const TString& description, bool hidden, bool noCompletion) { AddMode(mode, TMainFunctionPtr(func), description, hidden, noCompletion); } - + void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtrV func, const TString& description, bool hidden, bool noCompletion) { AddMode(mode, TMainFunctionPtrV(func), description, hidden, noCompletion); } @@ -109,8 +109,8 @@ void TModChooser::AddMode(const TString& mode, TMainClass* func, const TString& void TModChooser::AddMode(const TString& mode, TMainClassV* func, const TString& description, bool hidden, bool noCompletion) { Wrappers.push_back(MakeHolder<ClassWrapper>(func)); AddMode(mode, Wrappers.back().Get(), description, hidden, noCompletion); -} - +} + void TModChooser::AddGroupModeDescription(const TString& description, bool hidden, bool noCompletion) { UnsortedModes.push_back(MakeHolder<TMode>(TString(), nullptr, description.data(), hidden, noCompletion)); } @@ -119,15 +119,15 @@ void TModChooser::SetDefaultMode(const TString& mode) { DefaultMode = mode; } -void TModChooser::AddAlias(const TString& alias, const TString& mode) { - if (!Modes.FindPtr(mode)) { - ythrow yexception() << "TMode '" << mode << "' not found in TModChooser."; - } - - Modes[mode]->Aliases.push_back(alias); - Modes[alias] = Modes[mode]; -} - +void TModChooser::AddAlias(const TString& alias, const TString& mode) { + if (!Modes.FindPtr(mode)) { + ythrow yexception() << "TMode '" << mode << "' not found in TModChooser."; + } + + Modes[mode]->Aliases.push_back(alias); + Modes[alias] = Modes[mode]; +} + void TModChooser::SetDescription(const TString& descr) { Description = descr; } @@ -157,18 +157,18 @@ void TModChooser::DisableSvnRevisionOption() { } void TModChooser::AddCompletions(TString progName, const TString& name, bool hidden, bool noCompletion) { - if (CompletionsGenerator == nullptr) { - CompletionsGenerator = NLastGetopt::MakeCompletionMod(this, std::move(progName), name); + if (CompletionsGenerator == nullptr) { + CompletionsGenerator = NLastGetopt::MakeCompletionMod(this, std::move(progName), name); AddMode(name, CompletionsGenerator.Get(), "generate autocompletion files", hidden, noCompletion); - } + } } -int TModChooser::Run(const int argc, const char** argv) const { - Y_ENSURE(argc, "Can't run TModChooser with empty list of arguments."); +int TModChooser::Run(const int argc, const char** argv) const { + Y_ENSURE(argc, "Can't run TModChooser with empty list of arguments."); bool shiftArgs = true; TString modeName; - if (argc == 1) { + if (argc == 1) { if (DefaultMode.empty()) { PrintHelp(argv[0]); return 0; @@ -205,89 +205,89 @@ int TModChooser::Run(const int argc, const char** argv) const { } if (shiftArgs) { - TString firstArg; + TString firstArg; TVector<const char*> nargv(Reserve(argc)); - + if (PrintShortCommandInUsage) { - firstArg = modeIter->second->Name; + firstArg = modeIter->second->Name; } else { - firstArg = argv[0] + TString(" ") + modeIter->second->Name; + firstArg = argv[0] + TString(" ") + modeIter->second->Name; } - nargv.push_back(firstArg.data()); - - for (int i = 2; i < argc; ++i) { + nargv.push_back(firstArg.data()); + + for (int i = 2; i < argc; ++i) { nargv.push_back(argv[i]); } // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1). // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336 nargv.push_back(nullptr); - + return (*modeIter->second->Main)(nargv.size() - 1, nargv.data()); } else { - return (*modeIter->second->Main)(argc, argv); + return (*modeIter->second->Main)(argc, argv); } } -int TModChooser::Run(const TVector<TString>& argv) const { +int TModChooser::Run(const TVector<TString>& argv) const { TVector<const char*> nargv(Reserve(argv.size() + 1)); - for (auto& arg : argv) { - nargv.push_back(arg.c_str()); - } + for (auto& arg : argv) { + nargv.push_back(arg.c_str()); + } // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1). // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336 nargv.push_back(nullptr); return Run(nargv.size() - 1, nargv.data()); -} - -size_t TModChooser::TMode::CalculateFullNameLen() const { - size_t len = Name.size(); - if (Aliases) { - len += 2; - for (auto& alias : Aliases) { - len += alias.size() + 1; - } - } - return len; -} - -TString TModChooser::TMode::FormatFullName(size_t pad) const { - TStringBuilder name; - if (Aliases) { - name << "{"; - } - - name << NColorizer::StdErr().GreenColor(); - name << Name; - name << NColorizer::StdErr().OldColor(); - - if (Aliases) { - for (const auto& alias : Aliases) { - name << "|" << NColorizer::StdErr().GreenColor() << alias << NColorizer::StdErr().OldColor(); - } - name << "}"; - } - - auto len = CalculateFullNameLen(); - if (pad > len) { - name << TString(" ") * (pad - len); - } - - return name; -} - +} + +size_t TModChooser::TMode::CalculateFullNameLen() const { + size_t len = Name.size(); + if (Aliases) { + len += 2; + for (auto& alias : Aliases) { + len += alias.size() + 1; + } + } + return len; +} + +TString TModChooser::TMode::FormatFullName(size_t pad) const { + TStringBuilder name; + if (Aliases) { + name << "{"; + } + + name << NColorizer::StdErr().GreenColor(); + name << Name; + name << NColorizer::StdErr().OldColor(); + + if (Aliases) { + for (const auto& alias : Aliases) { + name << "|" << NColorizer::StdErr().GreenColor() << alias << NColorizer::StdErr().OldColor(); + } + name << "}"; + } + + auto len = CalculateFullNameLen(); + if (pad > len) { + name << TString(" ") * (pad - len); + } + + return name; +} + void TModChooser::PrintHelp(const TString& progName) const { - Cerr << Description << Endl << Endl; + Cerr << Description << Endl << Endl; Cerr << NColorizer::StdErr().BoldColor() << "Usage" << NColorizer::StdErr().OldColor() << ": " << progName << " MODE [MODE_OPTIONS]" << Endl; Cerr << Endl; Cerr << NColorizer::StdErr().BoldColor() << "Modes" << NColorizer::StdErr().OldColor() << ":" << Endl; size_t maxModeLen = 0; - for (const auto& [name, mode] : Modes) { - if (name != mode->Name) - continue; // this is an alias - maxModeLen = Max(maxModeLen, mode->CalculateFullNameLen()); - } + for (const auto& [name, mode] : Modes) { + if (name != mode->Name) + continue; // this is an alias + maxModeLen = Max(maxModeLen, mode->CalculateFullNameLen()); + } if (ShowSeparated) { for (const auto& unsortedMode : UnsortedModes) @@ -300,14 +300,14 @@ void TModChooser::PrintHelp(const TString& progName) const { } } } else { - for (const auto& mode : Modes) { - if (mode.first != mode.second->Name) - continue; // this is an alias + for (const auto& mode : Modes) { + if (mode.first != mode.second->Name) + continue; // this is an alias if (!mode.second->Hidden) { Cerr << " " << mode.second->FormatFullName(maxModeLen + 4) << mode.second->Description << Endl; } - } + } } Cerr << Endl; @@ -319,54 +319,54 @@ void TModChooser::PrintHelp(const TString& progName) const { } return; } - -TVersionHandlerPtr TModChooser::GetVersionHandler() const { - return VersionHandler; -} - -bool TModChooser::IsSvnRevisionOptionDisabled() const { - return SvnRevisionOptionDisabled; -} - -int TMainClassArgs::Run(int argc, const char** argv) { - return DoRun(NLastGetopt::TOptsParseResult(&GetOptions(), argc, argv)); -} - -const NLastGetopt::TOpts& TMainClassArgs::GetOptions() { - if (Opts_.Empty()) { - Opts_ = NLastGetopt::TOpts(); - RegisterOptions(Opts_.GetRef()); - } - - return Opts_.GetRef(); -} - -void TMainClassArgs::RegisterOptions(NLastGetopt::TOpts& opts) { - opts.AddHelpOption('h'); -} - -int TMainClassArgs::operator()(const int argc, const char** argv) { - return Run(argc, argv); -} - -int TMainClassModes::operator()(const int argc, const char** argv) { - return Run(argc, argv); -} - -int TMainClassModes::Run(int argc, const char** argv) { - auto& chooser = GetSubModes(); - return chooser.Run(argc, argv); -} - -const TModChooser& TMainClassModes::GetSubModes() { - if (Modes_.Empty()) { - Modes_.ConstructInPlace(); - RegisterModes(Modes_.GetRef()); - } - - return Modes_.GetRef(); -} - -void TMainClassModes::RegisterModes(TModChooser& modes) { - modes.SetModesHelpOption("-h"); -} + +TVersionHandlerPtr TModChooser::GetVersionHandler() const { + return VersionHandler; +} + +bool TModChooser::IsSvnRevisionOptionDisabled() const { + return SvnRevisionOptionDisabled; +} + +int TMainClassArgs::Run(int argc, const char** argv) { + return DoRun(NLastGetopt::TOptsParseResult(&GetOptions(), argc, argv)); +} + +const NLastGetopt::TOpts& TMainClassArgs::GetOptions() { + if (Opts_.Empty()) { + Opts_ = NLastGetopt::TOpts(); + RegisterOptions(Opts_.GetRef()); + } + + return Opts_.GetRef(); +} + +void TMainClassArgs::RegisterOptions(NLastGetopt::TOpts& opts) { + opts.AddHelpOption('h'); +} + +int TMainClassArgs::operator()(const int argc, const char** argv) { + return Run(argc, argv); +} + +int TMainClassModes::operator()(const int argc, const char** argv) { + return Run(argc, argv); +} + +int TMainClassModes::Run(int argc, const char** argv) { + auto& chooser = GetSubModes(); + return chooser.Run(argc, argv); +} + +const TModChooser& TMainClassModes::GetSubModes() { + if (Modes_.Empty()) { + Modes_.ConstructInPlace(); + RegisterModes(Modes_.GetRef()); + } + + return Modes_.GetRef(); +} + +void TMainClassModes::RegisterModes(TModChooser& modes) { + modes.SetModesHelpOption("-h"); +} diff --git a/library/cpp/getopt/small/modchooser.h b/library/cpp/getopt/small/modchooser.h index 0a8de6d50b..95f5a5944e 100644 --- a/library/cpp/getopt/small/modchooser.h +++ b/library/cpp/getopt/small/modchooser.h @@ -1,7 +1,7 @@ #pragma once -#include "last_getopt_opts.h" - +#include "last_getopt_opts.h" + #include <util/generic/map.h> #include <util/generic/string.h> #include <util/generic/vector.h> @@ -60,12 +60,12 @@ public: //! Set default mode (if not specified explicitly) void SetDefaultMode(const TString& mode); - void AddAlias(const TString& alias, const TString& mode); - + void AddAlias(const TString& alias, const TString& mode); + //! Set main program description. void SetDescription(const TString& descr); - //! Set modes help option name (-? is by default) + //! Set modes help option name (-? is by default) void SetModesHelpOption(const TString& helpOption); //! Specify handler for '--version' parameter @@ -83,7 +83,7 @@ public: void DisableSvnRevisionOption(); void AddCompletions(TString progName, const TString& name = "completion", bool hidden = false, bool noCompletion = false); - + /*! Run appropriate mode. * * In this method following things happen: @@ -104,11 +104,11 @@ public: struct TMode { TString Name; - TMainClass* Main; + TMainClass* Main; TString Description; bool Hidden; bool NoCompletion; - TVector<TString> Aliases; + TVector<TString> Aliases; TMode() : Main(nullptr) @@ -116,24 +116,24 @@ public: } TMode(const TString& name, TMainClass* main, const TString& descr, bool hidden, bool noCompletion); - - // Full name includes primary name and aliases. Also, will add ANSI colors. - size_t CalculateFullNameLen() const; - TString FormatFullName(size_t pad) const; + + // Full name includes primary name and aliases. Also, will add ANSI colors. + size_t CalculateFullNameLen() const; + TString FormatFullName(size_t pad) const; }; - TVector<const TMode*> GetUnsortedModes() const { - auto ret = TVector<const TMode*>(Reserve(UnsortedModes.size())); - for (auto& mode : UnsortedModes) { - ret.push_back(mode.Get()); - } - return ret; - } - - TVersionHandlerPtr GetVersionHandler() const; - - bool IsSvnRevisionOptionDisabled() const; - + TVector<const TMode*> GetUnsortedModes() const { + auto ret = TVector<const TMode*>(Reserve(UnsortedModes.size())); + for (auto& mode : UnsortedModes) { + ret.push_back(mode.Get()); + } + return ret; + } + + TVersionHandlerPtr GetVersionHandler() const; + + bool IsSvnRevisionOptionDisabled() const; + private: //! Main program description. TString Description; @@ -142,10 +142,10 @@ private: TString ModesHelpOption; //! Wrappers around all modes. - TVector<THolder<TMainClass>> Wrappers; + TVector<THolder<TMainClass>> Wrappers; //! Modes - TMap<TString, TMode*> Modes; + TMap<TString, TMode*> Modes; TString DefaultMode; @@ -165,51 +165,51 @@ private: TString SeparationString; //! Unsorted list of options - TVector<THolder<TMode>> UnsortedModes; - - //! Mode that generates completions - THolder<TMainClass> CompletionsGenerator; + TVector<THolder<TMode>> UnsortedModes; + + //! Mode that generates completions + THolder<TMainClass> CompletionsGenerator; }; - -//! Mode class that allows introspecting its console arguments. -class TMainClassArgs: public TMainClass { -public: + +//! Mode class that allows introspecting its console arguments. +class TMainClassArgs: public TMainClass { +public: int operator()(int argc, const char** argv) final; - -public: - //! Run this mode. - int Run(int argc, const char** argv); - - //! Get console arguments for this mode. - const NLastGetopt::TOpts& GetOptions(); - -protected: - //! Fill given empty `TOpts` with options. - virtual void RegisterOptions(NLastGetopt::TOpts& opts); - - //! Actual mode logic. Takes parsed options and returns exit code. - virtual int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) = 0; - -private: - TMaybe<NLastGetopt::TOpts> Opts_; -}; - -//! Mode class that uses sub-modes to dispatch commands further. -class TMainClassModes: public TMainClass { -public: + +public: + //! Run this mode. + int Run(int argc, const char** argv); + + //! Get console arguments for this mode. + const NLastGetopt::TOpts& GetOptions(); + +protected: + //! Fill given empty `TOpts` with options. + virtual void RegisterOptions(NLastGetopt::TOpts& opts); + + //! Actual mode logic. Takes parsed options and returns exit code. + virtual int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) = 0; + +private: + TMaybe<NLastGetopt::TOpts> Opts_; +}; + +//! Mode class that uses sub-modes to dispatch commands further. +class TMainClassModes: public TMainClass { +public: int operator()(int argc, const char** argv) final; - -public: - //! Run this mode. - int Run(int argc, const char** argv); - - //! Get sub-modes for this mode. - const TModChooser& GetSubModes(); - -protected: - //! Fill given modchooser with sub-modes. - virtual void RegisterModes(TModChooser& modes); - -private: - TMaybe<TModChooser> Modes_; -}; + +public: + //! Run this mode. + int Run(int argc, const char** argv); + + //! Get sub-modes for this mode. + const TModChooser& GetSubModes(); + +protected: + //! Fill given modchooser with sub-modes. + virtual void RegisterModes(TModChooser& modes); + +private: + TMaybe<TModChooser> Modes_; +}; diff --git a/library/cpp/getopt/small/wrap.cpp b/library/cpp/getopt/small/wrap.cpp index 9fbd38842a..8002028a2a 100644 --- a/library/cpp/getopt/small/wrap.cpp +++ b/library/cpp/getopt/small/wrap.cpp @@ -1,99 +1,99 @@ -#include "wrap.h" - +#include "wrap.h" + #include <library/cpp/colorizer/colors.h> - -#include <util/generic/string.h> -#include <util/stream/str.h> -#include <util/charset/utf8.h> - -#include <cctype> - -namespace NLastGetopt { - TString Wrap(ui32 width, TStringBuf text, TStringBuf indent, size_t* lastLineLen, bool* hasParagraphs) { - if (width == 0) { - return TString(text); - } - - if (width >= indent.size()) { - width -= indent.size(); - } - - if (hasParagraphs) { - *hasParagraphs = false; - } - - TString res; - auto os = TStringOutput(res); - - const char* spaceBegin = text.begin(); - const char* wordBegin = text.begin(); - const char* wordEnd = text.begin(); - const char* end = text.end(); - - size_t lenSoFar = 0; - - bool isPreParagraph = false; - - do { - spaceBegin = wordBegin = wordEnd; - - while (wordBegin < end && *wordBegin == ' ') { - wordBegin++; - } - - if (wordBegin == end) { - break; - } - - wordEnd = wordBegin; - - while (wordEnd < end && *wordEnd != ' ' && *wordEnd != '\n') { - wordEnd++; - } - - auto spaces = TStringBuf(spaceBegin, wordBegin); - auto word = TStringBuf(wordBegin, wordEnd); - - size_t spaceLen = spaces.size(); - - size_t wordLen = 0; - if (!GetNumberOfUTF8Chars(word.data(), word.size(), wordLen)) { - wordLen = word.size(); // not a utf8 string -- just use its binary size - } - wordLen -= NColorizer::TotalAnsiEscapeCodeLen(word); - - // Empty word means we've found a bunch of whitespaces followed by newline. - // We don't want to print trailing whitespaces. - if (word) { - // We can't fit this word into the line -- insert additional line break. - // We shouldn't insert line breaks if we're at the beginning of a line, hence `lenSoFar` check. - if (lenSoFar && lenSoFar + spaceLen + wordLen > width) { - os << Endl << indent << word; - lenSoFar = wordLen; - } else { - os << spaces << word; - lenSoFar += spaceLen + wordLen; - } - isPreParagraph = false; - } - - if (wordEnd != end && *wordEnd == '\n') { - os << Endl << indent; - lenSoFar = 0; - wordEnd++; - if (hasParagraphs && isPreParagraph) { - *hasParagraphs = true; - } else { - isPreParagraph = true; - } - continue; - } - } while (wordEnd < end); - - if (lastLineLen) { - *lastLineLen = lenSoFar; - } - - return res; - } -} + +#include <util/generic/string.h> +#include <util/stream/str.h> +#include <util/charset/utf8.h> + +#include <cctype> + +namespace NLastGetopt { + TString Wrap(ui32 width, TStringBuf text, TStringBuf indent, size_t* lastLineLen, bool* hasParagraphs) { + if (width == 0) { + return TString(text); + } + + if (width >= indent.size()) { + width -= indent.size(); + } + + if (hasParagraphs) { + *hasParagraphs = false; + } + + TString res; + auto os = TStringOutput(res); + + const char* spaceBegin = text.begin(); + const char* wordBegin = text.begin(); + const char* wordEnd = text.begin(); + const char* end = text.end(); + + size_t lenSoFar = 0; + + bool isPreParagraph = false; + + do { + spaceBegin = wordBegin = wordEnd; + + while (wordBegin < end && *wordBegin == ' ') { + wordBegin++; + } + + if (wordBegin == end) { + break; + } + + wordEnd = wordBegin; + + while (wordEnd < end && *wordEnd != ' ' && *wordEnd != '\n') { + wordEnd++; + } + + auto spaces = TStringBuf(spaceBegin, wordBegin); + auto word = TStringBuf(wordBegin, wordEnd); + + size_t spaceLen = spaces.size(); + + size_t wordLen = 0; + if (!GetNumberOfUTF8Chars(word.data(), word.size(), wordLen)) { + wordLen = word.size(); // not a utf8 string -- just use its binary size + } + wordLen -= NColorizer::TotalAnsiEscapeCodeLen(word); + + // Empty word means we've found a bunch of whitespaces followed by newline. + // We don't want to print trailing whitespaces. + if (word) { + // We can't fit this word into the line -- insert additional line break. + // We shouldn't insert line breaks if we're at the beginning of a line, hence `lenSoFar` check. + if (lenSoFar && lenSoFar + spaceLen + wordLen > width) { + os << Endl << indent << word; + lenSoFar = wordLen; + } else { + os << spaces << word; + lenSoFar += spaceLen + wordLen; + } + isPreParagraph = false; + } + + if (wordEnd != end && *wordEnd == '\n') { + os << Endl << indent; + lenSoFar = 0; + wordEnd++; + if (hasParagraphs && isPreParagraph) { + *hasParagraphs = true; + } else { + isPreParagraph = true; + } + continue; + } + } while (wordEnd < end); + + if (lastLineLen) { + *lastLineLen = lenSoFar; + } + + return res; + } +} diff --git a/library/cpp/getopt/small/wrap.h b/library/cpp/getopt/small/wrap.h index e98028688d..223c21e720 100644 --- a/library/cpp/getopt/small/wrap.h +++ b/library/cpp/getopt/small/wrap.h @@ -1,16 +1,16 @@ -#pragma once - -#include <util/generic/fwd.h> -#include <util/generic/strbuf.h> - -namespace NLastGetopt { - /** - * Split text to multiple lines so that each line fits the given width. - * Can work with UTF8, understands ANSI escape codes. - * - * @param indent will print this string after each newline. - * @param lastLineLen output: will set to number of unicode codepoints in the last printed line. - * @param hasParagraphs output: will set to true if there are two consecutive newlines in the text. - */ - TString Wrap(ui32 width, TStringBuf text, TStringBuf indent = "", size_t* lastLineLen = nullptr, bool* hasParagraphs = nullptr); -} +#pragma once + +#include <util/generic/fwd.h> +#include <util/generic/strbuf.h> + +namespace NLastGetopt { + /** + * Split text to multiple lines so that each line fits the given width. + * Can work with UTF8, understands ANSI escape codes. + * + * @param indent will print this string after each newline. + * @param lastLineLen output: will set to number of unicode codepoints in the last printed line. + * @param hasParagraphs output: will set to true if there are two consecutive newlines in the text. + */ + TString Wrap(ui32 width, TStringBuf text, TStringBuf indent = "", size_t* lastLineLen = nullptr, bool* hasParagraphs = nullptr); +} diff --git a/library/cpp/getopt/small/ya.make b/library/cpp/getopt/small/ya.make index 96de0f04b1..90bc114f0a 100644 --- a/library/cpp/getopt/small/ya.make +++ b/library/cpp/getopt/small/ya.make @@ -7,10 +7,10 @@ PEERDIR( ) SRCS( - completer.cpp - completer_command.cpp - completion_generator.cpp - formatted_output.cpp + completer.cpp + completer_command.cpp + completion_generator.cpp + formatted_output.cpp last_getopt.cpp last_getopt_easy_setup.cpp last_getopt_opt.cpp @@ -21,7 +21,7 @@ SRCS( opt.cpp opt2.cpp posix_getopt.cpp - wrap.cpp + wrap.cpp ygetopt.cpp ) diff --git a/library/cpp/getopt/ut/last_getopt_ut.cpp b/library/cpp/getopt/ut/last_getopt_ut.cpp index c99a1d053d..9d2a46a51d 100644 --- a/library/cpp/getopt/ut/last_getopt_ut.cpp +++ b/library/cpp/getopt/ut/last_getopt_ut.cpp @@ -612,9 +612,9 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { opts.AddCharOption('d').DefaultValue("42"); opts.AddCharOption('s').DefaultValue("str_default"); opts.SetFreeArgsNum(123, 456); - opts.SetFreeArgTitle(0, "first_free_arg", "help"); - opts.SetFreeArgTitle(2, "second_free_arg"); - opts.AddSection("Section", "Section\n text"); + opts.SetFreeArgTitle(0, "first_free_arg", "help"); + opts.SetFreeArgTitle(2, "second_free_arg"); + opts.AddSection("Section", "Section\n text"); const char* cmd[] = {prog}; TOptsParser parser(&opts, Y_ARRAY_SIZE(cmd), cmd); TStringStream out; @@ -636,8 +636,8 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "123" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "456" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "first_free_arg" << colors.OldColor()) != TString::npos); - // free args without help not rendered even if they have custom title - UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "second_free_arg" << colors.OldColor()) == TString::npos); + // free args without help not rendered even if they have custom title + UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "second_free_arg" << colors.OldColor()) == TString::npos); // find signatures UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Usage" << colors.OldColor()) != TString::npos); @@ -645,10 +645,10 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Optional parameters" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Free args" << colors.OldColor()) != TString::npos); - // find sections - UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Section" << colors.OldColor() << ":") != TString::npos); - UNIT_ASSERT(out.Str().find(TStringBuilder() << " Section\n text") != TString::npos); - + // find sections + UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Section" << colors.OldColor() << ":") != TString::npos); + UNIT_ASSERT(out.Str().find(TStringBuilder() << " Section\n text") != TString::npos); + // print without colors TStringStream out2; opts.PrintUsage(prog, out2); diff --git a/library/cpp/getopt/ut/wrap.cpp b/library/cpp/getopt/ut/wrap.cpp index 3444eea102..2d78a8ba4d 100644 --- a/library/cpp/getopt/ut/wrap.cpp +++ b/library/cpp/getopt/ut/wrap.cpp @@ -1,96 +1,96 @@ #include <library/cpp/testing/unittest/registar.h> - + #include <library/cpp/getopt/small/wrap.h> - -Y_UNIT_TEST_SUITE(Wrap) { - Y_UNIT_TEST(TestWrapping) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b c d eeffeff").Quote(), - TString("a b c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nc d\neeffeff").Quote(), - TString("a b\nc d\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\n c d\neeffeff").Quote(), - TString("a b\n c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx c d\neeffeff").Quote(), - TString("a b\nx\nc d\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), - TString("a b\nx\n c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), - TString("a b\nx\n c\nd\neeffeff").Quote() - ); - } - - Y_UNIT_TEST(TestWrappingIndent) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b c d", "|>").Quote(), - TString("a b\n|>c d").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\n\nc d", "|>").Quote(), - TString("a b\n|>\n|>c d").Quote() - ); - } - - Y_UNIT_TEST(TestWrappingAnsi) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote(), - TString("\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a \033[1;2;3;4;5mb c\033[1;2;3;4;5m \033[1;2;3;4;5md e f").Quote(), - TString("a \033[1;2;3;4;5mb c\033[1;2;3;4;5m\n\033[1;2;3;4;5md e f").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), - TString("a b \033[1;2;3;4;5m\nc d").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), - TString("a b\n\033[1;2;3;4;5m c d").Quote() - ); - } - - Y_UNIT_TEST(TestTextInfo) { - size_t lastLineLen; - bool hasParagraphs; - - NLastGetopt::Wrap(5, "a b c d e", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 3); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); - - NLastGetopt::Wrap(5, "a b c\n\nd e f h", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 1); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "a b c\n\n", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 0); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "\n \na b c", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "\nx\na b c", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); - } -} + +Y_UNIT_TEST_SUITE(Wrap) { + Y_UNIT_TEST(TestWrapping) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b c d eeffeff").Quote(), + TString("a b c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nc d\neeffeff").Quote(), + TString("a b\nc d\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\n c d\neeffeff").Quote(), + TString("a b\n c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx c d\neeffeff").Quote(), + TString("a b\nx\nc d\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), + TString("a b\nx\n c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), + TString("a b\nx\n c\nd\neeffeff").Quote() + ); + } + + Y_UNIT_TEST(TestWrappingIndent) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b c d", "|>").Quote(), + TString("a b\n|>c d").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\n\nc d", "|>").Quote(), + TString("a b\n|>\n|>c d").Quote() + ); + } + + Y_UNIT_TEST(TestWrappingAnsi) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote(), + TString("\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a \033[1;2;3;4;5mb c\033[1;2;3;4;5m \033[1;2;3;4;5md e f").Quote(), + TString("a \033[1;2;3;4;5mb c\033[1;2;3;4;5m\n\033[1;2;3;4;5md e f").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), + TString("a b \033[1;2;3;4;5m\nc d").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), + TString("a b\n\033[1;2;3;4;5m c d").Quote() + ); + } + + Y_UNIT_TEST(TestTextInfo) { + size_t lastLineLen; + bool hasParagraphs; + + NLastGetopt::Wrap(5, "a b c d e", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 3); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); + + NLastGetopt::Wrap(5, "a b c\n\nd e f h", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 1); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "a b c\n\n", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 0); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "\n \na b c", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "\nx\na b c", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); + } +} diff --git a/library/cpp/getopt/ut/ya.make b/library/cpp/getopt/ut/ya.make index 8d00669efb..c2e928b6f0 100644 --- a/library/cpp/getopt/ut/ya.make +++ b/library/cpp/getopt/ut/ya.make @@ -8,7 +8,7 @@ SRCS( opt2_ut.cpp opt_ut.cpp posix_getopt_ut.cpp - wrap.cpp + wrap.cpp ygetopt_ut.cpp ) diff --git a/library/cpp/monlib/encode/buffered/string_pool.h b/library/cpp/monlib/encode/buffered/string_pool.h index 00e5644608..9d2f3216d7 100644 --- a/library/cpp/monlib/encode/buffered/string_pool.h +++ b/library/cpp/monlib/encode/buffered/string_pool.h @@ -78,10 +78,10 @@ namespace NMonitoring { return Index_.at(i); } - size_t Size() const { - return Index_.size(); - } - + size_t Size() const { + return Index_.size(); + } + private: void InitIndex(const char* data, ui32 size); diff --git a/library/cpp/monlib/encode/spack/spack_v1.h b/library/cpp/monlib/encode/spack/spack_v1.h index cf1c9417b9..c417171f84 100644 --- a/library/cpp/monlib/encode/spack/spack_v1.h +++ b/library/cpp/monlib/encode/spack/spack_v1.h @@ -24,9 +24,9 @@ namespace NMonitoring { EMetricType DecodeMetricType(ui8 byte); - [[nodiscard]] - bool TryDecodeMetricType(ui8 byte, EMetricType* result); - + [[nodiscard]] + bool TryDecodeMetricType(ui8 byte, EMetricType* result); + /////////////////////////////////////////////////////////////////////////////// // EValueType /////////////////////////////////////////////////////////////////////////////// @@ -43,9 +43,9 @@ namespace NMonitoring { EValueType DecodeValueType(ui8 byte); - [[nodiscard]] - bool TryDecodeValueType(ui8 byte, EValueType* result); - + [[nodiscard]] + bool TryDecodeValueType(ui8 byte, EValueType* result); + /////////////////////////////////////////////////////////////////////////////// // ETimePrecision /////////////////////////////////////////////////////////////////////////////// @@ -60,9 +60,9 @@ namespace NMonitoring { ETimePrecision DecodeTimePrecision(ui8 byte); - [[nodiscard]] - bool TryDecodeTimePrecision(ui8 byte, ETimePrecision* result); - + [[nodiscard]] + bool TryDecodeTimePrecision(ui8 byte, ETimePrecision* result); + /////////////////////////////////////////////////////////////////////////////// // ECompression /////////////////////////////////////////////////////////////////////////////// @@ -70,9 +70,9 @@ namespace NMonitoring { ECompression DecodeCompression(ui8 byte); - [[nodiscard]] - bool TryDecodeCompression(ui8 byte, ECompression* result); - + [[nodiscard]] + bool TryDecodeCompression(ui8 byte, ECompression* result); + /////////////////////////////////////////////////////////////////////////////// // TSpackHeader /////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp b/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp index 1f445fc80d..b188cbf7fa 100644 --- a/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp +++ b/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp @@ -284,119 +284,119 @@ namespace NMonitoring { } // namespace EValueType DecodeValueType(ui8 byte) { - EValueType result; - if (!TryDecodeValueType(byte, &result)) { + EValueType result; + if (!TryDecodeValueType(byte, &result)) { throw TSpackDecodeError() << "unknown value type: " << byte; - } - return result; - } - - bool TryDecodeValueType(ui8 byte, EValueType* result) { + } + return result; + } + + bool TryDecodeValueType(ui8 byte, EValueType* result) { if (byte == EncodeValueType(EValueType::NONE)) { - if (result) { - *result = EValueType::NONE; - } - return true; + if (result) { + *result = EValueType::NONE; + } + return true; } else if (byte == EncodeValueType(EValueType::ONE_WITHOUT_TS)) { - if (result) { - *result = EValueType::ONE_WITHOUT_TS; - } - return true; + if (result) { + *result = EValueType::ONE_WITHOUT_TS; + } + return true; } else if (byte == EncodeValueType(EValueType::ONE_WITH_TS)) { - if (result) { - *result = EValueType::ONE_WITH_TS; - } - return true; + if (result) { + *result = EValueType::ONE_WITH_TS; + } + return true; } else if (byte == EncodeValueType(EValueType::MANY_WITH_TS)) { - if (result) { - *result = EValueType::MANY_WITH_TS; - } - return true; + if (result) { + *result = EValueType::MANY_WITH_TS; + } + return true; } else { - return false; + return false; } } ETimePrecision DecodeTimePrecision(ui8 byte) { - ETimePrecision result; - if (!TryDecodeTimePrecision(byte, &result)) { + ETimePrecision result; + if (!TryDecodeTimePrecision(byte, &result)) { throw TSpackDecodeError() << "unknown time precision: " << byte; - } - return result; - } - - bool TryDecodeTimePrecision(ui8 byte, ETimePrecision* result) { + } + return result; + } + + bool TryDecodeTimePrecision(ui8 byte, ETimePrecision* result) { if (byte == EncodeTimePrecision(ETimePrecision::SECONDS)) { - if (result) { - *result = ETimePrecision::SECONDS; - } - return true; + if (result) { + *result = ETimePrecision::SECONDS; + } + return true; } else if (byte == EncodeTimePrecision(ETimePrecision::MILLIS)) { - if (result) { - *result = ETimePrecision::MILLIS; - } - return true; + if (result) { + *result = ETimePrecision::MILLIS; + } + return true; } else { - return false; + return false; } } EMetricType DecodeMetricType(ui8 byte) { - EMetricType result; - if (!TryDecodeMetricType(byte, &result)) { + EMetricType result; + if (!TryDecodeMetricType(byte, &result)) { throw TSpackDecodeError() << "unknown metric type: " << byte; - } - return result; - } - - bool TryDecodeMetricType(ui8 byte, EMetricType* result) { + } + return result; + } + + bool TryDecodeMetricType(ui8 byte, EMetricType* result) { if (byte == EncodeMetricType(EMetricType::GAUGE)) { - if (result) { - *result = EMetricType::GAUGE; - } - return true; + if (result) { + *result = EMetricType::GAUGE; + } + return true; } else if (byte == EncodeMetricType(EMetricType::COUNTER)) { - if (result) { - *result = EMetricType::COUNTER; - } - return true; + if (result) { + *result = EMetricType::COUNTER; + } + return true; } else if (byte == EncodeMetricType(EMetricType::RATE)) { - if (result) { - *result = EMetricType::RATE; - } - return true; + if (result) { + *result = EMetricType::RATE; + } + return true; } else if (byte == EncodeMetricType(EMetricType::IGAUGE)) { - if (result) { - *result = EMetricType::IGAUGE; - } - return true; + if (result) { + *result = EMetricType::IGAUGE; + } + return true; } else if (byte == EncodeMetricType(EMetricType::HIST)) { - if (result) { - *result = EMetricType::HIST; - } - return true; + if (result) { + *result = EMetricType::HIST; + } + return true; } else if (byte == EncodeMetricType(EMetricType::HIST_RATE)) { - if (result) { - *result = EMetricType::HIST_RATE; - } - return true; + if (result) { + *result = EMetricType::HIST_RATE; + } + return true; } else if (byte == EncodeMetricType(EMetricType::DSUMMARY)) { - if (result) { - *result = EMetricType::DSUMMARY; - } - return true; + if (result) { + *result = EMetricType::DSUMMARY; + } + return true; } else if (byte == EncodeMetricType(EMetricType::LOGHIST)) { - if (result) { - *result = EMetricType::LOGHIST; - } - return true; + if (result) { + *result = EMetricType::LOGHIST; + } + return true; } else if (byte == EncodeMetricType(EMetricType::UNKNOWN)) { - if (result) { - *result = EMetricType::UNKNOWN; - } - return true; + if (result) { + *result = EMetricType::UNKNOWN; + } + return true; } else { - return false; + return false; } } @@ -417,36 +417,36 @@ namespace NMonitoring { } ECompression DecodeCompression(ui8 byte) { - ECompression result; - if (!TryDecodeCompression(byte, &result)) { + ECompression result; + if (!TryDecodeCompression(byte, &result)) { throw TSpackDecodeError() << "unknown compression alg: " << byte; - } - return result; - } - - bool TryDecodeCompression(ui8 byte, ECompression* result) { + } + return result; + } + + bool TryDecodeCompression(ui8 byte, ECompression* result) { if (byte == EncodeCompression(ECompression::IDENTITY)) { - if (result) { - *result = ECompression::IDENTITY; - } - return true; + if (result) { + *result = ECompression::IDENTITY; + } + return true; } else if (byte == EncodeCompression(ECompression::ZLIB)) { - if (result) { - *result = ECompression::ZLIB; - } - return true; + if (result) { + *result = ECompression::ZLIB; + } + return true; } else if (byte == EncodeCompression(ECompression::ZSTD)) { - if (result) { - *result = ECompression::ZSTD; - } - return true; + if (result) { + *result = ECompression::ZSTD; + } + return true; } else if (byte == EncodeCompression(ECompression::LZ4)) { - if (result) { - *result = ECompression::LZ4; - } - return true; + if (result) { + *result = ECompression::LZ4; + } + return true; } else { - return false; + return false; } } diff --git a/library/cpp/monlib/encode/spack/varint.cpp b/library/cpp/monlib/encode/spack/varint.cpp index 051cf17380..ea3aff9409 100644 --- a/library/cpp/monlib/encode/spack/varint.cpp +++ b/library/cpp/monlib/encode/spack/varint.cpp @@ -22,17 +22,17 @@ namespace NMonitoring { } ui32 ReadVarUInt32(IInputStream* input) { - ui32 value = 0; - switch (TryReadVarUInt32(input, &value)) { - case EReadResult::OK: - return value; - case EReadResult::ERR_OVERFLOW: + ui32 value = 0; + switch (TryReadVarUInt32(input, &value)) { + case EReadResult::OK: + return value; + case EReadResult::ERR_OVERFLOW: ythrow yexception() << "the data is too long to read ui32"; - case EReadResult::ERR_UNEXPECTED_EOF: + case EReadResult::ERR_UNEXPECTED_EOF: ythrow yexception() << "the data unexpectedly ended"; - default: - ythrow yexception() << "unknown error while reading varint"; - } + default: + ythrow yexception() << "unknown error while reading varint"; + } } size_t ReadVarUInt32(const ui8* buf, size_t len, ui32* result) { @@ -56,24 +56,24 @@ namespace NMonitoring { return count; } -EReadResult TryReadVarUInt32(IInputStream* input, ui32* value) { +EReadResult TryReadVarUInt32(IInputStream* input, ui32* value) { size_t count = 0; ui32 result = 0; ui8 byte = 0; do { if (7 * count > 8 * sizeof(ui32)) { - return EReadResult::ERR_OVERFLOW; + return EReadResult::ERR_OVERFLOW; } if (input->Read(&byte, 1) != 1) { - return EReadResult::ERR_UNEXPECTED_EOF; + return EReadResult::ERR_UNEXPECTED_EOF; } result |= (static_cast<ui32>(byte & 0x7F)) << (7 * count); ++count; } while (byte & 0x80); *value = result; - return EReadResult::OK; + return EReadResult::OK; } } diff --git a/library/cpp/monlib/encode/spack/varint.h b/library/cpp/monlib/encode/spack/varint.h index 7ac522dd6c..7301fa3889 100644 --- a/library/cpp/monlib/encode/spack/varint.h +++ b/library/cpp/monlib/encode/spack/varint.h @@ -11,13 +11,13 @@ namespace NMonitoring { ui32 ReadVarUInt32(IInputStream* input); size_t ReadVarUInt32(const ui8* buf, size_t len, ui32* result); - enum class EReadResult { - OK, - ERR_OVERFLOW, - ERR_UNEXPECTED_EOF, - }; - - [[nodiscard]] - EReadResult TryReadVarUInt32(IInputStream* input, ui32* value); - + enum class EReadResult { + OK, + ERR_OVERFLOW, + ERR_UNEXPECTED_EOF, + }; + + [[nodiscard]] + EReadResult TryReadVarUInt32(IInputStream* input, ui32* value); + } diff --git a/library/cpp/monlib/metrics/metric_registry.cpp b/library/cpp/monlib/metrics/metric_registry.cpp index b083163a7b..f20b46dd36 100644 --- a/library/cpp/monlib/metrics/metric_registry.cpp +++ b/library/cpp/monlib/metrics/metric_registry.cpp @@ -40,10 +40,10 @@ namespace NMonitoring { CommonLabels_ = commonLabels; } - TMetricRegistry* TMetricRegistry::Instance() { - return Singleton<TMetricRegistry>(); - } - + TMetricRegistry* TMetricRegistry::Instance() { + return Singleton<TMetricRegistry>(); + } + TGauge* TMetricRegistry::Gauge(TLabels labels) { return Metric<TGauge, EMetricType::GAUGE>(std::move(labels)); } diff --git a/library/cpp/monlib/metrics/metric_registry.h b/library/cpp/monlib/metrics/metric_registry.h index 670cf8651e..725a4ec3ed 100644 --- a/library/cpp/monlib/metrics/metric_registry.h +++ b/library/cpp/monlib/metrics/metric_registry.h @@ -57,11 +57,11 @@ namespace NMonitoring { explicit TMetricRegistry(const TLabels& commonLabels); - /** - * Get a global metrics registry instance. - */ - static TMetricRegistry* Instance(); - + /** + * Get a global metrics registry instance. + */ + static TMetricRegistry* Instance(); + TGauge* Gauge(TLabels labels); TLazyGauge* LazyGauge(TLabels labels, std::function<double()> supplier); TIntGauge* IntGauge(TLabels labels); diff --git a/library/cpp/testing/gmock_in_unittest/gmock.h b/library/cpp/testing/gmock_in_unittest/gmock.h index 31f6aee1c3..3afb6ecd09 100644 --- a/library/cpp/testing/gmock_in_unittest/gmock.h +++ b/library/cpp/testing/gmock_in_unittest/gmock.h @@ -1,5 +1,5 @@ #pragma once -#include <library/cpp/testing/gtest_extensions/gtest_extensions.h> +#include <library/cpp/testing/gtest_extensions/gtest_extensions.h> #include <gmock/gmock.h> diff --git a/library/cpp/testing/gmock_in_unittest/ya.make b/library/cpp/testing/gmock_in_unittest/ya.make index 5de68ad98d..9c9779b06c 100644 --- a/library/cpp/testing/gmock_in_unittest/ya.make +++ b/library/cpp/testing/gmock_in_unittest/ya.make @@ -5,7 +5,7 @@ OWNER(galaxycrab) PEERDIR( contrib/restricted/googletest/googlemock contrib/restricted/googletest/googletest - library/cpp/testing/gtest_extensions + library/cpp/testing/gtest_extensions library/cpp/testing/unittest ) diff --git a/library/cpp/testing/gtest_extensions/README.md b/library/cpp/testing/gtest_extensions/README.md index 5445c7a464..ab9b3dfe4f 100644 --- a/library/cpp/testing/gtest_extensions/README.md +++ b/library/cpp/testing/gtest_extensions/README.md @@ -1,5 +1,5 @@ -# Extensions for Gtest and Gmock - -Extensions that enable better support of util types in gtest and gmock: pretty printers, matchers, some convenience macros. - +# Extensions for Gtest and Gmock + +Extensions that enable better support of util types in gtest and gmock: pretty printers, matchers, some convenience macros. + If you're using `GTEST`, include `library/cpp/testing/gtest/gtest.h` and it will automatically enable these extensions. This is the preferred way to include gtest and gmock as opposed to including gtest, gmock and extensions directly. It eliminates chances of forgetting to include extensions. diff --git a/library/cpp/testing/gtest_extensions/assertions.cpp b/library/cpp/testing/gtest_extensions/assertions.cpp index f390409d1b..14416daed3 100644 --- a/library/cpp/testing/gtest_extensions/assertions.cpp +++ b/library/cpp/testing/gtest_extensions/assertions.cpp @@ -1,90 +1,90 @@ -#include "assertions.h" - -#include <util/string/builder.h> -#include <util/string/split.h> +#include "assertions.h" + +#include <util/string/builder.h> +#include <util/string/split.h> #include <util/system/type_name.h> - -namespace NGTest::NInternal { - namespace { - void FormatActual(const std::exception& err, const TBackTrace* bt, TStringBuilder& out) { + +namespace NGTest::NInternal { + namespace { + void FormatActual(const std::exception& err, const TBackTrace* bt, TStringBuilder& out) { out << "an exception of type " << TypeName(err) << " " - << "with message " << TString(err.what()).Quote() << "."; - if (bt) { - out << "\n Trace: "; - for (auto& line: StringSplitter(bt->PrintToString()).Split('\n')) { - out << " " << line.Token() << "\n"; - } - } - } - - void FormatActual(TStringBuilder& out) { - out << " Actual: it throws "; - auto exceptionPtr = std::current_exception(); - if (exceptionPtr) { - try { - std::rethrow_exception(exceptionPtr); - } catch (const yexception& err) { - FormatActual(err, err.BackTrace(), out); - return; - } catch (const std::exception& err) { - FormatActual(err, nullptr, out); - return; - } catch (...) { - out << "an unknown exception."; - return; - } - } - out << "nothing."; - } - - void FormatExpected(const char* statement, const char* type, const TString& contains, TStringBuilder& out) { - out << "Expected: "; - if (TStringBuf(statement).size() > 80) { - out << "statement"; - } else { - out << statement; - } - out << " throws an exception of type " << type; - - if (!contains.empty()) { - out << " with message containing " << contains.Quote(); - } - - out << "."; - } - } - - TString FormatErrorWrongException(const char* statement, const char* type) { - return FormatErrorWrongException(statement, type, ""); - } - - TString FormatErrorWrongException(const char* statement, const char* type, TString contains) { - TStringBuilder out; - - FormatExpected(statement, type, contains, out); - out << "\n"; - FormatActual(out); - - return out; - } - - TString FormatErrorUnexpectedException(const char* statement) { - TStringBuilder out; - - out << "Expected: "; - if (TStringBuf(statement).size() > 80) { - out << "statement"; - } else { - out << statement; - } - out << " doesn't throw an exception.\n "; - - FormatActual(out); - - return out; - } - - bool ExceptionMessageContains(const std::exception& err, TString contains) { - return TStringBuf(err.what()).Contains(contains); - } -} + << "with message " << TString(err.what()).Quote() << "."; + if (bt) { + out << "\n Trace: "; + for (auto& line: StringSplitter(bt->PrintToString()).Split('\n')) { + out << " " << line.Token() << "\n"; + } + } + } + + void FormatActual(TStringBuilder& out) { + out << " Actual: it throws "; + auto exceptionPtr = std::current_exception(); + if (exceptionPtr) { + try { + std::rethrow_exception(exceptionPtr); + } catch (const yexception& err) { + FormatActual(err, err.BackTrace(), out); + return; + } catch (const std::exception& err) { + FormatActual(err, nullptr, out); + return; + } catch (...) { + out << "an unknown exception."; + return; + } + } + out << "nothing."; + } + + void FormatExpected(const char* statement, const char* type, const TString& contains, TStringBuilder& out) { + out << "Expected: "; + if (TStringBuf(statement).size() > 80) { + out << "statement"; + } else { + out << statement; + } + out << " throws an exception of type " << type; + + if (!contains.empty()) { + out << " with message containing " << contains.Quote(); + } + + out << "."; + } + } + + TString FormatErrorWrongException(const char* statement, const char* type) { + return FormatErrorWrongException(statement, type, ""); + } + + TString FormatErrorWrongException(const char* statement, const char* type, TString contains) { + TStringBuilder out; + + FormatExpected(statement, type, contains, out); + out << "\n"; + FormatActual(out); + + return out; + } + + TString FormatErrorUnexpectedException(const char* statement) { + TStringBuilder out; + + out << "Expected: "; + if (TStringBuf(statement).size() > 80) { + out << "statement"; + } else { + out << statement; + } + out << " doesn't throw an exception.\n "; + + FormatActual(out); + + return out; + } + + bool ExceptionMessageContains(const std::exception& err, TString contains) { + return TStringBuf(err.what()).Contains(contains); + } +} diff --git a/library/cpp/testing/gtest_extensions/assertions.h b/library/cpp/testing/gtest_extensions/assertions.h index e8ea07b5df..9532b5ed96 100644 --- a/library/cpp/testing/gtest_extensions/assertions.h +++ b/library/cpp/testing/gtest_extensions/assertions.h @@ -1,111 +1,111 @@ -#pragma once - -#include <util/generic/string.h> - +#pragma once + +#include <util/generic/string.h> + #include <gtest/gtest.h> #include <gmock/gmock.h> - -/** - * Check that the given statement throws an exception of the given type, - * and that the thrown exception message contains the given substring. - */ -#define EXPECT_THROW_MESSAGE_HAS_SUBSTR(statement, expectedException, substring) \ - _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, GTEST_NONFATAL_FAILURE_) - -/** - * Check that the given statement throws an exception of the given type, - * and that the thrown exception message contains the given substring. - */ -#define ASSERT_THROW_MESSAGE_HAS_SUBSTR(statement, expectedException, substring) \ - _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, GTEST_FATAL_FAILURE_) - - -// Improve default macros. New implementation shows better exception messages. -// See https://github.com/google/googletest/issues/2878 - -#undef EXPECT_THROW -#define EXPECT_THROW(statement, expectedException) \ - _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, GTEST_NONFATAL_FAILURE_) - -#undef ASSERT_THROW -#define ASSERT_THROW(statement, expectedException) \ - _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, GTEST_FATAL_FAILURE_) - -#undef EXPECT_NO_THROW -#define EXPECT_NO_THROW(statement) \ - _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, GTEST_NONFATAL_FAILURE_) - -#undef ASSERT_NO_THROW -#define ASSERT_NO_THROW(statement) \ - _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, GTEST_FATAL_FAILURE_) - - -// Implementation details - -namespace NGTest::NInternal { - TString FormatErrorWrongException(const char* statement, const char* type); - TString FormatErrorWrongException(const char* statement, const char* type, TString contains); - TString FormatErrorUnexpectedException(const char* statement); - bool ExceptionMessageContains(const std::exception& err, TString contains); -} - -#define _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ - bool gtestCaughtExpected = false; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } catch (expectedException const&) { \ - gtestCaughtExpected = true; \ - } catch (...) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ - #statement, #expectedException); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } if (!gtestCaughtExpected) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ - #statement, #expectedException); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ - fail(gtestMsg.c_str()) - -#define _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ - bool gtestCaughtExpected = false; \ - ::TString gtestSubstring{substring}; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } catch (expectedException const& gtestError) { \ - if (!::NGTest::NInternal::ExceptionMessageContains(gtestError, gtestSubstring)) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ - #statement, #expectedException, gtestSubstring); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ - } \ - gtestCaughtExpected = true; \ - } catch (...) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ - #statement, #expectedException, gtestSubstring); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ - } if (!gtestCaughtExpected) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ - #statement, #expectedException, gtestSubstring); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__): \ - fail(gtestMsg.c_str()) - -#define _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } catch (...) { \ - gtestMsg = ::NGTest::NInternal::FormatErrorUnexpectedException(#statement); \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ - fail(gtestMsg.c_str()) + +/** + * Check that the given statement throws an exception of the given type, + * and that the thrown exception message contains the given substring. + */ +#define EXPECT_THROW_MESSAGE_HAS_SUBSTR(statement, expectedException, substring) \ + _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, GTEST_NONFATAL_FAILURE_) + +/** + * Check that the given statement throws an exception of the given type, + * and that the thrown exception message contains the given substring. + */ +#define ASSERT_THROW_MESSAGE_HAS_SUBSTR(statement, expectedException, substring) \ + _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, GTEST_FATAL_FAILURE_) + + +// Improve default macros. New implementation shows better exception messages. +// See https://github.com/google/googletest/issues/2878 + +#undef EXPECT_THROW +#define EXPECT_THROW(statement, expectedException) \ + _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, GTEST_NONFATAL_FAILURE_) + +#undef ASSERT_THROW +#define ASSERT_THROW(statement, expectedException) \ + _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, GTEST_FATAL_FAILURE_) + +#undef EXPECT_NO_THROW +#define EXPECT_NO_THROW(statement) \ + _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, GTEST_NONFATAL_FAILURE_) + +#undef ASSERT_NO_THROW +#define ASSERT_NO_THROW(statement) \ + _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, GTEST_FATAL_FAILURE_) + + +// Implementation details + +namespace NGTest::NInternal { + TString FormatErrorWrongException(const char* statement, const char* type); + TString FormatErrorWrongException(const char* statement, const char* type, TString contains); + TString FormatErrorUnexpectedException(const char* statement); + bool ExceptionMessageContains(const std::exception& err, TString contains); +} + +#define _Y_GTEST_EXPECT_THROW_IMPL_(statement, expectedException, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ + bool gtestCaughtExpected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expectedException const&) { \ + gtestCaughtExpected = true; \ + } catch (...) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ + #statement, #expectedException); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } if (!gtestCaughtExpected) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ + #statement, #expectedException); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtestMsg.c_str()) + +#define _Y_GTEST_EXPECT_THROW_MESSAGE_HAS_SUBSTR_IMPL_(statement, expectedException, substring, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ + bool gtestCaughtExpected = false; \ + ::TString gtestSubstring{substring}; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expectedException const& gtestError) { \ + if (!::NGTest::NInternal::ExceptionMessageContains(gtestError, gtestSubstring)) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ + #statement, #expectedException, gtestSubstring); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ + } \ + gtestCaughtExpected = true; \ + } catch (...) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ + #statement, #expectedException, gtestSubstring); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ + } if (!gtestCaughtExpected) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorWrongException( \ + #statement, #expectedException, gtestSubstring); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrowsubstr_, __LINE__): \ + fail(gtestMsg.c_str()) + +#define _Y_GTEST_EXPECT_NO_THROW_IMPL_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::TString gtestMsg = ""; ::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (...) { \ + gtestMsg = ::NGTest::NInternal::FormatErrorUnexpectedException(#statement); \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail(gtestMsg.c_str()) diff --git a/library/cpp/testing/gtest_extensions/gtest_extensions.cpp b/library/cpp/testing/gtest_extensions/gtest_extensions.cpp index 1277a804bc..d8631e97ea 100644 --- a/library/cpp/testing/gtest_extensions/gtest_extensions.cpp +++ b/library/cpp/testing/gtest_extensions/gtest_extensions.cpp @@ -1 +1 @@ -#include "gtest_extensions.h" +#include "gtest_extensions.h" diff --git a/library/cpp/testing/gtest_extensions/gtest_extensions.h b/library/cpp/testing/gtest_extensions/gtest_extensions.h index e20532241e..c90a59bf13 100644 --- a/library/cpp/testing/gtest_extensions/gtest_extensions.h +++ b/library/cpp/testing/gtest_extensions/gtest_extensions.h @@ -1,6 +1,6 @@ #pragma once -#include "assertions.h" -#include "matchers.h" -#include "pretty_printers.h" +#include "assertions.h" +#include "matchers.h" +#include "pretty_printers.h" #include "probe.h" diff --git a/library/cpp/testing/gtest_extensions/matchers.cpp b/library/cpp/testing/gtest_extensions/matchers.cpp index 7da7be8b3c..7eff491aec 100644 --- a/library/cpp/testing/gtest_extensions/matchers.cpp +++ b/library/cpp/testing/gtest_extensions/matchers.cpp @@ -1 +1 @@ -#include "matchers.h" +#include "matchers.h" diff --git a/library/cpp/testing/gtest_extensions/matchers.h b/library/cpp/testing/gtest_extensions/matchers.h index 044c1c3ee4..042baa0764 100644 --- a/library/cpp/testing/gtest_extensions/matchers.h +++ b/library/cpp/testing/gtest_extensions/matchers.h @@ -1,132 +1,132 @@ -#pragma once - -#include <util/generic/string.h> - +#pragma once + +#include <util/generic/string.h> + #include <gtest/gtest.h> #include <gmock/gmock.h> - -namespace testing { - /** - * When matching `const TStringBuf&`, implicitly convert other strings and string views to `Eq` matchers. - */ - template <typename T, typename TT> - class Matcher<const TBasicStringBuf<T, TT>&>: public internal::MatcherBase<const TBasicStringBuf<T, TT>&> { - public: - Matcher() { - } - - explicit Matcher(const MatcherInterface<const TBasicStringBuf<T, TT>&>* impl) - : internal::MatcherBase<const TBasicStringBuf<T, TT>&>(impl) { - } - + +namespace testing { + /** + * When matching `const TStringBuf&`, implicitly convert other strings and string views to `Eq` matchers. + */ + template <typename T, typename TT> + class Matcher<const TBasicStringBuf<T, TT>&>: public internal::MatcherBase<const TBasicStringBuf<T, TT>&> { + public: + Matcher() { + } + + explicit Matcher(const MatcherInterface<const TBasicStringBuf<T, TT>&>* impl) + : internal::MatcherBase<const TBasicStringBuf<T, TT>&>(impl) { + } + template <typename M, typename = typename std::remove_reference<M>::type::is_gtest_matcher> Matcher(M&& m) : internal::MatcherBase<const TBasicStringBuf<T, TT>&>(std::forward<M>(m)) { } - Matcher(const TBasicString<T, TT>& s) { - *this = Eq(TBasicStringBuf<T, TT>(s)); - } - - Matcher(const T* s) { - *this = Eq(TBasicStringBuf<T, TT>(s)); - } - - Matcher(TBasicStringBuf<T, TT> s) { - *this = Eq(s); - } - }; - - /** - * When matching `TBasicBuf`, implicitly convert other strings and string views to `Eq` matchers. - */ - template <typename T, typename TT> - class Matcher<TBasicStringBuf<T, TT>>: public internal::MatcherBase<TBasicStringBuf<T, TT>> { - public: - Matcher() { - } - - explicit Matcher(const MatcherInterface <TBasicStringBuf<T, TT>>* impl) - : internal::MatcherBase<TBasicStringBuf<T, TT>>(impl) { - } - - explicit Matcher(const MatcherInterface<const TBasicStringBuf<T, TT>&>* impl) - : internal::MatcherBase<TBasicStringBuf<T, TT>>(impl) { - } - + Matcher(const TBasicString<T, TT>& s) { + *this = Eq(TBasicStringBuf<T, TT>(s)); + } + + Matcher(const T* s) { + *this = Eq(TBasicStringBuf<T, TT>(s)); + } + + Matcher(TBasicStringBuf<T, TT> s) { + *this = Eq(s); + } + }; + + /** + * When matching `TBasicBuf`, implicitly convert other strings and string views to `Eq` matchers. + */ + template <typename T, typename TT> + class Matcher<TBasicStringBuf<T, TT>>: public internal::MatcherBase<TBasicStringBuf<T, TT>> { + public: + Matcher() { + } + + explicit Matcher(const MatcherInterface <TBasicStringBuf<T, TT>>* impl) + : internal::MatcherBase<TBasicStringBuf<T, TT>>(impl) { + } + + explicit Matcher(const MatcherInterface<const TBasicStringBuf<T, TT>&>* impl) + : internal::MatcherBase<TBasicStringBuf<T, TT>>(impl) { + } + template <typename M, typename = typename std::remove_reference<M>::type::is_gtest_matcher> Matcher(M&& m) : internal::MatcherBase<TBasicStringBuf<T, TT>>(std::forward<M>(m)) { } - Matcher(const TBasicString<T, TT>& s) { - *this = Eq(TBasicString<T, TT>(s)); - } - - Matcher(const T* s) { - *this = Eq(TBasicString<T, TT>(s)); - } - - Matcher(TBasicStringBuf<T, TT> s) { - *this = Eq(s); - } - }; - - /** - * When matching `const TString&`, implicitly convert other strings and string views to `Eq` matchers. - */ - template <typename T, typename TT> - class Matcher<const TBasicString<T, TT>&>: public internal::MatcherBase<const TBasicString<T, TT>&> { - public: - Matcher() { - } - - explicit Matcher(const MatcherInterface<const TBasicString<T, TT>&>* impl) - : internal::MatcherBase<const TBasicString<T, TT>&>(impl) { - } - - Matcher(const TBasicString<T, TT>& s) { - *this = Eq(s); - } - + Matcher(const TBasicString<T, TT>& s) { + *this = Eq(TBasicString<T, TT>(s)); + } + + Matcher(const T* s) { + *this = Eq(TBasicString<T, TT>(s)); + } + + Matcher(TBasicStringBuf<T, TT> s) { + *this = Eq(s); + } + }; + + /** + * When matching `const TString&`, implicitly convert other strings and string views to `Eq` matchers. + */ + template <typename T, typename TT> + class Matcher<const TBasicString<T, TT>&>: public internal::MatcherBase<const TBasicString<T, TT>&> { + public: + Matcher() { + } + + explicit Matcher(const MatcherInterface<const TBasicString<T, TT>&>* impl) + : internal::MatcherBase<const TBasicString<T, TT>&>(impl) { + } + + Matcher(const TBasicString<T, TT>& s) { + *this = Eq(s); + } + template <typename M, typename = typename std::remove_reference<M>::type::is_gtest_matcher> Matcher(M&& m) : internal::MatcherBase<const TBasicString<T, TT>&>(std::forward<M>(m)) { } - Matcher(const T* s) { - *this = Eq(TBasicString<T, TT>(s)); - } - }; - - /** - * When matching `TString`, implicitly convert other strings and string views to `Eq` matchers. - */ - template <typename T, typename TT> - class Matcher<TBasicString<T, TT>>: public internal::MatcherBase<TBasicString<T, TT>> { - public: - Matcher() { - } - - explicit Matcher(const MatcherInterface <TBasicString<T, TT>>* impl) - : internal::MatcherBase<TBasicString<T, TT>>(impl) { - } - - explicit Matcher(const MatcherInterface<const TBasicString<T, TT>&>* impl) - : internal::MatcherBase<TBasicString<T, TT>>(impl) { - } - + Matcher(const T* s) { + *this = Eq(TBasicString<T, TT>(s)); + } + }; + + /** + * When matching `TString`, implicitly convert other strings and string views to `Eq` matchers. + */ + template <typename T, typename TT> + class Matcher<TBasicString<T, TT>>: public internal::MatcherBase<TBasicString<T, TT>> { + public: + Matcher() { + } + + explicit Matcher(const MatcherInterface <TBasicString<T, TT>>* impl) + : internal::MatcherBase<TBasicString<T, TT>>(impl) { + } + + explicit Matcher(const MatcherInterface<const TBasicString<T, TT>&>* impl) + : internal::MatcherBase<TBasicString<T, TT>>(impl) { + } + template <typename M, typename = typename std::remove_reference<M>::type::is_gtest_matcher> Matcher(M&& m) : internal::MatcherBase<TBasicString<T, TT>>(std::forward<M>(m)) { } - Matcher(const TBasicString<T, TT>& s) { - *this = Eq(s); - } - - Matcher(const T* s) { - *this = Eq(TBasicString<T, TT>(s)); - } - }; -} + Matcher(const TBasicString<T, TT>& s) { + *this = Eq(s); + } + + Matcher(const T* s) { + *this = Eq(TBasicString<T, TT>(s)); + } + }; +} diff --git a/library/cpp/testing/gtest_extensions/pretty_printers.cpp b/library/cpp/testing/gtest_extensions/pretty_printers.cpp index 401745cbcb..588584303b 100644 --- a/library/cpp/testing/gtest_extensions/pretty_printers.cpp +++ b/library/cpp/testing/gtest_extensions/pretty_printers.cpp @@ -1 +1 @@ -#include "pretty_printers.h" +#include "pretty_printers.h" diff --git a/library/cpp/testing/gtest_extensions/pretty_printers.h b/library/cpp/testing/gtest_extensions/pretty_printers.h index 14d8284446..28e51700f6 100644 --- a/library/cpp/testing/gtest_extensions/pretty_printers.h +++ b/library/cpp/testing/gtest_extensions/pretty_printers.h @@ -1,84 +1,84 @@ -#pragma once - -#include <util/generic/string.h> -#include <util/generic/strbuf.h> -#include <util/generic/maybe.h> -#include <util/generic/variant.h> -#include <util/stream/output.h> -#include <util/stream/str.h> -#include <util/datetime/base.h> - +#pragma once + +#include <util/generic/string.h> +#include <util/generic/strbuf.h> +#include <util/generic/maybe.h> +#include <util/generic/variant.h> +#include <util/stream/output.h> +#include <util/stream/str.h> +#include <util/datetime/base.h> + #include <gtest/gtest.h> #include <gmock/gmock.h> - -/** - * Automatically define GTest pretty printer for type that can print itself to util's `IOutputStream`. - * - * Note that this macro should be instantiated in the same namespace as the type you're printing, otherwise - * ADL will not find it. - * - * Example: - * - * We define a struct `TMyContainer` and an output operator that works with `IOutputStream`. We then use this macro - * to automatically define GTest pretty printer: - * - * ``` - * namespace NMy { - * struct TMyContainer { - * int x, y; - * }; - * } - * - * template <> - * inline void Out<NMy::TMyContainer>(IOutputStream& stream, TTypeTraits<NMy::TMyContainer>::TFuncParam value) { - * stream << "{ x=" << value.x << ", y=" << value.y << " }"; - * } - * - * namespace NMy { - * Y_GTEST_ARCADIA_PRINTER(TMyContainer) - * } - * ``` - */ + +/** + * Automatically define GTest pretty printer for type that can print itself to util's `IOutputStream`. + * + * Note that this macro should be instantiated in the same namespace as the type you're printing, otherwise + * ADL will not find it. + * + * Example: + * + * We define a struct `TMyContainer` and an output operator that works with `IOutputStream`. We then use this macro + * to automatically define GTest pretty printer: + * + * ``` + * namespace NMy { + * struct TMyContainer { + * int x, y; + * }; + * } + * + * template <> + * inline void Out<NMy::TMyContainer>(IOutputStream& stream, TTypeTraits<NMy::TMyContainer>::TFuncParam value) { + * stream << "{ x=" << value.x << ", y=" << value.y << " }"; + * } + * + * namespace NMy { + * Y_GTEST_ARCADIA_PRINTER(TMyContainer) + * } + * ``` + */ #define Y_GTEST_ARCADIA_PRINTER(T) \ void PrintTo(const T& value, std::ostream* stream) { \ - ::TString ss; \ - ::TStringOutput s{ss}; \ - s << value; \ - *stream << ss; \ - } - - + ::TString ss; \ + ::TStringOutput s{ss}; \ + s << value; \ + *stream << ss; \ + } + + template <typename TCharType, typename TCharTraits> void PrintTo(const TBasicString<TCharType, TCharTraits>& value, std::ostream* stream) { - *stream << value.Quote().c_str(); -} - + *stream << value.Quote().c_str(); +} + template <typename TCharType, typename TCharTraits> void PrintTo(TBasicStringBuf<TCharType, TCharTraits> value, std::ostream* stream) { *stream << TBasicString<TCharType, TCharTraits>{value}.Quote().c_str(); -} - -template <typename T, typename P> +} + +template <typename T, typename P> void PrintTo(const TMaybe<T, P>& value, std::ostream* stream) { - if (value.Defined()) { - ::testing::internal::UniversalPrint(value.GetRef(), stream); - } else { - *stream << "nothing"; - } -} - + if (value.Defined()) { + ::testing::internal::UniversalPrint(value.GetRef(), stream); + } else { + *stream << "nothing"; + } +} + inline void PrintTo(TNothing /* value */, std::ostream* stream) { - *stream << "nothing"; -} - + *stream << "nothing"; +} + inline void PrintTo(std::monostate /* value */, std::ostream* stream) { - *stream << "monostate"; -} - -inline void PrintTo(TInstant value, std::ostream* stream) { - *stream << value.ToString(); -} - -inline void PrintTo(TDuration value, std::ostream* stream) { - *stream << value.ToString(); -} + *stream << "monostate"; +} + +inline void PrintTo(TInstant value, std::ostream* stream) { + *stream << value.ToString(); +} + +inline void PrintTo(TDuration value, std::ostream* stream) { + *stream << value.ToString(); +} diff --git a/library/cpp/testing/gtest_extensions/ut/README.md b/library/cpp/testing/gtest_extensions/ut/README.md index ee8d212c18..71fff4e8d3 100644 --- a/library/cpp/testing/gtest_extensions/ut/README.md +++ b/library/cpp/testing/gtest_extensions/ut/README.md @@ -1 +1 @@ -Note: integration tests are located in */devtools/ya/test/tests/gtest_beta*. Launch them as well after changing this library. +Note: integration tests are located in */devtools/ya/test/tests/gtest_beta*. Launch them as well after changing this library. diff --git a/library/cpp/testing/gtest_extensions/ut/gtest_extensions_ut.cpp b/library/cpp/testing/gtest_extensions/ut/gtest_extensions_ut.cpp index 81cdfd0427..de39aeeb2e 100644 --- a/library/cpp/testing/gtest_extensions/ut/gtest_extensions_ut.cpp +++ b/library/cpp/testing/gtest_extensions/ut/gtest_extensions_ut.cpp @@ -1,9 +1,9 @@ -#include <library/cpp/testing/gtest/gtest.h> +#include <library/cpp/testing/gtest/gtest.h> -#include <util/generic/string.h> -#include <util/generic/maybe.h> -#include <util/stream/output.h> -#include <util/stream/str.h> +#include <util/generic/string.h> +#include <util/generic/maybe.h> +#include <util/stream/output.h> +#include <util/stream/str.h> namespace { class IMock { @@ -24,323 +24,323 @@ namespace { } -TEST(MatchersSpecializations, String) { - TSampleMock mock; +TEST(MatchersSpecializations, String) { + TSampleMock mock; - TStringBuf simpleStringBuf = "SimpleStringBuf"; - const TStringBuf constSimpleStringBuf = "ConstSimpleStringBuf"; + TStringBuf simpleStringBuf = "SimpleStringBuf"; + const TStringBuf constSimpleStringBuf = "ConstSimpleStringBuf"; - TString simpleString = "SimpleString"; - const TString constSimpleString = "ConstSimpleString"; + TString simpleString = "SimpleString"; + const TString constSimpleString = "ConstSimpleString"; - EXPECT_CALL(mock, M1("ConstSimpleStringBuf")).Times(1); - EXPECT_CALL(mock, M2("SimpleStringBuf")).Times(1); - EXPECT_CALL(mock, M3("ConstSimpleString")).Times(1); - EXPECT_CALL(mock, M4("SimpleString")).Times(1); + EXPECT_CALL(mock, M1("ConstSimpleStringBuf")).Times(1); + EXPECT_CALL(mock, M2("SimpleStringBuf")).Times(1); + EXPECT_CALL(mock, M3("ConstSimpleString")).Times(1); + EXPECT_CALL(mock, M4("SimpleString")).Times(1); - mock.M1(constSimpleStringBuf); - mock.M2(simpleStringBuf); - mock.M3(constSimpleString); - mock.M4(simpleString); -} - -template <typename T, typename M> -std::pair<bool, std::string> Match(T&& t, M&& m) { - testing::StringMatchResultListener listener; - auto matches = testing::SafeMatcherCast<T>(std::forward<M>(m)).MatchAndExplain(std::forward<T>(t), &listener); - return {matches, listener.str()}; -} + mock.M1(constSimpleStringBuf); + mock.M2(simpleStringBuf); + mock.M3(constSimpleString); + mock.M4(simpleString); +} -TEST(Matchers, Throws) { +template <typename T, typename M> +std::pair<bool, std::string> Match(T&& t, M&& m) { + testing::StringMatchResultListener listener; + auto matches = testing::SafeMatcherCast<T>(std::forward<M>(m)).MatchAndExplain(std::forward<T>(t), &listener); + return {matches, listener.str()}; +} + +TEST(Matchers, Throws) { auto matcher = testing::Throws<std::runtime_error>(); - - { - std::stringstream ss; - testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); - auto explanation = ss.str(); - - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); - EXPECT_TRUE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw 10; }, matcher); - EXPECT_FALSE(matched); + + { + std::stringstream ss; + testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); + auto explanation = ss.str(); + + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); + EXPECT_TRUE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw 10; }, matcher); + EXPECT_FALSE(matched); EXPECT_THAT(explanation, testing::HasSubstr("throws an exception of an unknown type")); - } - - { - auto [matched, explanation] = Match([]() { (void)0; }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); - } -} - -TEST(Matchers, ThrowsMessage) { + } + + { + auto [matched, explanation] = Match([]() { (void)0; }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); + } +} + +TEST(Matchers, ThrowsMessage) { auto matcher = testing::ThrowsMessage<std::runtime_error>(testing::HasSubstr("error message")); - - { - std::stringstream ss; - testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); - auto explanation = ss.str(); - - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); - EXPECT_TRUE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw 10; }, matcher); - EXPECT_FALSE(matched); + + { + std::stringstream ss; + testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); + auto explanation = ss.str(); + + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); + EXPECT_TRUE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw 10; }, matcher); + EXPECT_FALSE(matched); EXPECT_THAT(explanation, testing::HasSubstr("throws an exception of an unknown type")); - } - - { - auto [matched, explanation] = Match([]() { (void)0; }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); - } -} - -TEST(Matchers, ThrowsMessageHasSubstr) { + } + + { + auto [matched, explanation] = Match([]() { (void)0; }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); + } +} + +TEST(Matchers, ThrowsMessageHasSubstr) { auto matcher = testing::ThrowsMessage<std::runtime_error>(testing::HasSubstr("error message")); - - { - std::stringstream ss; - testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); - auto explanation = ss.str(); - - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); - EXPECT_TRUE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw 10; }, matcher); - EXPECT_FALSE(matched); + + { + std::stringstream ss; + testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); + auto explanation = ss.str(); + + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); + EXPECT_TRUE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw 10; }, matcher); + EXPECT_FALSE(matched); EXPECT_THAT(explanation, testing::HasSubstr("throws an exception of an unknown type")); - } - - { - auto [matched, explanation] = Match([]() { (void)0; }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); - } -} - -TEST(Matchers, ThrowsCondition) { + } + + { + auto [matched, explanation] = Match([]() { (void)0; }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); + } +} + +TEST(Matchers, ThrowsCondition) { auto matcher = testing::Throws<std::runtime_error>( - testing::Property(&std::exception::what, testing::HasSubstr("error message"))); - - { - std::stringstream ss; - testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); - auto explanation = ss.str(); - - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); - EXPECT_TRUE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - } - - { - auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"message error\"")); - } - - { - auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); - EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); - } - - { - auto [matched, explanation] = Match([]() { throw 10; }, matcher); - EXPECT_FALSE(matched); + testing::Property(&std::exception::what, testing::HasSubstr("error message"))); + + { + std::stringstream ss; + testing::SafeMatcherCast<void(*)()>(matcher).DescribeTo(&ss); + auto explanation = ss.str(); + + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("error message"); }, matcher); + EXPECT_TRUE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + } + + { + auto [matched, explanation] = Match([]() { throw std::runtime_error("message error"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::runtime_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"message error\"")); + } + + { + auto [matched, explanation] = Match([]() { throw std::logic_error("error message"); }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("std::logic_error")); + EXPECT_THAT(explanation, testing::HasSubstr("\"error message\"")); + } + + { + auto [matched, explanation] = Match([]() { throw 10; }, matcher); + EXPECT_FALSE(matched); EXPECT_THAT(explanation, testing::HasSubstr("throws an exception of an unknown type")); + } + + { + auto [matched, explanation] = Match([]() { (void)0; }, matcher); + EXPECT_FALSE(matched); + EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); + } +} + +template <typename T> +std::string GtestPrint(T&& v) { + std::stringstream ss; + testing::internal::UniversalPrint(std::forward<T>(v), &ss); + return ss.str(); +} + +struct TThrowsOnMove { + TThrowsOnMove() = default; + TThrowsOnMove(TThrowsOnMove&&) { + ythrow yexception() << "move failed"; } - - { - auto [matched, explanation] = Match([]() { (void)0; }, matcher); - EXPECT_FALSE(matched); - EXPECT_THAT(explanation, testing::HasSubstr("does not throw any exception")); - } -} - -template <typename T> -std::string GtestPrint(T&& v) { - std::stringstream ss; - testing::internal::UniversalPrint(std::forward<T>(v), &ss); - return ss.str(); -} - -struct TThrowsOnMove { - TThrowsOnMove() = default; - TThrowsOnMove(TThrowsOnMove&&) { - ythrow yexception() << "move failed"; - } -}; - -TEST(PrettyPrinters, String) { - EXPECT_EQ(GtestPrint(TString("hello world")), "\"hello world\""); - EXPECT_EQ(GtestPrint(TStringBuf("hello world")), "\"hello world\""); -} - -TEST(PrettyPrinters, Maybe) { - EXPECT_EQ(GtestPrint(TMaybe<TString>("hello world")), "\"hello world\""); - EXPECT_EQ(GtestPrint(TMaybe<TString>()), "nothing"); - EXPECT_EQ(GtestPrint(Nothing()), "nothing"); -} - -struct T1 { - int x; -}; - +}; + +TEST(PrettyPrinters, String) { + EXPECT_EQ(GtestPrint(TString("hello world")), "\"hello world\""); + EXPECT_EQ(GtestPrint(TStringBuf("hello world")), "\"hello world\""); +} + +TEST(PrettyPrinters, Maybe) { + EXPECT_EQ(GtestPrint(TMaybe<TString>("hello world")), "\"hello world\""); + EXPECT_EQ(GtestPrint(TMaybe<TString>()), "nothing"); + EXPECT_EQ(GtestPrint(Nothing()), "nothing"); +} + +struct T1 { + int x; +}; + void PrintTo(T1 value, std::ostream* stream) { - *stream << "T1{" << value.x << "}"; -} - -struct T2 { - int x; -}; - -Y_DECLARE_OUT_SPEC(inline, T2, stream, value) { - stream << "T2{" << value.x << "}"; -} - -Y_GTEST_ARCADIA_PRINTER(T2) - -TEST(PrettyPrinters, Custom) { - EXPECT_EQ(GtestPrint(T1{10}), "T1{10}"); -} - -TEST(PrettyPrinters, CustomArcadia) { - EXPECT_EQ(GtestPrint(T2{10}), "T2{10}"); -} - -TEST(Exceptions, ExpectThrow) { - EXPECT_THROW(ythrow yexception() << "msg", yexception); -} - -TEST(Exceptions, ExpectThrowStructuredBindings) { - auto [a, b] = std::make_pair("a", "b"); - EXPECT_THROW(throw yexception() << a << "-" << b, yexception); -} - -TEST(Exceptions, ExpectThrowSkipInThrowTest) { - // this test should be skipped, not failed - EXPECT_THROW(GTEST_SKIP(), yexception); -} - -TEST(Exceptions, AssertThrow) { - ASSERT_THROW(ythrow yexception() << "msg", yexception); -} - -TEST(Exceptions, AssertThrowStructuredBindings) { - auto [a, b] = std::make_pair("a", "b"); - ASSERT_THROW(throw yexception() << a << "-" << b, yexception); -} - -TEST(Exceptions, AssertThrowSkipInThrowTest) { - // this test should be skipped, not failed - ASSERT_THROW(GTEST_SKIP(), yexception); -} - -TEST(Exceptions, ExpectThrowMessageHasSubstr) { - EXPECT_THROW_MESSAGE_HAS_SUBSTR(ythrow yexception() << "msg", yexception, "msg"); -} - -TEST(Exceptions, ExpectThrowMessageHasSubstrStructuredBindings) { - auto [a, b] = std::make_pair("a", "b"); - EXPECT_THROW_MESSAGE_HAS_SUBSTR(throw yexception() << a << "-" << b, yexception, "-"); -} - -TEST(Exceptions, ExpectThrowMessageHasSubstrSkipInThrowTest) { - // this test should be skipped, not failed - EXPECT_THROW_MESSAGE_HAS_SUBSTR(GTEST_SKIP(), yexception, "-"); -} - -TEST(Exceptions, AssertThrowMessageHasSubstr) { - ASSERT_THROW_MESSAGE_HAS_SUBSTR(ythrow yexception() << "msg", yexception, "msg"); -} - -TEST(Exceptions, AssertThrowMessageHasSubstrStructuredBindings) { - auto [a, b] = std::make_pair("a", "b"); - ASSERT_THROW_MESSAGE_HAS_SUBSTR(throw yexception() << a << "-" << b, yexception, "-"); -} - -TEST(Exceptions, AssertThrowMessageHasSubstrSkipInThrowTest) { - // this test should be skipped, not failed - ASSERT_THROW_MESSAGE_HAS_SUBSTR(GTEST_SKIP(), yexception, "-"); -} - -TEST(Exceptions, ExpectNoThrow) { - EXPECT_NO_THROW((void)0); -} - -TEST(Exceptions, AssertNoThrow) { - ASSERT_NO_THROW((void)0); -} - -TEST(Exceptions, ExpectAnyThrow) { - EXPECT_ANY_THROW(ythrow yexception() << "msg"); -} - -TEST(Exceptions, AssertAnyThrow) { - ASSERT_ANY_THROW(ythrow yexception() << "msg"); -} + *stream << "T1{" << value.x << "}"; +} + +struct T2 { + int x; +}; + +Y_DECLARE_OUT_SPEC(inline, T2, stream, value) { + stream << "T2{" << value.x << "}"; +} + +Y_GTEST_ARCADIA_PRINTER(T2) + +TEST(PrettyPrinters, Custom) { + EXPECT_EQ(GtestPrint(T1{10}), "T1{10}"); +} + +TEST(PrettyPrinters, CustomArcadia) { + EXPECT_EQ(GtestPrint(T2{10}), "T2{10}"); +} + +TEST(Exceptions, ExpectThrow) { + EXPECT_THROW(ythrow yexception() << "msg", yexception); +} + +TEST(Exceptions, ExpectThrowStructuredBindings) { + auto [a, b] = std::make_pair("a", "b"); + EXPECT_THROW(throw yexception() << a << "-" << b, yexception); +} + +TEST(Exceptions, ExpectThrowSkipInThrowTest) { + // this test should be skipped, not failed + EXPECT_THROW(GTEST_SKIP(), yexception); +} + +TEST(Exceptions, AssertThrow) { + ASSERT_THROW(ythrow yexception() << "msg", yexception); +} + +TEST(Exceptions, AssertThrowStructuredBindings) { + auto [a, b] = std::make_pair("a", "b"); + ASSERT_THROW(throw yexception() << a << "-" << b, yexception); +} + +TEST(Exceptions, AssertThrowSkipInThrowTest) { + // this test should be skipped, not failed + ASSERT_THROW(GTEST_SKIP(), yexception); +} + +TEST(Exceptions, ExpectThrowMessageHasSubstr) { + EXPECT_THROW_MESSAGE_HAS_SUBSTR(ythrow yexception() << "msg", yexception, "msg"); +} + +TEST(Exceptions, ExpectThrowMessageHasSubstrStructuredBindings) { + auto [a, b] = std::make_pair("a", "b"); + EXPECT_THROW_MESSAGE_HAS_SUBSTR(throw yexception() << a << "-" << b, yexception, "-"); +} + +TEST(Exceptions, ExpectThrowMessageHasSubstrSkipInThrowTest) { + // this test should be skipped, not failed + EXPECT_THROW_MESSAGE_HAS_SUBSTR(GTEST_SKIP(), yexception, "-"); +} + +TEST(Exceptions, AssertThrowMessageHasSubstr) { + ASSERT_THROW_MESSAGE_HAS_SUBSTR(ythrow yexception() << "msg", yexception, "msg"); +} + +TEST(Exceptions, AssertThrowMessageHasSubstrStructuredBindings) { + auto [a, b] = std::make_pair("a", "b"); + ASSERT_THROW_MESSAGE_HAS_SUBSTR(throw yexception() << a << "-" << b, yexception, "-"); +} + +TEST(Exceptions, AssertThrowMessageHasSubstrSkipInThrowTest) { + // this test should be skipped, not failed + ASSERT_THROW_MESSAGE_HAS_SUBSTR(GTEST_SKIP(), yexception, "-"); +} + +TEST(Exceptions, ExpectNoThrow) { + EXPECT_NO_THROW((void)0); +} + +TEST(Exceptions, AssertNoThrow) { + ASSERT_NO_THROW((void)0); +} + +TEST(Exceptions, ExpectAnyThrow) { + EXPECT_ANY_THROW(ythrow yexception() << "msg"); +} + +TEST(Exceptions, AssertAnyThrow) { + ASSERT_ANY_THROW(ythrow yexception() << "msg"); +} diff --git a/library/cpp/testing/gtest_extensions/ut/ya.make b/library/cpp/testing/gtest_extensions/ut/ya.make index 39b41cecfd..b68cb4645b 100644 --- a/library/cpp/testing/gtest_extensions/ut/ya.make +++ b/library/cpp/testing/gtest_extensions/ut/ya.make @@ -1,20 +1,20 @@ GTEST() OWNER( - amatanhead + amatanhead bulatman dancingqueue - prettyboy - thegeorg + prettyboy + thegeorg g:cpp-contrib ) SRCS( - gtest_extensions_ut.cpp + gtest_extensions_ut.cpp probe_ut.cpp ) PEERDIR( - library/cpp/testing/gtest_extensions + library/cpp/testing/gtest_extensions ) END() diff --git a/library/cpp/testing/gtest_extensions/ya.make b/library/cpp/testing/gtest_extensions/ya.make index e24e81e8bd..b4871724bb 100644 --- a/library/cpp/testing/gtest_extensions/ya.make +++ b/library/cpp/testing/gtest_extensions/ya.make @@ -1,10 +1,10 @@ LIBRARY() OWNER( - amatanhead + amatanhead bulatman dancingqueue - prettyboy - thegeorg + prettyboy + thegeorg g:cpp-contrib ) @@ -14,10 +14,10 @@ PEERDIR( ) SRCS( - assertions.cpp - gtest_extensions.cpp - matchers.cpp - pretty_printers.cpp + assertions.cpp + gtest_extensions.cpp + matchers.cpp + pretty_printers.cpp probe.cpp ) diff --git a/library/cpp/testing/unittest/gtest.h b/library/cpp/testing/unittest/gtest.h index b6768b1bf0..28e40e177d 100644 --- a/library/cpp/testing/unittest/gtest.h +++ b/library/cpp/testing/unittest/gtest.h @@ -1,10 +1,10 @@ #pragma once -// WARNING: this is a legacy header that tries to mimic the gtest interface while using unittest -// under the hood. Avoid using this interface -- use the genuine gtest instead (the GTEST macro). -// If you're already using GTEST macro and you've found yourself here, you probably meant -// to include `library/cpp/testing/gtest/gtest.h`. - +// WARNING: this is a legacy header that tries to mimic the gtest interface while using unittest +// under the hood. Avoid using this interface -- use the genuine gtest instead (the GTEST macro). +// If you're already using GTEST macro and you've found yourself here, you probably meant +// to include `library/cpp/testing/gtest/gtest.h`. + #include "registar.h" #include <util/generic/ymath.h> diff --git a/library/cpp/testing/ya.make b/library/cpp/testing/ya.make index 6a57ac2ee6..f743b8ae46 100644 --- a/library/cpp/testing/ya.make +++ b/library/cpp/testing/ya.make @@ -11,10 +11,10 @@ RECURSE( gmock gmock_in_unittest gmock_in_unittest/example_ut - gtest + gtest gtest_boost_extensions - gtest_extensions - gtest_main + gtest_extensions + gtest_main gtest_protobuf hook mock_server diff --git a/library/cpp/threading/future/core/future-inl.h b/library/cpp/threading/future/core/future-inl.h index 5fd4296a93..d68d80e536 100644 --- a/library/cpp/threading/future/core/future-inl.h +++ b/library/cpp/threading/future/core/future-inl.h @@ -533,7 +533,7 @@ namespace NThreading { //////////////////////////////////////////////////////////////////////////////// template <typename T> - inline TFuture<T>::TFuture(const TIntrusivePtr<TFutureState>& state) noexcept + inline TFuture<T>::TFuture(const TIntrusivePtr<TFutureState>& state) noexcept : State(state) { } @@ -655,7 +655,7 @@ namespace NThreading { //////////////////////////////////////////////////////////////////////////////// - inline TFuture<void>::TFuture(const TIntrusivePtr<TFutureState>& state) noexcept + inline TFuture<void>::TFuture(const TIntrusivePtr<TFutureState>& state) noexcept : State(state) { } @@ -758,7 +758,7 @@ namespace NThreading { //////////////////////////////////////////////////////////////////////////////// template <typename T> - inline TPromise<T>::TPromise(const TIntrusivePtr<TFutureState>& state) noexcept + inline TPromise<T>::TPromise(const TIntrusivePtr<TFutureState>& state) noexcept : State(state) { } @@ -864,7 +864,7 @@ namespace NThreading { //////////////////////////////////////////////////////////////////////////////// - inline TPromise<void>::TPromise(const TIntrusivePtr<TFutureState>& state) noexcept + inline TPromise<void>::TPromise(const TIntrusivePtr<TFutureState>& state) noexcept : State(state) { } diff --git a/library/cpp/threading/future/core/future.h b/library/cpp/threading/future/core/future.h index 2e82bb953e..48dc209673 100644 --- a/library/cpp/threading/future/core/future.h +++ b/library/cpp/threading/future/core/future.h @@ -76,12 +76,12 @@ namespace NThreading { public: using value_type = T; - TFuture() noexcept = default; - TFuture(const TFuture<T>& other) noexcept = default; + TFuture() noexcept = default; + TFuture(const TFuture<T>& other) noexcept = default; TFuture(TFuture<T>&& other) noexcept = default; - TFuture(const TIntrusivePtr<TFutureState>& state) noexcept; + TFuture(const TIntrusivePtr<TFutureState>& state) noexcept; - TFuture<T>& operator=(const TFuture<T>& other) noexcept = default; + TFuture<T>& operator=(const TFuture<T>& other) noexcept = default; TFuture<T>& operator=(TFuture<T>&& other) noexcept = default; void Swap(TFuture<T>& other); @@ -128,17 +128,17 @@ namespace NThreading { using TFutureState = NImpl::TFutureState<void>; private: - TIntrusivePtr<TFutureState> State = nullptr; + TIntrusivePtr<TFutureState> State = nullptr; public: using value_type = void; - TFuture() noexcept = default; - TFuture(const TFuture<void>& other) noexcept = default; + TFuture() noexcept = default; + TFuture(const TFuture<void>& other) noexcept = default; TFuture(TFuture<void>&& other) noexcept = default; - TFuture(const TIntrusivePtr<TFutureState>& state) noexcept; + TFuture(const TIntrusivePtr<TFutureState>& state) noexcept; - TFuture<void>& operator=(const TFuture<void>& other) noexcept = default; + TFuture<void>& operator=(const TFuture<void>& other) noexcept = default; TFuture<void>& operator=(TFuture<void>&& other) noexcept = default; void Swap(TFuture<void>& other); @@ -188,15 +188,15 @@ namespace NThreading { using TFutureState = NImpl::TFutureState<T>; private: - TIntrusivePtr<TFutureState> State = nullptr; + TIntrusivePtr<TFutureState> State = nullptr; public: - TPromise() noexcept = default; - TPromise(const TPromise<T>& other) noexcept = default; + TPromise() noexcept = default; + TPromise(const TPromise<T>& other) noexcept = default; TPromise(TPromise<T>&& other) noexcept = default; - TPromise(const TIntrusivePtr<TFutureState>& state) noexcept; + TPromise(const TIntrusivePtr<TFutureState>& state) noexcept; - TPromise<T>& operator=(const TPromise<T>& other) noexcept = default; + TPromise<T>& operator=(const TPromise<T>& other) noexcept = default; TPromise<T>& operator=(TPromise<T>&& other) noexcept = default; void Swap(TPromise<T>& other); @@ -235,12 +235,12 @@ namespace NThreading { TIntrusivePtr<TFutureState> State; public: - TPromise() noexcept = default; - TPromise(const TPromise<void>& other) noexcept = default; + TPromise() noexcept = default; + TPromise(const TPromise<void>& other) noexcept = default; TPromise(TPromise<void>&& other) noexcept = default; - TPromise(const TIntrusivePtr<TFutureState>& state) noexcept; + TPromise(const TIntrusivePtr<TFutureState>& state) noexcept; - TPromise<void>& operator=(const TPromise<void>& other) noexcept = default; + TPromise<void>& operator=(const TPromise<void>& other) noexcept = default; TPromise<void>& operator=(TPromise<void>&& other) noexcept = default; void Swap(TPromise<void>& other); |