From a4eded4918ae38b755d7f5fa7a77c80d6ab4768a Mon Sep 17 00:00:00 2001 From: MidAutumnMoon Date: Thu, 5 Sep 2024 00:02:14 +0800 Subject: [PATCH] first commit --- .editorconfig | 4 + .github/dependabot.yml | 13 ++ .github/workflows/ci.yml | 32 +++++ .gitignore | 56 +++++++++ .vscode/settings.json | 8 ++ Gemfile | 11 ++ Gemfile.lock | 35 ++++++ LICENSE | 28 +++++ README.md | 196 +++++++++++++++++++++++++++++++ Rakefile | 8 ++ assets/crimson-line.png | Bin 0 -> 1382 bytes assets/great-readability.png | Bin 0 -> 2017 bytes assets/readme.png | Bin 0 -> 5140 bytes assets/red-on-white.png | Bin 0 -> 1645 bytes assets/strike-bold-underline.png | Bin 0 -> 2493 bytes assets/what.png | Bin 0 -> 3401 bytes lib/reinbow.rb | 26 ++++ lib/reinbow/color/ansi.rb | 27 +++++ lib/reinbow/color/effects.rb | 44 +++++++ lib/reinbow/color/rgb.rb | 56 +++++++++ lib/reinbow/color/x11.rb | 179 ++++++++++++++++++++++++++++ lib/reinbow/painter.rb | 116 ++++++++++++++++++ reinbow.gemspec | 28 +++++ spec/color_ansi_spec.rb | 15 +++ spec/color_rgb_spec.rb | 46 ++++++++ spec/painter_spec.rb | 100 ++++++++++++++++ spec/refinement_spec.rb | 29 +++++ spec/spec_helper.rb | 1 + 28 files changed, 1058 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100755 LICENSE create mode 100644 README.md create mode 100644 Rakefile create mode 100644 assets/crimson-line.png create mode 100644 assets/great-readability.png create mode 100644 assets/readme.png create mode 100644 assets/red-on-white.png create mode 100644 assets/strike-bold-underline.png create mode 100644 assets/what.png create mode 100644 lib/reinbow.rb create mode 100644 lib/reinbow/color/ansi.rb create mode 100644 lib/reinbow/color/effects.rb create mode 100644 lib/reinbow/color/rgb.rb create mode 100644 lib/reinbow/color/x11.rb create mode 100644 lib/reinbow/painter.rb create mode 100644 reinbow.gemspec create mode 100644 spec/color_ansi_spec.rb create mode 100644 spec/color_rgb_spec.rb create mode 100644 spec/painter_spec.rb create mode 100644 spec/refinement_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b661808 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3f40554 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 + +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "bundler" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..29b8805 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: Run RSpec + +on: + push: + branches: ['master'] + pull_request: + workflow_dispatch: + +jobs: + + rspec: + runs-on: ubuntu-latest + + matrix: + ruby: [ 3.3 ] + + strategy: + fail-fast: false + + steps: + + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Run rspec + run: bundle exec rake spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3200e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +# .env + +# Ignore Byebug command history file. +.byebug_history + +## Specific to RubyMotion: +.dat* +.repl_history +build/ +*.bridgesupport +build-iPhoneOS/ +build-iPhoneSimulator/ + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc + +# Used by RuboCop. Remote config files pulled in from inherit_from directive. +# .rubocop-https?--* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..298a016 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "cSpell.ignoreWords": [ + "meven", + "mhello", + "mthis", + "uncolor" + ], +} \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b9aa098 --- /dev/null +++ b/Gemfile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "rake" + +group :test do + gem "rspec" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..50825ed --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,35 @@ +PATH + remote: . + specs: + reinbow (1.0.0) + +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.5.1) + rake (13.2.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + rake + reinbow! + rspec + +BUNDLED WITH + 2.5.11 diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..a298363 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, MidAutumnMoon + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1dfe463 --- /dev/null +++ b/README.md @@ -0,0 +1,196 @@ +# Reinbow + +Reinbow is a Ruby gem for colorizing printed text on terminal. + +It was original a fork of the great gem [ku1ik/rainbow](https://github.com/ku1ik/rainbow), but the old codebase took way too much efforts to overhaul, so instead this project was built from ground up. + +That means this project has **no relationship** with the original `rainbow` beyond inspiration, **does not** contain codes from it, and **is not** compatible with it either. + +```ruby +require "reinbow" + +using Reinbow + +puts "Blue cat".blue + " " \ + + "jumps over".on_yellow + " " \ + + "big".bold + " " \ + + "lazy meatball".rgb( "#f9e02e" ) +``` + +![screenshot of output of above code](./assets/readme.png) + + +## Installation + +Using Gemfile: + +```ruby +gem 'reinbow' +``` + +Using gem CLI: + +```ruby +gem install reinbow +``` + + +## Usage + +The intended way to use `reinbow` is by using [*refinement*](https://docs.ruby-lang.org/en/master/Refinement.html) and calling *coloring methods* on String instances. But there's also a method for constructing the underlying `Reinbow::Painter` instances manually. + +By default, coloring are enabled or disabled based on [*Standard for ANSI Colors in Terminals*](https://bixense.com/clicolors/), but methods are provided to toggle it manually. + +### Refinement + +Simply do: + +```ruby +# 1) import the gem +require "reinbow" + +module DesiredModule + # 2) "using" the Reinbow refinement in some module + using Reinbow + + # 3) all string instances in this scope + # will have coloring methods + def colored = @message.blue +end +``` + +Such way `reinbow` doesn't pollute the precious method namespace. + +### API + +Reinbow by default defines several methods to colorize or style strings, which have their names matching the [*SGR*](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR) counterparts. + +Full list of methods for applying terminal effects: + +* `#reset` +* `#bold` +* `#dim` +* `#italic` +* `#underline` +* `#blink` *a lot of terminals ignore this for accessability reasons* +* `#invert` +* `#hide` +* `#strike` + +Demo: + +```ruby +puts "strike bold and underline".strike.bold.underline +``` + +![screenshot of above code](./assets/strike-bold-underline.png) + +Full list of methods for coloring foreground and background, with the background ones having `on_` prefix. Note that their *bright* variants are **not yet** supported. + +* `#black` +* `#red` +* `#green` +* `#yellow` +* `#blue` +* `#magenta` +* `#cyan` +* `#white` +* `#default` +* `#on_black` +* `#on_red` +* `#on_green` +* `#on_yellow` +* `#on_blue` +* `#on_magenta` +* `#on_cyan` +* `#on_white` +* `#on_default` + +Demo: + +```ruby +puts "red on white".red.on_white +``` +![screenshot of above code](./assets/red-on-white.png) + + +The API also supports coloring with RGB values, and has a shorthand for using HEX string. Reinbow also comes with a full list of [*X11 Color Names*](https://en.wikipedia.org/wiki/X11_color_names). The detailed usage of both of them can be found in the *Examples* section below: + +* `#rgb( Reinbow::Rgb | String )` +* `#on_rgb( Reinbow::Rgb | String )` + +Demo: + +```ruby +puts "great readability" \ + .rgb( X11_COLORS[:lawngreen] ) \ + .on_rgb( "#faf0e6" ) +``` +![screenshot of above code](./assets/great-readability.png) + + +All coloring methods return `self` so that all method calls are chainable. + +```ruby +puts "well, your terminal, your land" \ + .rgb( X11_COLORS[:coral] ).on_magenta \ + .strike.italic.underline.bold +``` +![screenshot of above code](./assets/what.png) + + +There's also a method for turning reinbow functionality on and off. But note, unlike `rainbow`, there's **no global switch** for toggling it: + +* `#reinbow!( boolean )` + +And also a method for querying the on-off status: + +* `#reinbow?` + + +### `Reinbow::Painter` Class + +`T.B.D.` + + +## Examples + +### RGB Colors and X11 Color Names + +`Reinbow::Rgb` is a Data class for holding RGB values. It has following class methods: + +* `::new( red: 0..255, green: 0..255, blue: 0..255 )` +* `::[]( 0..255, 0..255, 0..255 )` +* `::hex( String )` + +Where `::[]` is a shorthand for the keyword based constructor, and `::hex` is for making `Rgb` instance from plain string HEX, which is case-insensitive and also supports 3-letter HEX. + +Example: + +```ruby +Rgb.new( red: 1, green: 133, blue: 0 ) +# or +Rgb[1, 133, 0] +# or +Rgb.hex( "#0b8500" ) + +# 3-letter HEX +Rgb.hex( "#abc" ) # => <... red=170, green=187, blue=204> +``` + +`Reinbow::X11_COLORS` is a predefined hash of [*X11 Colors Names*](https://en.wikipedia.org/wiki/X11_color_names) to their corresponding RGB values. The X11 colors are not provided as coloring methods because there are whopping 130+ of them, which will pollute the instance methods. + +Example: + +```ruby +X11 = Reinbow::X11_COLORS + +puts "crimson line".rgb( X11[:crimson] ).underline +``` + +![screenshot of above code](./assets/crimson-line.png) + + +## License + +[BSD-3-Clause](./LICENSE) diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..dd9dd9f --- /dev/null +++ b/Rakefile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new( :spec ) + +task default: :spec diff --git a/assets/crimson-line.png b/assets/crimson-line.png new file mode 100644 index 0000000000000000000000000000000000000000..eff0866b84c89ca2b593d9c4df8d85765119bad8 GIT binary patch literal 1382 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 I=n|gwH_av?t^fc4 literal 0 HcmV?d00001 diff --git a/assets/strike-bold-underline.png b/assets/strike-bold-underline.png new file mode 100644 index 0000000000000000000000000000000000000000..fa2e0afeded9e2b7b998ed41208761386805dfa2 GIT binary patch literal 2493 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 { code: } + @sgr_stack.push( "\e[#{code}m" ) + in Ansi + data => { raw_code: } + code = raw_code + ( layer == :fg ? 30 : 40 ) + @sgr_stack.push( "\e[#{code}m" ) + in Rgb + data => { red:, green:, blue: } + ground = layer == :fg ? 38 : 48 + @sgr_stack.push( "\e[#{ground};2;#{red};#{green};#{blue}m" ) + else + raise NotImplementedError, "Can't paint #{code.class}" + end + + self + end + + + # + # String behaviours + # + + def to_s + if @enable + sgr = @sgr_stack.join( nil ) + "#{sgr}#{@raw}\e[0m" + else + @raw + end + end + + def +( other ) = to_s + other.to_s + + end + +end diff --git a/reinbow.gemspec b/reinbow.gemspec new file mode 100644 index 0000000..da5bf13 --- /dev/null +++ b/reinbow.gemspec @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require_relative "./lib/reinbow" + +Gem::Specification.new do |spec| + + spec.name = "reinbow" + spec.version = Reinbow::VERSION + spec.authors = [ "MidAutumnMoon" ] + spec.description = "Ruby gem for colorizing text in terminal" + spec.summary = "Ruby gem for colorizing text in terminal" + spec.homepage = "https://github.com/MidAutumnMoon/Reinbow" + spec.license = "BSD-3-Clause" + spec.required_ruby_version = ">= 3.3.0" + + spec.metadata = { + "rubygems_mfa_required" => "true", + } + + spec.files = Dir[ + "lib/**/*", + "README.md", + "LICENSE" + ] + + spec.require_paths = [ "lib" ] + +end diff --git a/spec/color_ansi_spec.rb b/spec/color_ansi_spec.rb new file mode 100644 index 0000000..d201698 --- /dev/null +++ b/spec/color_ansi_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "spec_helper" + +require "reinbow/color/ansi" + +Ansi = Reinbow::Ansi + +describe Ansi do + + it "validates input" do + expect { Ansi.new( 100 ) }.to raise_error( ArgumentError ) + end + +end diff --git a/spec/color_rgb_spec.rb b/spec/color_rgb_spec.rb new file mode 100644 index 0000000..d277eb4 --- /dev/null +++ b/spec/color_rgb_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +require "reinbow/color/rgb" +require "reinbow/color/x11" + +Rgb = Reinbow::Rgb + +describe Rgb do + + it "validates all RGB values are in range" do + expect { Rgb.new( red: 256, green: 253, blue: -11 ) } + .to raise_error( ArgumentError ) + end + + it "has the shortcut initialization working" do + expect( Rgb[1, 2, 3] ).to \ + eq Rgb.new( red: 1, green: 2, blue: 3 ) + end + + context "when build RGB from HEX string" do + + let( :the_blue ) { Rgb[102, 204, 255] } + + it "validates input HEX" do + expect { Rgb.hex( "#znxcpl" ) }.to raise_error( ArgumentError ) + expect { Rgb.hex( "#xcpl" ) }.to raise_error( ArgumentError ) + end + + it "ignores leading #" do + expect( Rgb.hex( "#66ccff" ) ).to eq the_blue + expect( Rgb.hex( "66ccff" ) ).to eq the_blue + end + + it "converts HEX to RGB" do + expect( Rgb.hex( "#66ccff" ) ).to eq the_blue + end + + it "converts three letter HEX to RGB" do + expect( Rgb.hex( "#6cf" ) ).to eq the_blue + end + + end + +end diff --git a/spec/painter_spec.rb b/spec/painter_spec.rb new file mode 100644 index 0000000..b66c27e --- /dev/null +++ b/spec/painter_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require "spec_helper.rb" +require "reinbow/painter" + +Painter = Reinbow::Painter + +# Expected results should be verified visually. +describe Painter do + + subject { Painter.new( "lomn" ) } + + it "is its own class" do + expect( subject ).to be_a Painter + end + + context "behaves like a subset of string" do + it "has #to_s" do + expect( subject.to_s ).to eq( "lomn\e[0m" ) + end + + it "has #+" do + expect( "#{subject}!!" ).to eq( "lomn\e[0m!!" ) + end + end + + context "#paint!" do + x11 = Reinbow::X11_COLORS + + it "validates input data" do + expect { subject.paint!( data: "not accepted" ) } + .to raise_error( ArgumentError ) + + expect { subject.paint!( data: x11[:yellow], layer: :anywhere ) } + .to raise_error( ArgumentError ) + end + end + + context "shorthand methods for #paint!" do + ansi = Reinbow::ANSI_COLORS + + it "#fg for foreground" do + expect( subject.fg( ansi[:blue] ).to_s ) + .to eq( "\e[34mlomn\e[0m" ) + end + + it "#bg for background" do + expect( subject.bg( ansi[:blue] ).to_s ) + .to eq( "\e[44mlomn\e[0m" ) + end + end + + it "can be turned on and off" do + subject.reinbow!( false ) + expect( subject.italic.to_s ).to eq( "lomn" ) + subject.reinbow!( true ) + expect( subject.to_s ).to eq( "\e[3mlomn\e[0m" ) + end + + it "has methods to paint terminal effects" do + expect( subject.italic.bold.to_s ) + .to eq( "\e[3m\e[1mlomn\e[0m" ) + end + + it "has methods to paint ANSI terminal colors" do + expect( subject.red.on_blue.to_s ) + .to eq( "\e[31m\e[44mlomn\e[0m" ) + end + + context "paint RGB colors on terminal" do + x11 = Reinbow::X11_COLORS + + it "can use predefined colors" do + expect( subject.rgb( x11[:crimson] ).to_s ) + .to eq( "\e[38;2;220;20;60mlomn\e[0m" ) + end + + it "can use HEX as colors" do + expect( subject.rgb( "#66ccff" ).to_s ) + .to eq( "\e[38;2;102;204;255mlomn\e[0m" ) + end + + it "can use RGB as background color" do + expect( subject.on_rgb( x11[:crimson] ).to_s ) + .to eq( "\e[48;2;220;20;60mlomn\e[0m" ) + end + end + + it "can mix everything together" do + x11 = Reinbow::X11_COLORS + mess = subject.rgb( x11[:crimson] ).on_blue.italic.strike + expect( mess.to_s ) + .to eq( "\e[38;2;220;20;60m\e[44m\e[3m\e[9mlomn\e[0m" ) + end + + it "still provides access to the raw string" do + expect( subject.raw ).to eq( "lomn" ) + end + +end diff --git a/spec/refinement_spec.rb b/spec/refinement_spec.rb new file mode 100644 index 0000000..57bb19c --- /dev/null +++ b/spec/refinement_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "spec_helper" +require "reinbow" + + +module WithRefine + using Reinbow + def self.blue_hello = "hello".blue.to_s +end + +module WoRefine + def self.blue_hello = "hello".blue.to_s +end + + +describe "Refinement" do + + it "is using refinement" do + expect( WithRefine.blue_hello ) + .to eq( Reinbow( "hello" ).blue.to_s ) + end + + it "does not pullot objects" do + expect { WoRefine.blue_hello } + .to raise_error( NoMethodError ) + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..8e9b8f9 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1 @@ +# frozen_string_literal: true