code_picture 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 792f42d837cfad8b26ee37ca8c0142f1b8d6aee810b00adead0f5dd16e3524e0
4
+ data.tar.gz: f95f9fbd072f0b3acadfc8e26a5a59855ae85ef0cc542cfa3696330760c5cf9f
5
+ SHA512:
6
+ metadata.gz: 1070731c52f61e01f4203cf6a125542aa52eaae1f65b9a1543d99f7ec52b9fd5289e2a90cb4c6334338ffa970f81044558824bf5918fb62b5f53e37e0eae2582
7
+ data.tar.gz: 1ea47b349265298c25b89a80132c794d5778ccc9fd59c5e775267d68046abbf59f8033020086d47598ea32a59c097bb12a25b1ddf887fc261359e278941b0647
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-12-15
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Matheus Richard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Code Picture
2
+
3
+ Code Picture parses a Ruby file and generates a picture of the code structure.
4
+ It leverages the [Prism parser] to lex the code and then generates a
5
+ simple HTML pixel art.
6
+
7
+ [Prism parser]: https://github.com/ruby/prism
8
+
9
+ ## Usage
10
+
11
+ ### CLI
12
+
13
+ Install the gem
14
+
15
+ ```sh
16
+ gem install code_picture
17
+ ```
18
+
19
+ Then run the command:
20
+
21
+ ```sh
22
+ code-picture path-to-file.rb
23
+ ```
24
+
25
+ This will generate a `code-picture.html` file in the current directory.
26
+
27
+ #### Options
28
+
29
+ You can customize the generated image and the output file with the following
30
+ options:
31
+
32
+ ```
33
+ Usage: code-picture path-to-file.rb [options]
34
+ -p, --pixel-size=SIZE Define the pixel size of the generated image
35
+ -r, --max-pixels-per-row=SIZE Define the maximum number of pixels per row
36
+ -t, --theme=THEME Define the theme of the generated image [options: one-dark-pro (default), random, or a path to a YAML file]
37
+ -o, --output=FILE Write the generated image to the given file path
38
+ -v, --version Displays app version
39
+ -h, --help Prints this help
40
+ ```
41
+
42
+ ## Development
43
+
44
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
45
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
46
+ prompt that will allow you to experiment.
47
+
48
+ To install this gem onto your local machine, run `bundle exec rake install`. To
49
+ release a new version, update the version number in `version.rb`, and then run
50
+ `bundle exec rake release`, which will create a git tag for the version, push
51
+ git commits and the created tag, and push the `.gem` file to
52
+ [rubygems.org](https://rubygems.org).
53
+
54
+ ## Contributing
55
+
56
+ Bug reports and pull requests are welcome on GitHub at
57
+ https://github.com/[USERNAME]/code_picture.
58
+
59
+ ## License
60
+
61
+ The gem is available as open source under the terms of the [MIT
62
+ License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "standard/rake"
9
+ require "code_picture"
10
+
11
+ task default: %i[spec standard]
12
+
13
+ task :check_missing_token_types do
14
+ require "yaml"
15
+ require "net/http"
16
+
17
+ prism_config_url = "https://raw.githubusercontent.com/ruby/prism/a590c031130ab024488177fa7ccf18c970dce20d/config.yml"
18
+ all_tokens = YAML.load(Net::HTTP.get(URI(prism_config_url)))["tokens"].map { _1["name"].to_sym }
19
+ current_tokens = CodePicture::Theme.one_dark_pro.colors.keys
20
+ missing_tokens = (all_tokens - current_tokens) - CodePicture::IGNORED_TOKENS
21
+
22
+ if missing_tokens.any?
23
+ abort missing_tokens.join("\n")
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/code_picture/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "code_picture"
7
+ spec.version = CodePicture::VERSION
8
+ spec.authors = ["Matheus Richard"]
9
+ spec.email = ["matheusrichardt@gmail.com"]
10
+
11
+ spec.summary = "Transform your code into a picture."
12
+ spec.description = "CodePicture lexes your code and assign a color to each token. Then, it generates a picture with the same size as the code, where each pixel represents a token."
13
+ spec.homepage = "https://github.com/MatheusRich/code_picture"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ # spec.metadata["homepage_uri"] = spec.homepage
20
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
21
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) ||
28
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
29
+ end
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ # Uncomment to register a new dependency of your gem
36
+ spec.add_dependency "zeitwerk", "~> 2.6"
37
+ spec.add_dependency "prism", "~> 0.19.0"
38
+
39
+ # For more information and examples about making a new gem, check out our
40
+ # guide at: https://bundler.io/guides/creating_gem.html
41
+ end
data/exe/code-picture ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift("#{__dir__}/../lib")
4
+
5
+ require "code_picture"
6
+
7
+ CodePicture::Cli.new
8
+ .call(ARGV)
9
+ .on_failure { abort "Error: #{_1}" }
@@ -0,0 +1,21 @@
1
+ class CodePicture
2
+ class Cli
3
+ module Commands
4
+ module Help
5
+ Options = Data.define(:error, :help_text) do
6
+ def initialize(help_text:, error: nil) = super
7
+ end
8
+
9
+ def self.call(options)
10
+ puts options.help_text
11
+
12
+ if options.error
13
+ Result::Failure.new(error: options.error)
14
+ else
15
+ Result::Success.new(value: nil)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ class CodePicture
2
+ class Cli
3
+ module Commands
4
+ module Result
5
+ Failure = Data.define(:error) do
6
+ def successful? = false
7
+
8
+ def failed? = true
9
+
10
+ def on_success = self
11
+
12
+ def on_failure = yield(error)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ class CodePicture
2
+ class Cli
3
+ module Commands
4
+ module Result
5
+ Success = Data.define(:value) do
6
+ def successful? = true
7
+
8
+ def failed? = false
9
+
10
+ def on_success = yield(value)
11
+
12
+ def on_failure = self
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ class CodePicture
2
+ class Cli
3
+ module Commands
4
+ module TakePicture
5
+ Options = Data.define(:input_file_path, :output_file_path, *CodePicture::Options.members) do
6
+ def self.default = new(
7
+ input_file_path: nil,
8
+ output_file_path: "code-picture.html",
9
+ **CodePicture::Options.default.to_h
10
+ )
11
+ end
12
+
13
+ extend self
14
+
15
+ def call(options)
16
+ code_picture = take_picture(options)
17
+ save_picture(code_picture, options.output_file_path)
18
+
19
+ Result::Success.new(value: code_picture)
20
+ rescue Errno::ENOENT
21
+ Result::Failure.new(error: "Couldn't find file `#{options.input_file_path}`")
22
+ rescue CodePicture::Theme::Error => error
23
+ Result::Failure.new(error: error.message)
24
+ end
25
+
26
+ private
27
+
28
+ def take_picture(options)
29
+ source_code = File.read(options.input_file_path)
30
+
31
+ ::CodePicture.new(source_code, CodePicture::Options.from(options)).to_html
32
+ end
33
+
34
+ def save_picture(code_picture, output_file_path)
35
+ File.write(output_file_path, code_picture)
36
+ puts "Generated code picture and wrote it to `#{output_file_path}`"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ class CodePicture
2
+ class Cli
3
+ module Commands
4
+ Version = ->(_) do
5
+ puts "code-picture v#{::CodePicture::VERSION}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,71 @@
1
+ class CodePicture
2
+ class Cli
3
+ Options = Data.define(:command, :command_options) do
4
+ def self.empty = new(command: nil, command_options: Commands::TakePicture::Options.default)
5
+
6
+ def self.from(argv)
7
+ options = Cli::Options.empty
8
+
9
+ opt_parser = OptionParser.new do |parser|
10
+ parser.banner = "Usage: code-picture path-to-file.rb [options]"
11
+
12
+ parser.on("-p", "--pixel-size=SIZE", Integer, "Define the pixel size of the generated image") do |size|
13
+ command_options = options.command_options.with(pixel_size: size)
14
+ options = options.with(command_options:)
15
+ end
16
+
17
+ parser.on("-r", "--max-pixels-per-row=SIZE", Integer, "Define the maximum number of pixels per row") do |size|
18
+ command_options = options.command_options.with(max_pixels_per_row: size)
19
+ options = options.with(command_options:)
20
+ end
21
+
22
+ parser.on("-t", "--theme=THEME", String, "Define the theme of the generated image [options: one-dark-pro (default), random, or a path to a YAML file]") do |theme|
23
+ command_options = options.command_options.with(theme: theme)
24
+ options = options.with(command_options:)
25
+ end
26
+
27
+ parser.on("-o", "--output=FILE", String, "Write the generated image to the given file path") do |file|
28
+ command_options = options.command_options.with(output_file_path: file)
29
+ options = options.with(command_options:)
30
+ end
31
+
32
+ parser.on("-v", "--version", "Displays app version") do
33
+ options = options.with(command: Commands::Version, command_options: nil)
34
+ end
35
+ parser.on("-h", "--help", "Prints this help") do
36
+ options = Cli::Options.new(
37
+ command: Commands::Help,
38
+ command_options: Commands::Help::Options.new(help_text: parser.to_s)
39
+ )
40
+ end
41
+ end
42
+
43
+ begin
44
+ opt_parser.parse!(argv)
45
+ rescue OptionParser::InvalidOption
46
+ options = Cli::Options.new(
47
+ command: Commands::Help,
48
+ command_options: Commands::Help::Options.new(help_text: opt_parser.to_s)
49
+ )
50
+ end
51
+
52
+ if (input_file_path = argv.first)
53
+ options.with(
54
+ command: Commands::TakePicture,
55
+ command_options: options.command_options.with(input_file_path:)
56
+ )
57
+ elsif options.command.nil?
58
+ options.with(
59
+ command: Commands::Help,
60
+ command_options: Commands::Help::Options.new(
61
+ error: "Missing input file path",
62
+ help_text: opt_parser.to_s
63
+ )
64
+ )
65
+ else
66
+ options
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ require "optparse"
2
+
3
+ class CodePicture
4
+ class Cli
5
+ def call(argv)
6
+ Options
7
+ .from(argv)
8
+ .then { _1.command.call(_1.command_options) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ class CodePicture
2
+ Options = Data.define(:pixel_size, :theme, :max_pixels_per_row) do
3
+ def self.default = new(pixel_size: 15, theme: Theme.one_dark_pro, max_pixels_per_row: nil)
4
+
5
+ def self.from(other)
6
+ theme = if other.theme.is_a?(String)
7
+ Theme.find(other.theme)
8
+ else
9
+ other.theme
10
+ end
11
+
12
+ options = other.to_h.slice(*members).merge(theme:)
13
+
14
+ new(**options)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Code Picture</title>
7
+
8
+ <style>
9
+ .pixel {
10
+ display: table-cell;
11
+ width: <%= pixel_size %>px;
12
+ min-width: <%= pixel_size %>px;
13
+ max-width: <%= pixel_size %>px;
14
+ height: <%= pixel_size %>px;
15
+ min-height: <%= pixel_size %>px;
16
+ max-height: <%= pixel_size %>px;
17
+ position: relative;
18
+ }
19
+
20
+ .pixel:hover::after {
21
+ display: block;
22
+ content: attr(data-content);
23
+ position: absolute;
24
+ z-index: 100;
25
+ top: 30px;
26
+ background: white;
27
+ color: black;
28
+ padding: 5px;
29
+ min-width: 150px;
30
+ border-radius: 5px;
31
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
32
+ }
33
+
34
+ .row {
35
+ display: table-row;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ <div>
41
+ <%- rows.each do |row| -%>
42
+ <div class="row">
43
+ <%- row.each do |token| -%>
44
+ <span class="pixel"
45
+ data-content="<%= token.type %>"
46
+ style="background-color: <%= theme.color_for(token.type) %>"></span>
47
+ <%- end -%>
48
+ </div>
49
+ <%- end -%>
50
+ </div>
51
+ </body>
52
+ </html>
@@ -0,0 +1,53 @@
1
+ class CodePicture
2
+ Theme = Data.define(:colors) do
3
+ self::Error = Class.new(StandardError)
4
+
5
+ def self.find(name)
6
+ case name.to_s.tr("-", "_")
7
+ when "one_dark_pro"
8
+ one_dark_pro
9
+ when "random"
10
+ random
11
+ else
12
+ from_file(name)
13
+ end
14
+ end
15
+
16
+ def self.from_file(file_name)
17
+ require "yaml"
18
+ colors = YAML.load_file(file_name, symbolize_names: true)
19
+
20
+ unless colors.is_a?(Hash)
21
+ raise self::Error, "Theme file `#{file_name}` must be a mapping of token types to colors"
22
+ end
23
+
24
+ new(colors:)
25
+ rescue Psych::SyntaxError
26
+ raise self::Error, "Invalid syntax in theme file `#{file_name}`"
27
+ rescue Errno::ENOENT
28
+ raise self::Error, "Couldn't find theme file `#{file_name}`"
29
+ end
30
+
31
+ def self.default = one_dark_pro
32
+
33
+ def self.random
34
+ theme = {}
35
+ new(->(token_type) {
36
+ theme[token_type] ||= random_color
37
+ })
38
+ end
39
+
40
+ def self.one_dark_pro
41
+ from_file(File.expand_path("../themes/one_dark_pro.yml", __FILE__))
42
+ end
43
+
44
+ def self.random_color
45
+ "hsl(#{rand(0..360)},#{rand(42..98)}%,#{rand(40..90)}%)"
46
+ end
47
+ private_class_method :random_color
48
+
49
+ def color_for(token_type)
50
+ colors[token_type] || raise(self.class::Error, "No theme color defined for token type `#{token_type}`")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,157 @@
1
+ AMPERSAND: "#282c34"
2
+ AMPERSAND_AMPERSAND: "#282c34"
3
+ AMPERSAND_AMPERSAND_EQUAL: "#282c34"
4
+ AMPERSAND_DOT: "#282c34"
5
+ AMPERSAND_EQUAL: "#282c34"
6
+ BACKTICK: "#282c34"
7
+ BACK_REFERENCE: "#ABB2BF"
8
+ BANG: "#282c34"
9
+ BANG_EQUAL: "#282c34"
10
+ BANG_TILDE: "#282c34"
11
+ BRACE_LEFT: "#282c34"
12
+ BRACE_RIGHT: "#282c34"
13
+ BRACKET_LEFT: "#282c34"
14
+ BRACKET_LEFT_ARRAY: "#282c34"
15
+ BRACKET_LEFT_RIGHT: "#282c34"
16
+ BRACKET_LEFT_RIGHT_EQUAL: "#282c34"
17
+ BRACKET_RIGHT: "#282c34"
18
+ CARET: "#282c34"
19
+ CARET_EQUAL: "#282c34"
20
+ CHARACTER_LITERAL: "#ABB2BF"
21
+ CLASS_VARIABLE: "#e06c75"
22
+ COLON: "#282c34"
23
+ COLON_COLON: "#282c34"
24
+ COMMA: "#282c34"
25
+ COMMENT: "#5c6370"
26
+ CONSTANT: "#e5c07b"
27
+ DOT: "#282c34"
28
+ DOT_DOT: "#282c34"
29
+ DOT_DOT_DOT: "#282c34"
30
+ EMBDOC_BEGIN: "#5c6370"
31
+ EMBDOC_LINE: "#5c6370"
32
+ EMBEXPR_BEGIN: "#282c34"
33
+ EMBEXPR_END: "#282c34"
34
+ EMBVAR: "#ABB2BF"
35
+ EQUAL: "#282c34"
36
+ EQUAL_EQUAL: "#282c34"
37
+ EQUAL_EQUAL_EQUAL: "#282c34"
38
+ EQUAL_GREATER: "#282c34"
39
+ EQUAL_TILDE: "#282c34"
40
+ FLOAT: "#e5c07b"
41
+ FLOAT_IMAGINARY: "#e5c07b"
42
+ FLOAT_RATIONAL: "#e5c07b"
43
+ FLOAT_RATIONAL_IMAGINARY: "#e5c07b"
44
+ GLOBAL_VARIABLE: "#e06c75"
45
+ GREATER: "#282c34"
46
+ GREATER_EQUAL: "#282c34"
47
+ GREATER_GREATER: "#282c34"
48
+ GREATER_GREATER_EQUAL: "#282c34"
49
+ HEREDOC_END: "#98c379"
50
+ HEREDOC_START: "#98c379"
51
+ IDENTIFIER: "#e06c75"
52
+ INSTANCE_VARIABLE: "#e06c75"
53
+ INTEGER: "#e5c07b"
54
+ INTEGER_IMAGINARY: "#e5c07b"
55
+ INTEGER_RATIONAL: "#e5c07b"
56
+ INTEGER_RATIONAL_IMAGINARY: "#e5c07b"
57
+ KEYWORD_ALIAS: "#C678DD"
58
+ KEYWORD_AND: "#C678DD"
59
+ KEYWORD_BEGIN: "#C678DD"
60
+ KEYWORD_BEGIN_UPCASE: "#C678DD"
61
+ KEYWORD_BREAK: "#C678DD"
62
+ KEYWORD_CASE: "#C678DD"
63
+ KEYWORD_CLASS: "#C678DD"
64
+ KEYWORD_DEF: "#C678DD"
65
+ KEYWORD_DEFINED: "#C678DD"
66
+ KEYWORD_DO: "#c678dd"
67
+ KEYWORD_DO_LOOP: "#C678DD"
68
+ KEYWORD_ELSE: "#C678DD"
69
+ KEYWORD_ELSIF: "#C678DD"
70
+ KEYWORD_END: "#c678dd"
71
+ KEYWORD_END_UPCASE: "#C678DD"
72
+ KEYWORD_ENSURE: "#C678DD"
73
+ KEYWORD_FALSE: "#C678DD"
74
+ KEYWORD_FOR: "#C678DD"
75
+ KEYWORD_IF: "#C678DD"
76
+ KEYWORD_IF_MODIFIER: "#c678dd"
77
+ KEYWORD_IN: "#C678DD"
78
+ KEYWORD_MODULE: "#C678DD"
79
+ KEYWORD_NEXT: "#C678DD"
80
+ KEYWORD_NIL: "#e5c07b"
81
+ KEYWORD_NOT: "#C678DD"
82
+ KEYWORD_OR: "#C678DD"
83
+ KEYWORD_REDO: "#C678DD"
84
+ KEYWORD_RESCUE: "#C678DD"
85
+ KEYWORD_RESCUE_MODIFIER: "#C678DD"
86
+ KEYWORD_RETRY: "#C678DD"
87
+ KEYWORD_RETURN: "#c678dd"
88
+ KEYWORD_SELF: "#C678DD"
89
+ KEYWORD_SUPER: "#C678DD"
90
+ KEYWORD_THEN: "#C678DD"
91
+ KEYWORD_TRUE: "#C678DD"
92
+ KEYWORD_UNDEF: "#C678DD"
93
+ KEYWORD_UNLESS: "#C678DD"
94
+ KEYWORD_UNLESS_MODIFIER: "#C678DD"
95
+ KEYWORD_UNTIL: "#C678DD"
96
+ KEYWORD_UNTIL_MODIFIER: "#C678DD"
97
+ KEYWORD_WHEN: "#C678DD"
98
+ KEYWORD_WHILE: "#C678DD"
99
+ KEYWORD_WHILE_MODIFIER: "#C678DD"
100
+ KEYWORD_YIELD: "#C678DD"
101
+ KEYWORD___ENCODING__: "#e5c07b"
102
+ KEYWORD___FILE__: "#e5c07b"
103
+ KEYWORD___LINE__: "#e5c07b"
104
+ LABEL: "#56b6c2"
105
+ LABEL_END: "#56b6c2"
106
+ LAMBDA_BEGIN: "#282c34"
107
+ LESS: "#282c34"
108
+ LESS_EQUAL: "#282c34"
109
+ LESS_EQUAL_GREATER: "#282c34"
110
+ LESS_LESS: "#282c34"
111
+ LESS_LESS_EQUAL: "#282c34"
112
+ METHOD_NAME: "#61afef"
113
+ MINUS: "#282c34"
114
+ MINUS_EQUAL: "#282c34"
115
+ MINUS_GREATER: "#282c34"
116
+ NUMBERED_REFERENCE: "#e06c75"
117
+ PARENTHESIS_LEFT: "#282c34"
118
+ PARENTHESIS_LEFT_PARENTHESES: "#282c34"
119
+ PARENTHESIS_RIGHT: "#282c34"
120
+ PERCENT: "#282c34"
121
+ PERCENT_EQUAL: "#282c34"
122
+ PERCENT_LOWER_I: "#56b6c2"
123
+ PERCENT_LOWER_W: "#56b6c2"
124
+ PERCENT_LOWER_X: "#56b6c2"
125
+ PERCENT_UPPER_I: "#56b6c2"
126
+ PERCENT_UPPER_W: "#56b6c2"
127
+ PIPE: "#282c34"
128
+ PIPE_EQUAL: "#282c34"
129
+ PIPE_PIPE: "#282c34"
130
+ PIPE_PIPE_EQUAL: "#282c34"
131
+ PLUS: "#282c34"
132
+ PLUS_EQUAL: "#282c34"
133
+ QUESTION_MARK: "#282c34"
134
+ REGEXP_BEGIN: "#e06c75"
135
+ REGEXP_END: "#e06c75"
136
+ SEMICOLON: "#282c34"
137
+ SLASH: "#282c34"
138
+ SLASH_EQUAL: "#282c34"
139
+ STAR: "#282c34"
140
+ STAR_EQUAL: "#282c34"
141
+ STAR_STAR: "#282c34"
142
+ STAR_STAR_EQUAL: "#282c34"
143
+ STRING_BEGIN: "#98c379"
144
+ STRING_CONTENT: "#98c379"
145
+ STRING_END: "#98c379"
146
+ SYMBOL_BEGIN: "#282c34"
147
+ TILDE: "#282c34"
148
+ UAMPERSAND: "#56b6c2"
149
+ UCOLON_COLON: "#ABB2BF"
150
+ UDOT_DOT: "#ABB2BF"
151
+ UDOT_DOT_DOT: "#ABB2BF"
152
+ UMINUS: "#61afef"
153
+ UMINUS_NUM: "#61afef"
154
+ UPLUS: "#61afef"
155
+ USTAR: "#56b6c2"
156
+ USTAR_STAR: "#56b6c2"
157
+ __END__: "#e5c07b"
@@ -0,0 +1,3 @@
1
+ class CodePicture
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+ require "erb"
5
+ require "zeitwerk"
6
+
7
+ Zeitwerk::Loader.for_gem.setup
8
+
9
+ class CodePicture
10
+ IGNORED_TOKENS = %i[
11
+ EOF
12
+ IGNORED_NEWLINE
13
+ NEWLINE
14
+ WORDS_SEP
15
+ MISSING
16
+ NOT_PROVIDED
17
+ ]
18
+ HTML_TEMPLATE = File.read(File.expand_path("../code_picture/template.erb", __FILE__))
19
+
20
+ def initialize(code, options = Options.default)
21
+ @tokens = Prism
22
+ .lex(code)
23
+ .value
24
+ .filter_map { |token, _| token if !IGNORED_TOKENS.include?(token.type) }
25
+ @options = options
26
+ end
27
+
28
+ def to_html
29
+ row_size = @options.max_pixels_per_row || Math.sqrt(@tokens.size).ceil
30
+ rows = @tokens.each_slice(row_size)
31
+
32
+ ERB.new(HTML_TEMPLATE, trim_mode: "-").result(binding)
33
+ end
34
+
35
+ private
36
+
37
+ def theme = @options.theme
38
+
39
+ def pixel_size = @options.pixel_size
40
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: code_picture
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matheus Richard
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: prism
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.19.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.19.0
41
+ description: CodePicture lexes your code and assign a color to each token. Then, it
42
+ generates a picture with the same size as the code, where each pixel represents
43
+ a token.
44
+ email:
45
+ - matheusrichardt@gmail.com
46
+ executables:
47
+ - code-picture
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".rspec"
52
+ - ".tool-versions"
53
+ - CHANGELOG.md
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - code_picture.gemspec
58
+ - exe/code-picture
59
+ - lib/code_picture.rb
60
+ - lib/code_picture/cli.rb
61
+ - lib/code_picture/cli/commands/help.rb
62
+ - lib/code_picture/cli/commands/result/failure.rb
63
+ - lib/code_picture/cli/commands/result/success.rb
64
+ - lib/code_picture/cli/commands/take_picture.rb
65
+ - lib/code_picture/cli/commands/version.rb
66
+ - lib/code_picture/cli/options.rb
67
+ - lib/code_picture/options.rb
68
+ - lib/code_picture/template.erb
69
+ - lib/code_picture/theme.rb
70
+ - lib/code_picture/themes/one_dark_pro.yml
71
+ - lib/code_picture/version.rb
72
+ homepage: https://github.com/MatheusRich/code_picture
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.2.0
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.4.10
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Transform your code into a picture.
95
+ test_files: []