diff --git a/src/Core/Settings.h b/src/Core/Settings.h index d9071ef592b4..ea582c187107 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -1139,6 +1139,7 @@ class IColumn; M(Bool, output_format_enable_streaming, false, "Enable streaming in output formats that support it.", 0) \ M(Bool, output_format_write_statistics, true, "Write statistics about read rows, bytes, time elapsed in suitable output formats.", 0) \ M(Bool, output_format_pretty_row_numbers, false, "Add row numbers before each row for pretty output format", 0) \ + M(Bool, output_format_pretty_highlight_digit_groups, true, "If enabled and if output is a terminal, highlight every digit corresponding to the number of thousands, millions, etc. with underline.", 0) \ M(UInt64, output_format_pretty_single_large_number_tip_threshold, 1'000'000, "Print a readable number tip on the right side of the table if the block consists of a single number which exceeds this value (except 0)", 0) \ M(Bool, insert_distributed_one_random_shard, false, "If setting is enabled, inserting into distributed table will choose a random shard to write when there is no sharding key", 0) \ \ diff --git a/src/Core/SettingsChangesHistory.h b/src/Core/SettingsChangesHistory.h index ee56b86d9f23..3f72dd4df6ed 100644 --- a/src/Core/SettingsChangesHistory.h +++ b/src/Core/SettingsChangesHistory.h @@ -105,6 +105,7 @@ static std::map sett {"keeper_retry_max_backoff_ms", 5000, 5000, "Max backoff timeout for general keeper operations"}, {"s3queue_allow_experimental_sharded_mode", false, false, "Enable experimental sharded mode of S3Queue table engine. It is experimental because it will be rewritten"}, {"merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability", 0.0, 0.0, "For testing of `PartsSplitter` - split read ranges into intersecting and non intersecting every time you read from MergeTree with the specified probability."}, + {"output_format_pretty_highlight_digit_groups", false, true, "If enabled and if output is a terminal, highlight every digit corresponding to the number of thousands, millions, etc. with underline."}, }}, {"24.2", {{"allow_suspicious_variant_types", true, false, "Don't allow creating Variant type with suspicious variants by default"}, {"validate_experimental_and_suspicious_types_inside_nested_types", false, true, "Validate usage of experimental and suspicious types inside nested types"}, diff --git a/src/Formats/FormatFactory.cpp b/src/Formats/FormatFactory.cpp index e7dce2738699..9e01709f788f 100644 --- a/src/Formats/FormatFactory.cpp +++ b/src/Formats/FormatFactory.cpp @@ -167,6 +167,7 @@ FormatSettings getFormatSettings(const ContextPtr & context, const Settings & se format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width; format_settings.pretty.max_rows = settings.output_format_pretty_max_rows; format_settings.pretty.max_value_width = settings.output_format_pretty_max_value_width; + format_settings.pretty.highlight_digit_groups = settings.output_format_pretty_highlight_digit_groups; format_settings.pretty.output_format_pretty_row_numbers = settings.output_format_pretty_row_numbers; format_settings.pretty.output_format_pretty_single_large_number_tip_threshold = settings.output_format_pretty_single_large_number_tip_threshold; format_settings.protobuf.input_flatten_google_wrappers = settings.input_format_protobuf_flatten_google_wrappers; diff --git a/src/Formats/FormatSettings.h b/src/Formats/FormatSettings.h index 42b21c77cefa..d1b7f5b4d25a 100644 --- a/src/Formats/FormatSettings.h +++ b/src/Formats/FormatSettings.h @@ -275,6 +275,7 @@ struct FormatSettings UInt64 max_rows = 10000; UInt64 max_column_pad_width = 250; UInt64 max_value_width = 10000; + bool highlight_digit_groups = true; SettingFieldUInt64Auto color{"auto"}; bool output_format_pretty_row_numbers = false; diff --git a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp index c5db8f2e30a8..2bdffa295994 100644 --- a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp @@ -303,7 +303,7 @@ void PrettyBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port_kind const auto & type = *header.getByPosition(j).type; writeValueWithPadding(*columns[j], *serializations[j], i, widths[j].empty() ? max_widths[j] : widths[j][i], - max_widths[j], type.shouldAlignRightInPrettyFormats()); + max_widths[j], type.shouldAlignRightInPrettyFormats(), isNumber(type)); } writeCString(grid_symbols.bar, out); @@ -322,9 +322,75 @@ void PrettyBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port_kind } +static String highlightDigitGroups(String source) +{ + if (source.size() <= 4) + return source; + + bool is_regular_number = true; + size_t num_digits_before_decimal = 0; + for (auto c : source) + { + if (c == '-' || c == ' ') + continue; + if (c == '.') + break; + if (c >= '0' && c <= '9') + { + ++num_digits_before_decimal; + } + else + { + is_regular_number = false; + break; + } + } + + if (!is_regular_number || num_digits_before_decimal <= 4) + return source; + + String result; + size_t size = source.size(); + result.reserve(2 * size); + + bool before_decimal = true; + size_t digit_num = 0; + for (size_t i = 0; i < size; ++i) + { + auto c = source[i]; + if (before_decimal && c >= '0' && c <= '9') + { + ++digit_num; + size_t offset = num_digits_before_decimal - digit_num; + if (offset && offset % 3 == 0) + { + result += "\033[4m"; + result += c; + result += "\033[0m"; + } + else + { + result += c; + } + } + else if (c == '.') + { + before_decimal = false; + result += c; + } + else + { + result += c; + } + } + + return result; +} + + void PrettyBlockOutputFormat::writeValueWithPadding( const IColumn & column, const ISerialization & serialization, size_t row_num, - size_t value_width, size_t pad_to_width, bool align_right) + size_t value_width, size_t pad_to_width, bool align_right, bool is_number) { String serialized_value = " "; { @@ -359,6 +425,10 @@ void PrettyBlockOutputFormat::writeValueWithPadding( writeChar(' ', out); }; + /// Highlight groups of thousands. + if (color && is_number && format_settings.pretty.highlight_digit_groups) + serialized_value = highlightDigitGroups(serialized_value); + if (align_right) { write_padding(); diff --git a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h index 071bfa92e356..d185df392f3d 100644 --- a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h +++ b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h @@ -48,7 +48,7 @@ class PrettyBlockOutputFormat : public IOutputFormat void writeValueWithPadding( const IColumn & column, const ISerialization & serialization, size_t row_num, - size_t value_width, size_t pad_to_width, bool align_right); + size_t value_width, size_t pad_to_width, bool align_right, bool is_number); void resetFormatterImpl() override { diff --git a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp index bda517708380..99a83a8b0945 100644 --- a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp @@ -169,7 +169,7 @@ void PrettyCompactBlockOutputFormat::writeRow( const auto & type = *header.getByPosition(j).type; const auto & cur_widths = widths[j].empty() ? max_widths[j] : widths[j][row_num]; - writeValueWithPadding(*columns[j], *serializations[j], row_num, cur_widths, max_widths[j], type.shouldAlignRightInPrettyFormats()); + writeValueWithPadding(*columns[j], *serializations[j], row_num, cur_widths, max_widths[j], type.shouldAlignRightInPrettyFormats(), isNumber(type)); } writeCString(grid_symbols.bar, out); diff --git a/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp index e92863c93fb5..7b1758a45662 100644 --- a/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp @@ -84,7 +84,7 @@ void PrettySpaceBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port const auto & type = *header.getByPosition(column).type; auto & cur_width = widths[column].empty() ? max_widths[column] : widths[column][row]; writeValueWithPadding( - *columns[column], *serializations[column], row, cur_width, max_widths[column], type.shouldAlignRightInPrettyFormats()); + *columns[column], *serializations[column], row, cur_width, max_widths[column], type.shouldAlignRightInPrettyFormats(), isNumber(type)); } writeReadableNumberTip(chunk); diff --git a/tests/queries/0_stateless/00476_pretty_formats_and_widths.sql b/tests/queries/0_stateless/00476_pretty_formats_and_widths.sql index ece046b738e5..4bace207fb54 100644 --- a/tests/queries/0_stateless/00476_pretty_formats_and_widths.sql +++ b/tests/queries/0_stateless/00476_pretty_formats_and_widths.sql @@ -1,4 +1,4 @@ -SET output_format_pretty_color=1; +SET output_format_pretty_color=1, output_format_pretty_highlight_digit_groups=0; SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT Pretty; SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT PrettyCompact; SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT PrettySpace; diff --git a/tests/queries/0_stateless/03022_highlight_digit_groups.reference b/tests/queries/0_stateless/03022_highlight_digit_groups.reference new file mode 100644 index 000000000000..4e814c36309d --- /dev/null +++ b/tests/queries/0_stateless/03022_highlight_digit_groups.reference @@ -0,0 +1,68 @@ + multiply(exp10(number), if(modulo(number, 2), 1, -1)) + + -1 + 10 + -100 + 1000 + -10000 + 100000 + -1000000 + 10000000 + -100000000 + 1000000000 + -10000000000 + 100000000000 + -1000000000000 + 10000000000000 + -100000000000000 + 1000000000000000 + -10000000000000000 + 100000000000000000 + -1000000000000000000 + 10000000000000000000 + -100000000000000000000 + 1e21 + -1e22 + 1e23 + -1e24 + 1e25 + -1e26 + 1e27 + -1e28 + 1e29 + exp10(number) + + 1 + 10 + 100 + 1000 + 10000 + 100000 + 1000000 + 10000000 + 100000000 + 1000000000 + exp10(number) + + 1 + 10 + 100 + 1000 + 10000 + 100000 + 1000000 + 10000000 + 100000000 + 1000000000 + plus(exp10(number), exp10(negate(number))) + + 2 + 10.1 + 100.01 + 1000.001 + 10000.0001 + 100000.00001 + 1000000.000001 + 10000000.0000001 + 100000000.00000001 + 1000000000 diff --git a/tests/queries/0_stateless/03022_highlight_digit_groups.sql b/tests/queries/0_stateless/03022_highlight_digit_groups.sql new file mode 100644 index 000000000000..57341f228a98 --- /dev/null +++ b/tests/queries/0_stateless/03022_highlight_digit_groups.sql @@ -0,0 +1,6 @@ +SELECT exp10(number) * (number % 2 ? 1 : -1) FROM numbers(30) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1; + +SELECT exp10(number) FROM numbers(10) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1, output_format_pretty_highlight_digit_groups = 0; +SELECT exp10(number) FROM numbers(10) FORMAT PrettySpace; + +SELECT exp10(number) + exp10(-number) FROM numbers(10) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1;