Repository navigation
add support to ignore ansi sequences when formatting usage display. Fixes #879 - #942
timmaffett wants to merge 23 commits into
Conversation
| /// Matches the Control Sequence Introducer (CSI) ANSI escape sequences. | ||
| /// | ||
| /// Anatomy: | ||
| /// \x1b : The literal ESC character (ASCII 27). | ||
| /// \[ : The literal '[' character (together with ESC, this forms the CSI). | ||
| /// [0-9;?]* : Zero or more parameter bytes: | ||
| /// - 0-9 : Numeric parameters (e.g., color codes). | ||
| /// - ; : Parameter separators. | ||
| /// - ? : Private mode indicators (e.g., cursor toggles). | ||
| /// [a-zA-Z] : The 'Final Byte' that determines the command (e.g., 'm' for color). | ||
| static final RegExp _ansiRegex = RegExp(r'\x1b\[[0-9;?]*[a-zA-Z]'); |
There was a problem hiding this comment.
This covers the full set I've seen used in Dart, but I think we may as well expand the regex to include the other parameter bytes and intermediate bytes allowed.
| /// Matches the Control Sequence Introducer (CSI) ANSI escape sequences. | |
| /// | |
| /// Anatomy: | |
| /// \x1b : The literal ESC character (ASCII 27). | |
| /// \[ : The literal '[' character (together with ESC, this forms the CSI). | |
| /// [0-9;?]* : Zero or more parameter bytes: | |
| /// - 0-9 : Numeric parameters (e.g., color codes). | |
| /// - ; : Parameter separators. | |
| /// - ? : Private mode indicators (e.g., cursor toggles). | |
| /// [a-zA-Z] : The 'Final Byte' that determines the command (e.g., 'm' for color). | |
| static final RegExp _ansiRegex = RegExp(r'\x1b\[[0-9;?]*[a-zA-Z]'); | |
| /// Matches the Control Sequence Introducer (CSI) ANSI escape sequences. | |
| /// | |
| /// Anatomy based on ECMA-48: | |
| /// \x1b : The literal ESC character (ASCII 27). | |
| /// \[ : The literal '[' character (together with ESC, this forms the CSI). | |
| /// [\x30-\x3f]* : Parameter Bytes (0-9:;<=>?). | |
| /// [\x20-\x2f]* : Intermediate Bytes ( !"#$%&'()*+,-./). | |
| /// [\x40-\x7e] : Final Byte (@A-Z[\]^_`a-z{|}~). | |
| static final RegExp _ansiRegex = RegExp(r'\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]'); |
There was a problem hiding this comment.
OK, I have change it to now use your more complete regex. I also expanded the tests to exercise a wider range of ECMA-48 ANSI sequences.
|
Thanks for the quick feedback @natebosch In my first attempt at the simpler regex I was just thinking about catching ANSI text styling codes that would typically be used in a usage message, but this one is not that much more complex and it will now catch everything. |
| } | ||
|
|
||
| /// Pads [source] to [length] by adding spaces at the end. | ||
| String padRight(String source, int length) => |
There was a problem hiding this comment.
(Could be extension member too, but would be named padRightIgnoreAnsi.)
There was a problem hiding this comment.
padRightIgnoreAscii extension now
# Conflicts: # pkgs/args/CHANGELOG.md
|
OK @irhn I have address all of your comments - Thank you for taking the time for a thorough and the great feedback! I also included a modified example of the ArgParser example that uses extensive ANSI sequences just so I could verify it in action. I am happy to remove this if needed. |
Avoid third_party dependencies from core Dart team repos.
Package publishing
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. |
PR Health
Coverage
|
| File | Coverage |
|---|---|
| pkgs/args/example/arg_parser/ansi_example.dart | 💔 Not covered |
| pkgs/args/lib/command_runner.dart | 💔 Not covered |
| pkgs/args/lib/src/usage.dart | 💔 Not covered |
| pkgs/args/lib/src/utils.dart | 💔 Not covered |
This check for test coverage is informational (issues shown here will not fail the PR).
This check can be disabled by tagging the PR with skip-coverage-check.
Breaking changes ✔️
| Package | Change | Current Version | New Version | Needed Version | Looking good? |
|---|---|---|---|---|---|
| args | Breaking | 2.7.0 | 2.8.0-wip | 2.8.0-wip | ✔️ |
This check can be disabled by tagging the PR with skip-breaking-check.
Unused Dependencies ⚠️
| Package | Status |
|---|---|
| args | ❗ Show IssuesThese packages are used outside lib/ but are not dev_dependencies: |
For details on how to fix these, see dependency_validator.
This check can be disabled by tagging the PR with skip-unused-dependencies-check.
Changelog Entry ✔️
| Package | Changed Files |
|---|
Changes to files need to be accounted for in their respective changelogs.
This check can be disabled by tagging the PR with skip-changelog-check.
License Headers ⚠️
""
| Files |
|---|
| pkgs/args/lib/src/usage.dart |
| pkgs/args/lib/src/utils.dart |
| pkgs/args/test/utils_test.dart |
All source files should start with a license header.
Unrelated files missing license headers
| Files |
|---|
| pkgs/args/example/command_runner/draw.dart |
| pkgs/args/lib/args.dart |
| pkgs/args/lib/src/allow_anything_parser.dart |
| pkgs/args/lib/src/arg_parser.dart |
| pkgs/args/lib/src/arg_parser_exception.dart |
| pkgs/args/lib/src/arg_results.dart |
| pkgs/args/lib/src/help_command.dart |
| pkgs/args/lib/src/option.dart |
| pkgs/args/lib/src/parser.dart |
| pkgs/args/lib/src/usage_exception.dart |
| pkgs/args/test/allow_anything_test.dart |
| pkgs/args/test/args_test.dart |
| pkgs/args/test/command_parse_test.dart |
| pkgs/args/test/command_runner_test.dart |
| pkgs/args/test/command_test.dart |
| pkgs/args/test/parse_performance_test.dart |
| pkgs/args/test/parse_test.dart |
| pkgs/args/test/test_utils.dart |
| pkgs/args/test/trailing_options_test.dart |
| pkgs/args/test/usage_test.dart |
| pkgs/async/lib/async.dart |
| pkgs/async/lib/src/async_cache.dart |
| pkgs/async/lib/src/async_memoizer.dart |
| pkgs/async/lib/src/byte_collector.dart |
| pkgs/async/lib/src/cancelable_operation.dart |
| pkgs/async/lib/src/chunked_stream_reader.dart |
| pkgs/async/lib/src/delegate/event_sink.dart |
| pkgs/async/lib/src/delegate/future.dart |
| pkgs/async/lib/src/delegate/sink.dart |
| pkgs/async/lib/src/delegate/stream.dart |
| pkgs/async/lib/src/delegate/stream_consumer.dart |
| pkgs/async/lib/src/delegate/stream_sink.dart |
| pkgs/async/lib/src/delegate/stream_subscription.dart |
| pkgs/async/lib/src/future_group.dart |
| pkgs/async/lib/src/lazy_stream.dart |
| pkgs/async/lib/src/null_stream_sink.dart |
| pkgs/async/lib/src/restartable_timer.dart |
| pkgs/async/lib/src/result/capture_sink.dart |
| pkgs/async/lib/src/result/capture_transformer.dart |
| pkgs/async/lib/src/result/error.dart |
| pkgs/async/lib/src/result/future.dart |
| pkgs/async/lib/src/result/release_sink.dart |
| pkgs/async/lib/src/result/release_transformer.dart |
| pkgs/async/lib/src/result/result.dart |
| pkgs/async/lib/src/result/value.dart |
| pkgs/async/lib/src/single_subscription_transformer.dart |
| pkgs/async/lib/src/sink_base.dart |
| pkgs/async/lib/src/stream_closer.dart |
| pkgs/async/lib/src/stream_completer.dart |
| pkgs/async/lib/src/stream_extensions.dart |
| pkgs/async/lib/src/stream_group.dart |
| pkgs/async/lib/src/stream_queue.dart |
| pkgs/async/lib/src/stream_sink_completer.dart |
| pkgs/async/lib/src/stream_sink_extensions.dart |
| pkgs/async/lib/src/stream_sink_transformer.dart |
| pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart |
| pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart |
| pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart |
| pkgs/async/lib/src/stream_sink_transformer/typed.dart |
| pkgs/async/lib/src/stream_splitter.dart |
| pkgs/async/lib/src/stream_subscription_transformer.dart |
| pkgs/async/lib/src/stream_zip.dart |
| pkgs/async/lib/src/subscription_stream.dart |
| pkgs/async/lib/src/typed/stream_subscription.dart |
| pkgs/async/lib/src/typed_stream_transformer.dart |
| pkgs/async/test/async_cache_test.dart |
| pkgs/async/test/async_memoizer_test.dart |
| pkgs/async/test/byte_collection_test.dart |
| pkgs/async/test/cancelable_operation_test.dart |
| pkgs/async/test/chunked_stream_reader.dart |
| pkgs/async/test/future_group_test.dart |
| pkgs/async/test/io_sink_impl.dart |
| pkgs/async/test/lazy_stream_test.dart |
| pkgs/async/test/null_stream_sink_test.dart |
| pkgs/async/test/reject_errors_test.dart |
| pkgs/async/test/restartable_timer_test.dart |
| pkgs/async/test/result/result_captureAll_test.dart |
| pkgs/async/test/result/result_flattenAll_test.dart |
| pkgs/async/test/result/result_future_test.dart |
| pkgs/async/test/result/result_test.dart |
| pkgs/async/test/single_subscription_transformer_test.dart |
| pkgs/async/test/sink_base_test.dart |
| pkgs/async/test/stream_closer_test.dart |
| pkgs/async/test/stream_completer_test.dart |
| pkgs/async/test/stream_extensions_test.dart |
| pkgs/async/test/stream_group_test.dart |
| pkgs/async/test/stream_queue_test.dart |
| pkgs/async/test/stream_sink_completer_test.dart |
| pkgs/async/test/stream_sink_transformer_test.dart |
| pkgs/async/test/stream_splitter_test.dart |
| pkgs/async/test/stream_zip_test.dart |
| pkgs/async/test/stream_zip_zone_test.dart |
| pkgs/async/test/subscription_stream_test.dart |
| pkgs/async/test/subscription_transformer_test.dart |
| pkgs/async/test/typed_wrapper/stream_subscription_test.dart |
| pkgs/async/test/utils.dart |
| pkgs/characters/benchmark/benchmark.dart |
| pkgs/characters/example/main.dart |
| pkgs/characters/lib/characters.dart |
| pkgs/characters/lib/src/characters.dart |
| pkgs/characters/lib/src/characters_impl.dart |
| pkgs/characters/lib/src/extensions.dart |
| pkgs/characters/test/characters_test.dart |
| pkgs/characters/test/src/equiv.dart |
| pkgs/characters/test/src/text_samples.dart |
| pkgs/characters/test/src/unicode_tests.dart |
| pkgs/characters/test/src/various_tests.dart |
| pkgs/characters/tool/benchmark.dart |
| pkgs/characters/tool/bin/generate_tables.dart |
| pkgs/characters/tool/bin/generate_tests.dart |
| pkgs/characters/tool/generate.dart |
| pkgs/characters/tool/src/atsp.dart |
| pkgs/characters/tool/src/data_files.dart |
| pkgs/characters/tool/src/debug_names.dart |
| pkgs/characters/tool/src/graph.dart |
| pkgs/characters/tool/src/indirect_table.dart |
| pkgs/characters/tool/src/list_overlap.dart |
| pkgs/characters/tool/src/shared.dart |
| pkgs/characters/tool/src/string_literal_writer.dart |
| pkgs/characters/tool/src/table_builder.dart |
| pkgs/collection/benchmark/benchmark_utils.dart |
| pkgs/collection/benchmark/deep_collection_equality.dart |
| pkgs/collection/benchmark/legacy_quicksort.dart |
| pkgs/collection/benchmark/sort_benchmark.dart |
| pkgs/collection/lib/algorithms.dart |
| pkgs/collection/lib/collection.dart |
| pkgs/collection/lib/equality.dart |
| pkgs/collection/lib/iterable_zip.dart |
| pkgs/collection/lib/priority_queue.dart |
| pkgs/collection/lib/src/algorithms.dart |
| pkgs/collection/lib/src/boollist.dart |
| pkgs/collection/lib/src/canonicalized_map.dart |
| pkgs/collection/lib/src/combined_wrappers/combined_iterable.dart |
| pkgs/collection/lib/src/combined_wrappers/combined_iterator.dart |
| pkgs/collection/lib/src/combined_wrappers/combined_list.dart |
| pkgs/collection/lib/src/combined_wrappers/combined_map.dart |
| pkgs/collection/lib/src/comparators.dart |
| pkgs/collection/lib/src/empty_unmodifiable_set.dart |
| pkgs/collection/lib/src/equality.dart |
| pkgs/collection/lib/src/equality_map.dart |
| pkgs/collection/lib/src/equality_set.dart |
| pkgs/collection/lib/src/functions.dart |
| pkgs/collection/lib/src/iterable_extensions.dart |
| pkgs/collection/lib/src/iterable_zip.dart |
| pkgs/collection/lib/src/list_extensions.dart |
| pkgs/collection/lib/src/priority_queue.dart |
| pkgs/collection/lib/src/queue_list.dart |
| pkgs/collection/lib/src/union_set.dart |
| pkgs/collection/lib/src/union_set_controller.dart |
| pkgs/collection/lib/src/unmodifiable_wrappers.dart |
| pkgs/collection/lib/src/utils.dart |
| pkgs/collection/lib/src/wrappers.dart |
| pkgs/collection/lib/wrappers.dart |
| pkgs/collection/test/algorithms_test.dart |
| pkgs/collection/test/boollist_test.dart |
| pkgs/collection/test/canonicalized_map_test.dart |
| pkgs/collection/test/combined_wrapper/iterable_test.dart |
| pkgs/collection/test/combined_wrapper/list_test.dart |
| pkgs/collection/test/combined_wrapper/map_test.dart |
| pkgs/collection/test/comparators_test.dart |
| pkgs/collection/test/equality_map_test.dart |
| pkgs/collection/test/equality_set_test.dart |
| pkgs/collection/test/equality_test.dart |
| pkgs/collection/test/extensions_test.dart |
| pkgs/collection/test/functions_test.dart |
| pkgs/collection/test/ignore_ascii_case_test.dart |
| pkgs/collection/test/iterable_zip_test.dart |
| pkgs/collection/test/priority_queue_test.dart |
| pkgs/collection/test/queue_list_test.dart |
| pkgs/collection/test/separate_extensions_test.dart |
| pkgs/collection/test/union_set_controller_test.dart |
| pkgs/collection/test/union_set_test.dart |
| pkgs/collection/test/unmodifiable_collection_test.dart |
| pkgs/collection/test/wrapper_test.dart |
| pkgs/convert/benchmark/fixed_datetime_formatter_benchmark.dart |
| pkgs/convert/example/example.dart |
| pkgs/convert/lib/convert.dart |
| pkgs/convert/lib/src/accumulator_sink.dart |
| pkgs/convert/lib/src/byte_accumulator_sink.dart |
| pkgs/convert/lib/src/charcodes.dart |
| pkgs/convert/lib/src/fixed_datetime_formatter.dart |
| pkgs/convert/lib/src/hex.dart |
| pkgs/convert/lib/src/hex/decoder.dart |
| pkgs/convert/lib/src/identity_codec.dart |
| pkgs/convert/lib/src/percent.dart |
| pkgs/convert/lib/src/percent/decoder.dart |
| pkgs/convert/lib/src/string_accumulator_sink.dart |
| pkgs/convert/lib/src/utils.dart |
| pkgs/convert/test/accumulator_sink_test.dart |
| pkgs/convert/test/byte_accumulator_sink_test.dart |
| pkgs/convert/test/codepage_test.dart |
| pkgs/convert/test/fixed_datetime_formatter_test.dart |
| pkgs/convert/test/hex_test.dart |
| pkgs/convert/test/identity_codec_test.dart |
| pkgs/convert/test/percent_test.dart |
| pkgs/convert/test/string_accumulator_sink_test.dart |
| pkgs/crypto/benchmark/benchmark.dart |
| pkgs/crypto/example/example.dart |
| pkgs/crypto/lib/crypto.dart |
| pkgs/crypto/lib/src/digest.dart |
| pkgs/crypto/lib/src/digest_sink.dart |
| pkgs/crypto/lib/src/hash.dart |
| pkgs/crypto/lib/src/hash_sink.dart |
| pkgs/crypto/lib/src/hmac.dart |
| pkgs/crypto/lib/src/md5.dart |
| pkgs/crypto/lib/src/sha1.dart |
| pkgs/crypto/lib/src/sha256.dart |
| pkgs/crypto/lib/src/sha512.dart |
| pkgs/crypto/lib/src/sha512_fastsinks.dart |
| pkgs/crypto/lib/src/sha512_slowsinks.dart |
| pkgs/crypto/lib/src/utils.dart |
| pkgs/crypto/test/hmac_md5_test.dart |
| pkgs/crypto/test/hmac_sha1_test.dart |
| pkgs/crypto/test/hmac_sha256_test.dart |
| pkgs/crypto/test/hmac_sha2_test.dart |
| pkgs/crypto/test/sha1_test.dart |
| pkgs/crypto/test/sha256_test.dart |
| pkgs/crypto/test/sha512_test.dart |
| pkgs/crypto/test/sha_monte_test.dart |
| pkgs/crypto/test/utils.dart |
| pkgs/crypto/tool/md5sum.dart |
| pkgs/fixnum/lib/fixnum.dart |
| pkgs/fixnum/lib/src/int32.dart |
| pkgs/fixnum/lib/src/int64.dart |
| pkgs/fixnum/lib/src/int64_emulated.dart |
| pkgs/fixnum/lib/src/int64_native.dart |
| pkgs/fixnum/lib/src/intx.dart |
| pkgs/fixnum/lib/src/utilities.dart |
| pkgs/fixnum/test/all_tests.dart |
| pkgs/fixnum/test/int32_test.dart |
| pkgs/fixnum/test/int64_test.dart |
| pkgs/fixnum/test/int_64_vm_test.dart |
| pkgs/lints/tool/gen_docs.dart |
| pkgs/lints/tool/validate_lib.dart |
| pkgs/logging/example/main.dart |
| pkgs/logging/lib/logging.dart |
| pkgs/logging/lib/src/level.dart |
| pkgs/logging/lib/src/log_record.dart |
| pkgs/logging/lib/src/logger.dart |
| pkgs/logging/test/logging_test.dart |
| pkgs/os_detect/bin/os_detect.dart |
| pkgs/os_detect/example/example.dart |
| pkgs/os_detect/example/tree_shaking.dart |
| pkgs/os_detect/lib/os_detect.dart |
| pkgs/os_detect/lib/override.dart |
| pkgs/os_detect/lib/src/os_kind.dart |
| pkgs/os_detect/lib/src/os_override.dart |
| pkgs/os_detect/lib/src/osid_html.dart |
| pkgs/os_detect/lib/src/osid_io.dart |
| pkgs/os_detect/lib/src/osid_unknown.dart |
| pkgs/os_detect/test/osid_test.dart |
| pkgs/path/benchmark/benchmark.dart |
| pkgs/path/example/example.dart |
| pkgs/path/lib/path.dart |
| pkgs/path/lib/src/characters.dart |
| pkgs/path/lib/src/context.dart |
| pkgs/path/lib/src/internal_style.dart |
| pkgs/path/lib/src/path_exception.dart |
| pkgs/path/lib/src/path_map.dart |
| pkgs/path/lib/src/path_set.dart |
| pkgs/path/lib/src/style.dart |
| pkgs/path/lib/src/style/posix.dart |
| pkgs/path/lib/src/style/url.dart |
| pkgs/path/lib/src/style/windows.dart |
| pkgs/path/lib/src/utils.dart |
| pkgs/path/test/browser_test.dart |
| pkgs/path/test/io_test.dart |
| pkgs/path/test/path_map_test.dart |
| pkgs/path/test/path_set_test.dart |
| pkgs/path/test/path_test.dart |
| pkgs/path/test/posix_test.dart |
| pkgs/path/test/relative_test.dart |
| pkgs/path/test/url_test.dart |
| pkgs/path/test/utils.dart |
| pkgs/path/test/windows_test.dart |
| pkgs/typed_data/lib/src/typed_buffer.dart |
| pkgs/typed_data/lib/src/typed_queue.dart |
| pkgs/typed_data/lib/typed_buffers.dart |
| pkgs/typed_data/lib/typed_data.dart |
| pkgs/typed_data/test/queue_test.dart |
| pkgs/typed_data/test/typed_buffers_test.dart |
| pkgs/typed_data/test/typed_buffers_vm_test.dart |
This check can be disabled by tagging the PR with skip-license-check.
API leaks ✔️
The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
| Package | Leaked API symbol | Leaking sources |
|---|
This check can be disabled by tagging the PR with skip-leaking-check.
|
We avoid dependencies to third party packages from these repos so I've gone ahead and rewritten the example to use a different ansi library - it's not quite as full featured in terms of colors covered, but it shows the intent just as well. |
natebosch
left a comment
There was a problem hiding this comment.
Took a closer look at the tests and I think we may be over-testing.
…length_ignore_ansi # Conflicts: # pkgs/args/lib/src/utils.dart # pkgs/args/test/utils_test.dart
|
I believe that I have addressed all the outstanding requested changes. |
This is a RE-push of the #880 PR - the bot closed it and now the code does not update/so I made this PR so that the new changes can be seen. If the #880 PR got re-opened it would probably update and this could be closed...?
---From ORIGINAL #880 PR ----------------
This PR adds logic to exclude hidden ANSI escape sequences when formatting the Usage display.
The ANSI escape sequences are excluded when calculating the lengths of help strings used in the Usage display by using a string extension lengthWithoutAnsi which removes any hidden ANSI escape sequences present before calculating the string length.
It includes tests of the lengthWithoutAnsi getter to ensure that it returns correct length values when no ANSI escape sequences are present as well as when a variety of ANSI escape sequences are present.
(The regex that i am using here is well exercised, as it is the same reg ex that I use within VS Code when parsing and formatting ANSI sequences within the debug console)