diff --git a/lib/cli/ui.rb b/lib/cli/ui.rb index f6151d90..f8d20ac2 100644 --- a/lib/cli/ui.rb +++ b/lib/cli/ui.rb @@ -219,6 +219,44 @@ def puts( ) end + # Convenience Method for +CLI::UI::Printer.write+ to write without appending a newline. + # + # ==== Attributes + # + # * +msg+ - Message to print + # * +kwargs+ - keyword arguments for +Printer.write+ + # + sig do + params( + msg: String, + frame_color: T.nilable(Colorable), + to: IOLike, + encoding: Encoding, + format: T::Boolean, + graceful: T::Boolean, + wrap: T::Boolean, + ).void + end + def write( + msg, + frame_color: nil, + to: $stdout, + encoding: Encoding::UTF_8, + format: true, + graceful: true, + wrap: true + ) + CLI::UI::Printer.write( + msg, + frame_color: frame_color, + to: to, + encoding: encoding, + format: format, + graceful: graceful, + wrap: wrap, + ) + end + # Convenience Method for +CLI::UI::Frame.open+ # # ==== Attributes diff --git a/lib/cli/ui/printer.rb b/lib/cli/ui/printer.rb index 41281165..9753241b 100644 --- a/lib/cli/ui/printer.rb +++ b/lib/cli/ui/printer.rb @@ -56,14 +56,84 @@ def puts( graceful: true, wrap: true ) + process_message(msg, frame_color, to, encoding, format, wrap, graceful) do |processed_msg| + to.puts(processed_msg) + end + end + + # Write a message to a stream with common utilities without appending a newline. + # Allows overriding the color, encoding, and target stream. + # By default, it formats the string using CLI:UI and rescues common stream errors. + # + # ==== Attributes + # + # * +msg+ - (required) the string to output. Can be frozen. + # + # ==== Options + # + # * +:frame_color+ - Override the frame color. Defaults to nil. + # * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a write method. Defaults to $stdout. + # * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8. + # * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true. + # * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true. + # * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true. + # + # ==== Returns + # Returns whether the message was successfully printed, + # which can be useful if +:graceful+ is set to true. + # + # ==== Example + # + # CLI::UI::Printer.write('{{x}} Ouch', to: $stderr) + # + sig do + params( + msg: String, + frame_color: T.nilable(Colorable), + to: IOLike, + encoding: T.nilable(Encoding), + format: T::Boolean, + graceful: T::Boolean, + wrap: T::Boolean, + ).returns(T::Boolean) + end + def write( + msg, + frame_color: nil, + to: $stdout, + encoding: Encoding::UTF_8, + format: true, + graceful: true, + wrap: true + ) + process_message(msg, frame_color, to, encoding, format, wrap, graceful) do |processed_msg| + to.write(processed_msg) + end + end + + private + + sig do + params( + msg: String, + frame_color: T.nilable(Colorable), + to: IOLike, + encoding: T.nilable(Encoding), + format: T::Boolean, + wrap: T::Boolean, + graceful: T::Boolean, + block: T.proc.params(arg0: String).void, + ).returns(T::Boolean) + end + def process_message(msg, frame_color, to, encoding, format, wrap, graceful, &block) # rubocop:disable Metrics/ParameterLists msg = (+msg).force_encoding(encoding) if encoding msg = CLI::UI.fmt(msg) if format msg = CLI::UI.wrap(msg) if wrap if frame_color - CLI::UI::Frame.with_frame_color_override(frame_color) { to.puts(msg) } + CLI::UI::Frame.with_frame_color_override(frame_color) { block.call(msg) } else - to.puts(msg) + block.call(msg) end true diff --git a/test/cli/ui/printer_test.rb b/test/cli/ui/printer_test.rb index a22f336d..366cb53f 100644 --- a/test/cli/ui/printer_test.rb +++ b/test/cli/ui/printer_test.rb @@ -103,6 +103,31 @@ def test_encoding_ut8 end assert_equal(msg + "\n", out) end + + def test_write + out, _ = capture_io do + CLI::UI::StdoutRouter.ensure_activated + assert(Printer.write('foo', format: false)) + assert(Printer.write(' bar', format: false)) + end + + assert_equal('foo bar', out) + end + + def test_write_color_frame + out = nil + capture_io do + Frame.open('test') do + out, _ = capture_io do + CLI::UI::StdoutRouter.ensure_activated + Printer.write('foo', frame_color: :red) + Printer.write(' bar') + end + end + end + + assert_equal("\e[31m┃\e[0m \e[0mfoo\e[0m bar", out) + end end end end