{"id":18515283,"url":"https://github.com/chokobole/console","last_synced_at":"2025-04-09T06:34:54.267Z","repository":{"id":50659273,"uuid":"230723449","full_name":"chokobole/console","owner":"chokobole","description":"A C++ collection of library for console application.","archived":false,"fork":false,"pushed_at":"2020-01-11T08:39:34.000Z","size":770,"stargazers_count":8,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T01:01:39.241Z","etag":null,"topics":["animation","ansi","ansi-escape-codes","bazel-support","cpp","cpp-library","flag","terminal","vt100"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chokobole.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-29T08:27:04.000Z","updated_at":"2024-11-25T23:13:57.000Z","dependencies_parsed_at":"2022-09-18T19:01:13.263Z","dependency_job_id":null,"html_url":"https://github.com/chokobole/console","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fconsole","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fconsole/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fconsole/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fconsole/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chokobole","download_url":"https://codeload.github.com/chokobole/console/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247993668,"owners_count":21030043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","ansi","ansi-escape-codes","bazel-support","cpp","cpp-library","flag","terminal","vt100"],"created_at":"2024-11-06T15:47:13.844Z","updated_at":"2025-04-09T06:34:49.252Z","avatar_url":"https://github.com/chokobole.png","language":"C++","readme":"# Console\n\nA C++ collection of library for console application.\n\n## Contents\n- [Console](#console)\n  - [Contents](#contents)\n  - [How to use](#how-to-use)\n  - [Usages](#usages)\n    - [ANSI / VT100 Terminal Control Escape Sequences](#ansi--vt100-terminal-control-escape-sequences)\n      - [Demo](#demo)\n      - [Overview](#overview)\n      - [Text Attribute](#text-attribute)\n      - [Color](#color)\n        - [Named color](#named-color)\n        - [Rgb](#rgb)\n      - [Etc](#etc)\n        - [Cursor control](#cursor-control)\n        - [Scrolling](#scrolling)\n        - [Tab Control](#tab-control)\n        - [Erasing Text](#erasing-text)\n    - [Animation](#animation)\n      - [Example](#example)\n      - [Predefined Animations](#predefined-animations)\n      - [Custom Animation](#custom-animation)\n    - [Flag](#flag)\n      - [Demo](#demo-1)\n      - [Overview](#overview-1)\n      - [Predefined Flags](#predefined-flags)\n      - [Vector Flag](#vector-flag)\n      - [Custom Flag](#custom-flag)\n      - [SubParser](#subparser)\n      - [Autocompletion](#autocompletion)\n\n## How to use\n\nTo use `console`, add the followings to your `WORKSPACE` file.\n\n```python\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"com_chokobole_console\",\n    sha256 = \"\u003csha256\u003e\",\n    strip_prefix = \"console-\u003ccommit\u003e\",\n    urls = [\n        \"https://github.com/chokobole/console/archive/\u003ccommit\u003e.tar.gz\",\n    ],\n)\n\nload(\"@com_chokobole_console//bazel:console_deps.bzl\", \"console_deps\")\n\nconsole_deps()\n\nload(\"@com_chokobole_color//bazel:color_deps.bzl\", \"color_deps\")\n\ncolor_deps()\n\nload(\"@com_chokobole_base//bazel:base_deps.bzl\", \"base_deps\")\n\nbase_deps()\n\nload(\"@com_chokobole_bazel_utils//:buildifier_deps.bzl\", \"buildifier_deps\")\n\nbuildifier_deps()\n\nload(\"@com_chokobole_bazel_utils//:buildifier_deps_deps.bzl\", \"buildifier_deps_deps\")\n\nbuildifier_deps_deps()\n```\n\nThen, in your `BUILD` files, import and use the rules.\n\n```python\ncc_binary(\n    name = \"name\",\n    srcs = [...],\n    deps = [\"@com_chokobole_console//:console\"],\n)\n```\n\n## Usages\n\n### ANSI / VT100 Terminal Control Escape Sequences\n\n#### Demo\n\n![resources/demo.gif](resources/demo.gif)\n\n#### Overview\n\nBefore going deeper, let's see the simple example below.\n\n```c++\n#include \u003ciostream\u003e\n#include \"console/stream.h\"\n\nconsole::Stream stream;\nstream.Red();\nstd::cout \u003c\u003c \"red\" \u003c\u003c std::endl;\n```\n\nThe example above prints out the `\"red\"` as `red` colored text. Ah simple! But for those who use [ANSI escaped code](https://en.wikipedia.org/wiki/ANSI_escape_code) for the first time, the second example might be troublesome.\n\n```c++\nconsole::Stream stream;\nstream.Red();\nstd::cout \u003c\u003c \"red\" \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"plain\" \u003c\u003c std::endl;\n```\n\nWhat color do you think the `\"plain\"` will be printed out? The answer is `red`. Why? Because you didn't reset the color, so the color code applied to `\"red\"` still was on. So you have to explicitly set the color code off like below.\n\n```c++\nconsole::Stream stream;\nstream.Red();\nstd::cout \u003c\u003c \"red\" \u003c\u003c std::endl;\nstream.ColorOff();\nstd::cout \u003c\u003c \"plain\" \u003c\u003c std::endl;\n```\n\nFor convenience, at destruction of `console::Stream` invalidate all the attributes. So it maybe helps you from calling ~Off methods everytime. **NOTE: But for the background, maybe you have to call `EraseEndOfLine` to clear the background.**\n\n```c++\n{\n  console::Stream stream;\n  stream.Red();\n  std::cout \u003c\u003c \"red\" \u003c\u003c std::endl;\n}\n```\n\nAlso you can give any kind of `std::ostream` to `console::Stream`, so that you can make a helper function like below.\n\n```c++\nstd::string Info(const std::string\u0026 text) {\n  std::stringstream ss;\n  {\n    console::Stream stream(ss);\n    stream.Green().Bold();\n    ss \u003c\u003c \"INFO: \";\n  }\n  ss \u003c\u003c text;\n  return ss.str();\n}\n```\n\n**NOTE: If you are working on windows, you have to enable ANSI like below.**\n\n```c++\n#if defined(OS_WIN)\n  console::Console::EnableAnsi(std::cout);\n#endif\n```\n\n#### Text Attribute\n\n* `Reset()`: All attributes off\n* `Bold()`: To off, call `BoldOff()`\n* `Dim()`: To off, call `DimdOff()`\n* `Italic()`: To off, call `ItalicOff()`\n* `Underline()`: To off, call `UnderlineOff()`\n* `BlinkSlow()`: To off, call `BlinkOff()`\n* `BlinkRapid()`: To off, call `BlinkOff()`\n* `Inverse()`: To off, call `InverseOff()`\n* `Conceal()`: To off, call `ConcealOff()`\n* `StrikeThrough()`: To off, call `StrikeThroughOff()`\n\n#### Color\n\nTo off color, call `ColorOff()`, to off background color, call `BgColorOff()`.\n\n##### Named color\n\n* `Black()`\n* `Red()`\n* `Green()`\n* `Yellow()`\n* `Blue()`\n* `Magenta()`\n* `Cyan()`\n* `White()`\n* `LightBlack()`\n* `LightRed()`\n* `LightGreen()`\n* `LightYellow()`\n* `LightBlue()`\n* `LightMagenta()`\n* `LightCyan()`\n* `LightWhite()`\n* `BgBlack()`\n* `BgRed()`\n* `BgGreen()`\n* `BgYellow()`\n* `BgBlue()`\n* `BgMagenta()`\n* `BgCyan()`\n* `BgWhite()`\n* `BgLightBlack()`\n* `BgLightRed()`\n* `BgLightGreen()`\n* `BgLightYellow()`\n* `BgLightBlue()`\n* `BgLightMagenta()`\n* `BgLightCyan()`\n* `BgLightWhite()`\n\n##### Rgb\n\nIf you want to use custom color, you can call `Rgb(color::Rgb)` method. It automatically detects whether the terminal supports [8-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) or [24-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit). You can read the [README.md](https://github.com/chokobole/color/blob/master/README.md) from [color](https://github.com/chokobole/color).\n\n#### Etc\n\n**NOTE: Explanation was taken and modified from http://www.termsys.demon.co.uk/vtansi.htm**\n\n##### Cursor control\n\n* `SetCursor(size_t row = 0, size_t column = 0)`: Sets the cursor position where subsequent text will begin. If no row/column parameters are provided, the cursor will move to the home position, at the upper left of the screen.\n* `CursorUp(size_t n = 1)`: Moves the cursor up by `n` rows.\n* `CursorDown(size_t n = 1)`: Moves the cursor down by `n` rows.\n* `CursorForward(size_t n = 1)`: Moves the cursor forward by `n` columns.\n* `CursorBackward(size_t n = 1)`: Moves the cursor backward by `n` columns.\n* `SaveCursor()`: Saves current cursor position.\n* `RestoreCursor()`: Restores cursor position after the save point.\n* `SaveCursorAndAttributes()`: Save current cursor position and attributes.\n* `RestoreCursorAndAttributes()`: Restores cursor position after the save point and attributes.\n\n##### Scrolling\n\n* `ScrollScreen()`: Enable scrolling for entire display.\n* `ScrollScreen(size_t start, size_t end)`: Enable scrolling from row `start` to row `end`.\n* `ScrollDown()`: Scroll display down one line.\n* `ScrollUp()`: Scroll display up one line.\n\n##### Tab Control\n\n* `SetTab()`: Sets a tab at the current position.\n* `ClearTab()`: Clears tab at the current position.\n* `ClearAllTab()`: Clears all tabs.\n\n##### Erasing Text\n\n* `EraseEndOfLine()`: Erases from the current cursor position to the end of the current line.\n* `EraseStartOfLine()`: Erases from the current cursor position to the start of the current line.\n* `EraseEntireLine()`: Erases the entire currnet line.\n* `EraseDown()`: Erases the screen from the current line down to the bottom of the screen.\n* `EraseUp()`: Erases the screen from the current line up to the top of the screen.\n* `EraseScreen()`: Erases the screen with the background colour and moves the cursor to home.\n\n### Animation\n\n#### Example\n\nLet's see a simple example like below.\n\n```c++\n#include \u003cchrono\u003e\n#include \u003cthread\u003e\n\n#include \"color/colormap.h\"\n#include \"console/animation.h\"\n#include \"console/stream.h\"\n\nint main() {\n  console::Stream stream;\n  color::Colormap colormap;\n  std::vector\u003ccolor::Rgb\u003e rainbow_colors;\n  colormap.Rainbow(30, \u0026rainbow_colors);\n\n  const char* text =\n      \"Lorem ipsum dolor sit amet, consectetur adipiscing elit\\n\";\n\n  std::unique_ptr\u003cconsole::FlowTextAnimation\u003e flow_animation(\n      new console::FlowTextAnimation());\n  flow_animation-\u003eset_repeat(true);\n  flow_animation-\u003eset_colors(rainbow_colors);\n  flow_animation-\u003eset_text(text);\n  console::AnimationGroup group;\n  group.AddAnimation(std::move(flow_animation));\n  group.set_on_animation_did_update([](size_t) {\n    console::Stream stream;\n    stream.CursorUp(1);\n    stream.EraseEndOfLine();\n  });\n\n  while (true) {\n    group.Update();\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n  }\n\n  return 0;\n}\n```\n\n#### Predefined Animations\n\n* `FlowTextAnimation`\n* `RadarTextAnimation`\n* `NeonTextAnimation`\n* `KaraokeTextAnimation`\n\n You can find how to use those animations in [examples/animation.cc](examples/animation.cc)\n\n#### Custom Animation\n\nAlso you can define custom animation like below! You can find the full code in [examples/custom_animation.cc](examples/custom_animation.cc)\n\n```c++\nclass ProgressAnimation : public console::Animation {\n public:\n  explicit ProgressAnimation(size_t total_frames)\n      : total_frames_(total_frames) {}\n\n private:\n  bool ShouldUpdate() override { return current_frame_ \u003c total_frames_; }\n\n  void DoUpdate() override {\n    int did = static_cast\u003cdouble\u003e(current_frame_ + 1) / total_frames_ * 50;\n    for (int i = 0; i \u003c did; ++i) {\n      std::cout \u003c\u003c \"=\";\n    }\n    std::cout \u003c\u003c \"\u003e\";\n    for (int i = 0; i \u003c 50 - did; ++i) {\n      std::cout \u003c\u003c \" \";\n    }\n    std::cout \u003c\u003c \"[\" \u003c\u003c (current_frame_ + 1) \u003c\u003c \" / \" \u003c\u003c total_frames_ \u003c\u003c \"]\\n\";\n  }\n\n  size_t total_frames_;\n};\n\nint main() {\n  bool updating = true;\n  ProgressAnimation animation(100);\n  animation.set_on_animation_end([\u0026updating] { updating = false; });\n  animation.set_on_animation_did_update([](size_t) {\n    console::Stream stream;\n    stream.CursorUp(1);\n    stream.EraseEntireLine();\n  });\n\n  do {\n    animation.Update();\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n  } while (updating);\n\n  return 0;\n}\n```\n\n### Flag\n\n#### Demo\n\n![resources/demo2.gif](resources/demo2.gif)\n\n#### Overview\n\n* Easy\n* Explaniable\n* Customizable\n* Composable\n* Support autocomplete\n\nLet's see a simple example like below. You can find the full code in [examples/simple_flag.cc](examples/simple_flag.cc)\n\n```c++\n#include \"console/flag.h\"\n\nint main(int argc, char** argv) {\n  struct Person {\n    std::string name;\n    uint8_t age;\n    bool married;\n  };\n\n  Person person;\n  console::FlagParser flag_parser;\n  flag_parser.set_program_name(\"simple_flag\");\n  flag_parser.AddFlag\u003cconsole::StringFlag\u003e(\u0026person.name)\n      .set_name(\"name\")\n      .set_help(\"what's your name?\");\n  flag_parser.AddFlag\u003cconsole::Uint8Flag\u003e(\u0026person.age)\n      .set_name(\"age\")\n      .set_help(\"how old are you?\");\n  flag_parser.AddFlag\u003cconsole::BoolFlag\u003e(\u0026person.married)\n      .set_long_name(\"--married\")\n      .set_help(\"are you married?\");\n  flag_parser.Parse(argc, argv);\n}\n```\n\n```bash\n# bazel build //examples:simple_flag\n\n$ bazel-bin/examples/simple_flag foo 25 --married\n{\n  name: foo\n  age: 25\n  married: true\n}\n```\n\nThere are some rules to compose flags.\n1. Flag must have `short_name`, `long_name` or `name`. All the names should contain alphabet or digit. But `short_name` has a prefix \"-\" and `long_name` has a prefix \"--\". `long_name` and `short_name` can be set together, but `name` shouldn't.\n2. Positional arguments should be before optional arguments.\n3. BoolFlag should be optional.\n4. SubParser should be positional.\n5. SubParser and positional arguments can't be together.\n\n#### Predefined Flags\n\n* `BoolFlag`: Equals to `Flag\u003cbool\u003e`\n* `Uint8Flag`: Equals to `Flag\u003cuint8_t\u003e`\n* `Int8Flag`: Equals to `Flag\u003cint8_t\u003e`\n* `Uint16Flag`: Equals to `Flag\u003cuint16_t\u003e`\n* `Int16Flag`: Equals to `Flag\u003cint16_t\u003e`\n* `Uint32Flag`: Equals to `Flag\u003cuint32_t\u003e`\n* `Int32Flag`: Equals to `Flag\u003cint32_t\u003e`\n* `Uint64Flag`: Equals to `Flag\u003cuint64_t\u003e`\n* `Int64Flag`: Equals to `Flag\u003cint64_t\u003e`\n* `FloatFlag`: Equals to `Flag\u003cfloat\u003e`\n* `DoubleFlag`: Equals to `Flag\u003cdouble\u003e`\n* `StringFlag`: Equals to `Flag\u003cstd::string\u003e`\n\n#### Vector Flag\n\nIn case you want to receive numbers of arguments, you can add `console::Flag\u003cstd::vector\u003cT\u003e\u003e` like below. You can find the full code in [examples/vector_flag.cc](examples/vector_flag.cc).\n\n```c++\nstd::vector\u003cint\u003e numbers;\nconsole::FlagParser flag_parser;\nflag_parser.set_program_name(\"vector_flag\");\nflag_parser.AddFlag\u003cconsole::Flag\u003cstd::vector\u003cint\u003e\u003e\u003e(\u0026numbers)\n    .set_short_name(\"-n\")\n    .set_long_name(\"--number\")\n    .set_help(\"numbers for add, you can accumulate!\")\n    .set_required()\n    .set_sequential();\n```\n\nThen you can use the program like below.\n\n```bash\n# bazel build //examples:vector_flag\n\n$ bazel-bin/examples/vector_flag -n 1 -n 2 -n 3\n[SUM]: 6\n```\n\n#### Custom Flag\n\nIf you want to handle flags besides provided ones, then you have 2 options. First, you can do with function callback. You can find the full code in [examples/custom_flag.cc](examples/custom_flag.cc). **NOTE: Because it is desgined to be used without exception and with string_view, it depends on [abseil-cpp](https://github.com/abseil/abseil-cpp) internally.**\n\n```c++\nuint16_t number;\nconsole::FlagParser flag_parser;\nflag_parser.set_program_name(\"custom_flag\");\nflag_parser\n    .AddFlag\u003cconsole::Uint16Flag\u003e(\n        [\u0026number](absl::string_view input, std::string* reason) {\n          uint16_t n;\n            if (!console::FlagValueTraits\u003cuint16_t\u003e::ParseValue(input, \u0026n,\n                                                                reason)) {\n              return false;\n            }\n            if (n % 2 == 0) {\n              *reason = absl::StrFormat(\"%u is not a odd number\", n);\n              return false;\n            }\n            number = n;\n            return true;\n        })\n    .set_name(\"number\")\n    .set_required()\n    .set_help(\"Please input only odd numbers!\");\n```\n\n```bash\n# bazel build //examples:custom_flag\n\n$ bazel-bin/examples/custom_flag 2\n[ERROR]: number is failed to parse: (reason: 2 is not a odd number).\n$ bazel-bin/examples/custom_flag 65536\n[ERROR]: number is failed to parse: (reason: 65536 is out of its range).\n$ bazel-bin/examples/custom_flag -1\n[ERROR]: number is failed to parse: (reason: failed to convert to number (\"-1\")).\n```\n\nThe other one is using specialized templated class. You can find the full code in [examples/custom_flag2.cc](examples/custom_flag2.cc).\n\n```c++\nstruct OddNumber {\n  uint16_t number;\n};\n\nnamespace console {\n\ntemplate \u003c\u003e\nclass FlagValueTraits\u003cOddNumber\u003e {\n public:\n  static bool ParseValue(absl::string_view input, OddNumber* value,\n                         std::string* reason) {\n    // same above\n  }\n};\n\n}  // namespace console\n\nOddNumber number;\nconsole::FlagParser flag_parser;\nflag_parser.set_program_name(\"custom_flag2\");\nflag_parser.AddFlag\u003cconsole::Flag\u003cOddNumber\u003e\u003e(\u0026number)\n    .set_name(\"number\")\n    .set_required()\n    .set_help(\"Please input only odd numbers!\");\n```\n\n#### SubParser\n\nSometimes you maybe want to use subcommand like `git commit` or `git log`. When `commit` or `log` comes, the flags get differnt! You can do this with `SubParser`. You can find the full code in [examples/sub_parser.cc](examples/sub_parser.cc).\n\n```c++\nstruct Add {\n  int a;\n  int b;\n};\n\nstruct Pow {\n  int base;\n  int exponent;\n};\n\nAdd add;\nPow pow;\nconsole::FlagParser flag_parser;\nflag_parser.set_program_name(\"sub_parser\");\nconsole::SubParser\u0026 add_parser = flag_parser.AddSubParser();\nadd_parser.set_name(\"add\").set_help(\"add\");\nadd_parser.AddFlag\u003cconsole::Int32Flag\u003e(\u0026add.a).set_name(\"a\").set_help(\n    \"number1 for add\");\nadd_parser.AddFlag\u003cconsole::Int32Flag\u003e(\u0026add.b).set_name(\"b\").set_help(\n    \"number2 for add\");\nconsole::SubParser\u0026 pow_parser = flag_parser.AddSubParser();\npow_parser.set_name(\"pow\").set_help(\"pow\");\npow_parser.AddFlag\u003cconsole::Int32Flag\u003e(\u0026pow.base).set_name(\"base\").set_help(\n    \"base for pow\");\npow_parser.AddFlag\u003cconsole::Int32Flag\u003e(\u0026pow.exponent)\n    .set_name(\"exponent\")\n    .set_help(\"exponent for pow\");\n```\n\n```bash\n# bazel build //examples:sub_parser\n\n$ bazel-bin/examples/sub_parser -h\nUsage:\n\nsub_parser \u003ccommand\u003e [--verbose]\n\nCommands:\n\nadd                 add\npow                 pow\n\n$bazel-bin/examples/sub_parser add -h\nUsage:\n\nsub_parser add a b\n\nPositional arguments:\n\na                   number1 for add\nb                   number2 for ad\n\n$ bazel-bin/examples/sub_parser add 1 2\n[ADD]: 3\n$ bazel-bin/examples/sub_parser pow 1 2\n[POW]: 1\n```\n\n#### Autocompletion\n\n![resources/demo3.gif](resources/demo3.gif)\n\n```bash\nnpm install -g console-autocompletion\n```\n\nIf you want to apply bash autocompletion, you just include one header `console/autocompletion.h`, and generate json file  using `console::Autocompletion::WriteToJson(flag_parser, filename);`. Using this one, you can feed the information of flag parser to this npm package [console-autocompletion](https://www.npmjs.com/package/console-autocompletion) like below.\n\n```bash\nmkdir ~/.console-autocompletion\ncp /path/to/json ~/.console-autocompletion/\n\nalias program_name=/path/to/program # Maybe you don't have to do this\nconsole-autocompletion-installer program_name bash_completion\nsource bash_completion\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchokobole%2Fconsole","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchokobole%2Fconsole","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchokobole%2Fconsole/lists"}