Module: PryTheme

Defined in:
lib/pry-theme.rb,
lib/pry-theme/theme.rb,
lib/pry-theme/helper.rb,
lib/pry-theme/palette.rb,
lib/pry-theme/version.rb,
lib/pry-theme/commands.rb,
lib/pry-theme/uninstaller.rb,
lib/pry-theme/when_started_hook.rb

Defined Under Namespace

Modules: Formatting, Helper, Uninstaller Classes: Color, NoColorError, NoPaletteError, NoThemeError, Palette, Theme, WhenStartedHook

Constant Summary collapse

ROOT =

The root path for PryTheme source codes.

File.expand_path(File.dirname(__FILE__))
EXAMPLES_ROOT =

The root path for PryTheme examples.

File.join(ROOT, "..", "examples")
CONFIG_DIR =

The root path for the directory with configuration files for OS you’re using.

case RbConfig::CONFIG["host_os"]
when /mingw|mswin/
  File.join(ENV["APPDATA"], "pry-theme")
else
  # /darwin|linux/ and friends.
  File.join(ENV["HOME"], ".pry")
end
THEME_DIR =

Pry themes’ directory.

File.join(CONFIG_DIR, "themes")
DEFAULT_THEME_NAME =

The name of the default theme of Pry Theme.

"pry-classic"
COLLECTION =

The URI for GitHub API link to Pry Theme Collection contents.

"https://api.github.com/repos/kyrylo/pry-theme-collection/contents"
VERSION =
"0.0.9"
Commands =
Pry::CommandSet.new do

  create_command "pry-theme", "Manage your Pry themes." do
    include PryTheme::Helper

    banner <<-BANNER
      Usage: pry-theme [OPTIONS] [--help]

      Change your theme on the fly (for one session).

        pry-theme pry-modern

      Show all themes from Pry Theme Collection.

        pry-theme -lr

      Install a theme from Pry Theme Collection.

        pry-theme -i pry-classic

      Test your current color theme.

        pry-theme -t

      Wiki: https://github.com/kyrylo/pry-theme/wiki/Pry-Theme-CLI
    BANNER

    def options(opt)
      opt.on :a, "all-colors", "Show all available 8/256 colors."
      opt.on :c, "color",      "Show information about a specific color (256)."
      opt.on :t, "test",       "Test your current theme", :argument => false
      opt.on :l, "list",       "Show a list of installed themes", :argument => false
      opt.on :r, "remote",     "Show a list of themes from Pry Theme Collection", :argument => false
      opt.on :i, "install",    "Install a theme from Pry Theme Collection"
    end

    def process
      if opts.a?
        show_palette_colors
      elsif opts.c?
        show_specific_color
      elsif opts.t?
        test_theme
      elsif opts.l?
        opts.r? ? show_remote_list : show_list
      elsif opts.i?
        install_theme
      elsif args[0] =~ /\A\w+-?\w+\z/
        switch_to_theme
      elsif args.empty?
        output.puts "Current theme: #{PryTheme.current_theme}"
      end
    rescue NoPaletteError => no_palette_error
      warn no_palette_error
    rescue NoColorError => no_color_error
      warn no_color_error
    end

    private

    def switch_to_theme
      PryTheme.set_theme(args[0].strip) and
      output.puts "Using #{ args[0] } theme"
    end

    def show_palette_colors
      lputs Palette.new(args[0]).to_a.join("\n")
    end

    def show_specific_color
      unless args[0] =~ /\A(\d{1,3})\z/ && (0...256).include?($1.to_i)
        raise NoColorError, "Invalid color number: #{ args[0] }"
      end

      pal = Palette.new(256)
      color = pal.colors.detect { |c| c.term == args[0].to_i }

      if color
        output.puts color.to_term(pal.notation)
      end
    end

    def test_theme
      example = <<-TEST
# Time for testing your colors!
module PryTheme
module JustTesting

  THIS_IS_CONSTANT = :this_is_symbol

  class ThisIsClass

    def this_is_method
      this_is_float   = 10_000.00
      this_is_integer = 10_000

      "this_is_string"

      TRUE or FALSE or ARGV # <-- Predefined constants

      @this_is_instance_variable
      @@this_is_class_variable

      `echo 'Hello, hi, from "system" call!'`

      $ # <-- The dollar is an error!

      /[0-9]{1,3}this is regexp\\w+/xi
      $1 or $2 or $3333
    end

  end
end
end
# Testing "#{PryTheme.current_theme}" theme complete.
      TEST

      lputs colorize_code(example)
    end

    def show_list
      old_theme = PryTheme.current_theme.dup

      all_themes = installed_themes.map do |theme|
        theme = File.basename(theme, ".prytheme")
        meta = Theme.new(theme)
        PryTheme.set_theme(theme)

        chunk = <<-CHUNK
class PickMe
def please
  @i, @@beg, you = 10_000, 400.00, "please!"
end
end
        CHUNK

        mark_current = "* " if theme == old_theme
        header = make_bold("#{mark_current}[#{theme}]")
        snippet = colorize_code(chunk)
        [header, meta.description, "---", snippet].compact.join("\n")
      end

      lputs all_themes.join("\n")
    ensure
      PryTheme.set_theme(old_theme)
    end

    def show_remote_list
      body = {}
      fetch_collection("/") do |http, uri|
        output.puts "Fetching list of themes..."
        response = http.request(Net::HTTP::Get.new(uri.request_uri))
        body = JSON.parse(response.body)
      end

      i = 0
      remote_themes = body.map do |theme|
        if (name = theme["name"]) =~ /\A[[a-z][0-9]-]+\z/
          "#{i+=1}. #{installed?(name) ? make_bold(name) : name}"
        end
      end.compact

      lputs remote_themes.join("\n")
    end

    def install_theme
      return unless args[0]

      body = {}
      fetch_collection("/#{args[0]}/#{args[0]}.prytheme") do |http, uri|
        output.puts "Fetching theme from the collection..."
        response = http.request(Net::HTTP::Get.new(uri.request_uri))
        body = JSON.parse(response.body)
      end

      if body["message"]
        output.puts "Cannot find theme: #{args[0]}"
        return
      end

      theme = Base64.decode64(body["content"])

      File.open(local_theme("#{args[0]}.prytheme"), "w") do |f|
        f.puts theme
      end

      output.puts "Successfully installed #{args[0]}!"
    rescue
      output.puts "An error occurred!"
    end
  end

end

Class Method Summary collapse

Class Method Details

.color_to_term(color, palette) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/pry-theme.rb', line 76

def self.color_to_term(color, palette)
  color_pattern = /
                    \A

                    # Matches "yellow".
                    (
                      (
                        \w+(0[1-9])?
                      )
                      \s?
                    )?

                    # Matches "yellow (bu)" or "(bu)".
                    (
                      \(
                        (
                          d?b?u?i? # Order matters.
                        )
                      \)
                    )?


                    # Matches "yellow (bu) on red" or "on red".
                    (
                      \s?
                      on\s
                      (
                        [a-z]+(0[1-9])?
                      )
                    )?

                    \z
                  /x

  if color
    m = color.match(color_pattern)

    color_fg   = if $2
                   c = palette.colors.find do |color|
                     color.human == $2.to_sym
                   end

                   if c
                     c.term
                   else
                     raise NoColorError
                   end
                 end

    formatting = if $5
                   formatting = $5.each_char.map do |ch|
                     Formatting::ATTRIBUTES[ch]
                   end
                 end

    color_bg   = if $7
                   Formatting::BACKGROUNDS[$7]
                 end

    # Uh oh :(
    notation = if !color_fg
                 "38;0"
               elsif palette.notation
                 palette.notation[0..-2]
               else
                 nil
               end

    [notation, color_fg, formatting, color_bg].flatten.compact.join(";")
  else
    # In cases when a user decided not to provide an argument value in theme,
    # use default color. Not handling this situation results in CodeRay's
    # error ("can't convert nil into String" stuff).
    "38;0;0"
  end
rescue NoColorError => e
  Pry.output.puts "#{e}: wrong color value: `#{$2}`. Typo?"
end

.convert(theme_name) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/pry-theme.rb', line 48

def self.convert(theme_name)
  begin
    theme = Theme.new(theme_name)
  rescue NoThemeError => no_theme_error
    warn no_theme_error
    return
  end

  palette = Palette.new(theme.color_depth)
  scheme  = {}

  theme.scheme.each_pair do |k, v|
    if v.is_a?(Hash)
      nested_h = {}

      v.each_pair do |nested_k, nested_v|
        nested_h[nested_k.to_sym] = color_to_term(nested_v, palette)
      end

      scheme[k.to_sym] = nested_h
    else
      scheme[k.to_sym] = color_to_term(v, palette)
    end
  end

  scheme
end

.current_themeObject



44
45
46
# File 'lib/pry-theme.rb', line 44

def self.current_theme
  @current_theme
end

.install_gem_hooksObject



155
156
157
158
159
# File 'lib/pry-theme.rb', line 155

def self.install_gem_hooks
  Gem.post_uninstall do |u|
    Uninstaller.run(u) if u.spec.name == "pry-theme"
  end
end

.set_theme(theme_name) ⇒ Object



38
39
40
41
42
# File 'lib/pry-theme.rb', line 38

def self.set_theme(theme_name)
  return unless theme = PryTheme.convert(theme_name)
  ::CodeRay::Encoders::Terminal::TOKEN_COLORS.merge!(theme)
  @current_theme = theme_name
end