преди 4 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #include "./writer.hpp"
  2. #include <neo/buffer_algorithm/concat.hpp>
  3. #include <array>
  4. #include <charconv>
  5. using namespace fansi;
  6. using namespace neo::literals;
  7. namespace {
  8. int code_for_color(std_color col, bool bright) {
  9. return 30 + int(col) + ((bright && col != std_color::normal) ? 60 : 0);
  10. }
  11. } // namespace
  12. void text_writer::put_style(const text_style& new_style) noexcept {
  13. auto& prev_style = _style;
  14. bool unbold = false;
  15. std::string reset_then_enable = "0";
  16. std::string set_toggles;
  17. using neo::dynbuf_concat;
  18. auto append_int = [&](std::string& out, int i) {
  19. std::array<char, 4> valbuf;
  20. auto res = std::to_chars(valbuf.data(), valbuf.data() + sizeof(valbuf), i);
  21. if (!out.empty()) {
  22. out.push_back(';');
  23. }
  24. neo::dynbuf_concat(out, neo::as_buffer(valbuf, res.ptr - valbuf.data()));
  25. };
  26. auto append_toggle = [&](bool my_state, bool prev_state, int on_val) {
  27. int off_val = on_val + 20;
  28. if (!my_state) {
  29. if (prev_state != my_state) {
  30. append_int(set_toggles, off_val);
  31. if (off_val == 21) {
  32. // ! Hack: Terminals disagree on the meaning of 21. ECMA says
  33. // "double-underline", but intuition tells us it would be bold-off, since it is
  34. // SGR Bold [1] plus twenty, as with all other toggles.
  35. unbold = true;
  36. }
  37. }
  38. } else {
  39. append_int(reset_then_enable, on_val);
  40. if (prev_state != my_state) {
  41. append_int(set_toggles, on_val);
  42. }
  43. }
  44. };
  45. append_toggle(new_style.bold, prev_style.bold, 1);
  46. append_toggle(new_style.faint, prev_style.faint, 2);
  47. append_toggle(new_style.italic, prev_style.italic, 3);
  48. append_toggle(new_style.underline, prev_style.underline, 4);
  49. append_toggle(new_style.reverse, prev_style.reverse, 7);
  50. append_toggle(new_style.strike, prev_style.strike, 9);
  51. int fg_int = code_for_color(new_style.fg_color, new_style.bright);
  52. int bg_int = code_for_color(new_style.bg_color, new_style.bright) + 10;
  53. int prev_fg_int = code_for_color(prev_style.fg_color, prev_style.bright);
  54. int prev_bg_int = code_for_color(prev_style.bg_color, prev_style.bright) + 10;
  55. if (new_style.fg_color == std_color::normal) {
  56. // No need to change the foreground color for the reset, but maybe for the toggle
  57. if (fg_int != prev_fg_int) {
  58. append_int(set_toggles, fg_int);
  59. }
  60. } else {
  61. append_int(reset_then_enable, fg_int);
  62. if (fg_int != prev_fg_int) {
  63. append_int(set_toggles, fg_int);
  64. }
  65. }
  66. if (new_style.bg_color == std_color::normal) {
  67. // No need to change the background color for the reset, but maybe for the toggle
  68. if (bg_int != prev_bg_int) {
  69. append_int(set_toggles, bg_int);
  70. }
  71. } else {
  72. append_int(reset_then_enable, bg_int);
  73. if (bg_int != prev_bg_int) {
  74. append_int(set_toggles, bg_int);
  75. }
  76. }
  77. if (set_toggles.empty()) {
  78. // No changes necessary
  79. } else if (unbold || set_toggles.size() > reset_then_enable.size()) {
  80. dynbuf_concat(_buf, "\x1b[", reset_then_enable, "m");
  81. } else {
  82. dynbuf_concat(_buf, "\x1b[", set_toggles, "m");
  83. }
  84. _style = new_style;
  85. }