prettyrb 0.1.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6627cfb70e4dee209000644f4be5d57d6794c240fda5767b4b85d877c7cb2ed5
4
- data.tar.gz: ae15fc8e9b154777b0bdaad845047b8a8c6709ee0e8e7616bcabb77cf12f3f06
3
+ metadata.gz: f3ca66d3c436cf92ecaeef327cd56e32380eb956c844cafcc605e471f80e5a9d
4
+ data.tar.gz: 6a88f83ea9a52c7cebcb0c70d72a8f599c8480e0a628c2eba016ce506ef99996
5
5
  SHA512:
6
- metadata.gz: 5e0543768d14fbb1fa9715a2c1657bbcc4d376939e342cd7badc14cddde7a0b8c8354d3ead3321b0a14ad5143fae8381c4eaf55e4d3c66785ac0eb8300ae75d7
7
- data.tar.gz: 7d186ac5763c031e88d4357d00cae4afe6ba002d1a82381bf8f00b8050b8c33ca9e3f9a4ca8dde1312488fba002196544ffea134bdb144cefca2a05350a55c13
6
+ metadata.gz: 3fb301879d8c8905aa82268ac2d04944fc02eb4a3e9d28466b80a70dd3e3b2e1ad93dba3a4916736e5c5533c067eb69b869a610f12ee6746d61ec5165e43d128
7
+ data.tar.gz: 3a2894fd21b9493aec14b1d85a08e3be929e356ad7dd2dee013409f2ec3dcd47f914ab15d6028146e769533812abb786bbce415d449edcaefd679093c06d5af5
@@ -0,0 +1,24 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ build:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby 2.6
17
+ uses: actions/setup-ruby@v1
18
+ with:
19
+ ruby-version: 2.6.x
20
+ - name: Build and test with Rake
21
+ run: |
22
+ gem install bundler
23
+ bundle install --jobs 4 --retry 3
24
+ bundle exec rake
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ coverage
@@ -0,0 +1 @@
1
+ 2.6.0
@@ -1,26 +1,40 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prettyrb (0.1.0)
5
- ruby_parser (~> 3.14)
4
+ prettyrb (0.5.0)
5
+ parser (~> 2.7.0.5)
6
+ thor
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ ast (2.4.0)
12
+ coderay (1.1.2)
13
+ docile (1.3.2)
14
+ method_source (1.0.0)
10
15
  minitest (5.14.0)
11
- rake (10.5.0)
12
- ruby_parser (3.14.2)
13
- sexp_processor (~> 4.9)
14
- sexp_processor (4.14.1)
16
+ parser (2.7.0.5)
17
+ ast (~> 2.4.0)
18
+ pry (0.13.1)
19
+ coderay (~> 1.1)
20
+ method_source (~> 1.0)
21
+ rake (12.3.3)
22
+ simplecov (0.18.5)
23
+ docile (~> 1.1)
24
+ simplecov-html (~> 0.11)
25
+ simplecov-html (0.12.2)
26
+ thor (1.0.1)
15
27
 
16
28
  PLATFORMS
17
29
  ruby
18
30
 
19
31
  DEPENDENCIES
20
- bundler (~> 1.17)
32
+ bundler (~> 2.1.4)
21
33
  minitest (~> 5.0)
22
34
  prettyrb!
23
- rake (~> 10.0)
35
+ pry (~> 0.13)
36
+ rake (~> 12.3.3)
37
+ simplecov (~> 0.18)
24
38
 
25
39
  BUNDLED WITH
26
- 1.17.2
40
+ 2.1.4
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Prettyrb
2
2
 
3
- Super experimental gem for auto-formatting Ruby files. Pronounced "pretty-erb"
3
+ Super experimental gem for auto-formatting Ruby files. AKA the code isn't great, but things are kind of working.
4
+
5
+ Pronounced "pretty-erb".
4
6
 
5
7
  ## Installation
6
8
 
@@ -20,12 +22,24 @@ Or install it yourself as:
20
22
 
21
23
  ## Usage
22
24
 
25
+ CLI:
26
+
27
+ ```
28
+ $ prettyrb format FILE
29
+ ```
30
+
31
+ or to re-write the file:
32
+
33
+ ```
34
+ prettyrb format FILE --write
35
+ ```
36
+
37
+ In Ruby code:
38
+
23
39
  ```ruby
24
40
  PrettyRb.new(source_code).format
25
41
  ```
26
42
 
27
- CLI helper coming soon™
28
-
29
43
  ## License
30
44
 
31
45
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "prettyrb"
4
+ require "prettyrb/cli"
5
+
6
+ Prettyrb::CLI.start
@@ -1,191 +1,29 @@
1
- require "ruby_parser"
1
+ require "forwardable"
2
+
3
+ require "parser/current"
2
4
  require "prettyrb/version"
3
5
 
6
+ require "prettyrb/nodes/base_node"
7
+ require "prettyrb/nodes/if_node"
8
+ require "prettyrb/nodes/string_helper"
9
+ require "prettyrb/nodes/logical_operator_helper"
10
+ require "prettyrb/nodes/str_node"
11
+ require "prettyrb/nodes/dstr_node"
12
+ require "prettyrb/nodes/def_node"
13
+ require "prettyrb/nodes/and_node"
14
+ require "prettyrb/nodes/or_node"
15
+ require "prettyrb/nodes/regexp_node"
16
+ require "prettyrb/nodes/send_node"
17
+
18
+ require "prettyrb/document"
19
+ require "prettyrb/builder"
20
+ require "prettyrb/formatter"
21
+ require "prettyrb/visitor"
22
+ require "prettyrb/writer"
23
+
24
+ require "prettyrb/cli"
25
+
4
26
  module Prettyrb
27
+ MAX_LINE_LENGTH = 100
5
28
  class Error < StandardError; end
6
-
7
- class Formatter
8
- def self.for(sexp)
9
- case sexp.sexp_type
10
- when :if
11
- IfFormatter
12
- when :str
13
- StringFormatter
14
- when :or
15
- OrFormatter
16
- when :and
17
- AndFormatter
18
- when :lit
19
- LitFormatter
20
- when :call
21
- CallFormatter
22
- when :class
23
- ClassFormatter
24
- when :cdecl
25
- CdeclFormatter
26
- when :array
27
- ArrayFormatter
28
- else
29
- raise "can't handle #{sexp}"
30
- end
31
- end
32
-
33
- def initialize(code)
34
- @code = code
35
- end
36
-
37
- def format
38
- sexp_tree = RubyParser.new.parse(@code)
39
-
40
- self.class.for(sexp_tree).new(sexp_tree, 0, nil).format
41
- end
42
-
43
- private
44
-
45
- def format_type(sexp, indentation)
46
- case sexp.sexp_type
47
- when :if
48
- IfFormatter.new(sexp, indentation).format
49
- else
50
- raise "can't handle #{sexp}"
51
- end
52
- end
53
-
54
- attr_reader :code
55
- end
56
-
57
- class BaseFormatter
58
- attr_reader :sexp, :indentation, :parent
59
-
60
- def initialize(sexp, indentation, parent)
61
- @sexp = sexp
62
- @indentation = indentation
63
- @parent = parent
64
- end
65
-
66
- def type
67
- sexp.sexp_type
68
- end
69
-
70
- def indents
71
- ' ' * indentation
72
- end
73
- end
74
-
75
- class LitFormatter < BaseFormatter
76
- def format
77
- sexp[1]
78
- end
79
- end
80
-
81
- class StringFormatter < BaseFormatter
82
- def format
83
- if parent.type == :array
84
- "\"#{sexp[1]}\""
85
- else
86
- "#{indents}\"#{sexp[1]}\""
87
- end
88
- end
89
- end
90
-
91
- class IfFormatter < BaseFormatter
92
- def format
93
- start = "#{' ' * @indentation}if "
94
- condition = Formatter.for(sexp[1]).new(@sexp[1], @indentation + 2, self).format
95
- body = Formatter.for(sexp[2]).new(@sexp[2], @indentation + 2, self).format
96
- ending = "#{' ' * @indentation}end"
97
-
98
- "#{start}#{condition}\n#{body}\n#{ending}"
99
- end
100
- end
101
-
102
- class OrFormatter < BaseFormatter
103
- def format
104
- left = Formatter.for(sexp[1]).new(sexp[1], @indentation, self).format
105
- right = Formatter.for(sexp[2]).new(sexp[2], @indentation, self).format
106
-
107
- if needs_parens?
108
- "(#{left} || #{right})"
109
- else
110
- "#{left} || #{right}"
111
- end
112
- end
113
-
114
- private
115
-
116
- def needs_parens?
117
- parent&.type == :or || parent&.type == :and
118
- end
119
- end
120
-
121
- class AndFormatter < BaseFormatter
122
- def format
123
- left = Formatter.for(sexp[1]).new(sexp[1], @indentation, self).format
124
- right = Formatter.for(sexp[2]).new(sexp[2], @indentation, self).format
125
-
126
- if needs_parens?
127
- "(#{left} && #{right})"
128
- else
129
- "#{left} && #{right}"
130
- end
131
- end
132
-
133
- private
134
-
135
- def needs_parens?
136
- parent&.type == :or || parent&.type == :and
137
- end
138
- end
139
-
140
- class CallFormatter < BaseFormatter
141
- def format
142
- if infix?
143
- left = Formatter.for(sexp[1]).new(sexp[1], @indentation, self).format
144
- right = Formatter.for(sexp[3]).new(sexp[3], @indentation, self).format
145
-
146
- if needs_parens?
147
- "(#{left} #{sexp[2]} #{right})"
148
- else
149
- "#{left} #{sexp[2]} #{right}"
150
- end
151
- else
152
- content = Formatter.for(sexp[1]).new(sexp[1], @indentation, self).format
153
- "#{content}.#{sexp[2]}"
154
- end
155
- end
156
-
157
- private
158
-
159
- def infix?
160
- sexp.length == 4
161
- end
162
-
163
- def needs_parens?
164
- parent&.type == :or || parent&.type == :and
165
- end
166
- end
167
-
168
- class ClassFormatter < BaseFormatter
169
- def format
170
- content = Formatter.for(sexp[3]).new(sexp[3], indentation + 2, self).format
171
- "#{indents}class #{sexp[1]}\n#{content}\n#{indents}end" # TODO handle inheritance
172
- end
173
- end
174
-
175
- class CdeclFormatter < BaseFormatter
176
- def format
177
- content = Formatter.for(sexp[2]).new(sexp[2], indentation + 2, self).format
178
- "#{indents}#{sexp[1]} = #{content}"
179
- end
180
- end
181
-
182
- class ArrayFormatter < BaseFormatter
183
- def format
184
- elements = sexp[1..-1].map do |nested_sexp|
185
- Formatter.for(nested_sexp).new(nested_sexp, indentation + 2, self).format
186
- end
187
-
188
- "[#{elements.join(", ")}]"
189
- end
190
- end
191
29
  end
@@ -0,0 +1,20 @@
1
+ module Prettyrb
2
+ class Builder < Parser::Builders::Default
3
+ NODE_TYPES = {
4
+ and: Prettyrb::Nodes::AndNode,
5
+ dstr: Prettyrb::Nodes::DstrNode,
6
+ if: Prettyrb::Nodes::IfNode,
7
+ or: Prettyrb::Nodes::OrNode,
8
+ regexp: Prettyrb::Nodes::RegexpNode,
9
+ send: Prettyrb::Nodes::SendNode,
10
+ str: Prettyrb::Nodes::StrNode,
11
+ def: Prettyrb::Nodes::DefNode,
12
+ }.freeze
13
+
14
+ def n(type, children, source_map)
15
+ node_class = NODE_TYPES.fetch(type, Prettyrb::Nodes::BaseNode)
16
+
17
+ node_class.new(type, children, location: source_map)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ require "thor"
2
+
3
+ module Prettyrb
4
+ class CLI < Thor
5
+ desc "format [FILE]", "Ruby file to prettify"
6
+ def format(file)
7
+ content = File.read(file)
8
+ formatted_content = Prettyrb::Formatter.new(content).format
9
+
10
+ puts formatted_content
11
+ end
12
+
13
+ desc "write [FILES]", "Write prettified Ruby files"
14
+ def write(*files)
15
+ files.each do |file|
16
+ content = File.read(file)
17
+ begin
18
+ formatted_content = Prettyrb::Formatter.new(content).format
19
+ rescue Exception => e
20
+ puts "Failed to write #{file}"
21
+ throw e
22
+ end
23
+
24
+ File.open(file, 'w') do |f|
25
+ f.write(formatted_content)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,164 @@
1
+ module Prettyrb
2
+ module Document
3
+ module DSL
4
+ def concat(*args)
5
+ Document::Concat.new(*args)
6
+ end
7
+
8
+ def join(*args)
9
+ Document::Join.new(*args)
10
+ end
11
+
12
+ def group(*args)
13
+ Document::Group.new(*args)
14
+ end
15
+
16
+ def if_break(*args)
17
+ Document::IfBreak.new(*args)
18
+ end
19
+
20
+ def indent(*args)
21
+ Document::Indent.new(*args)
22
+ end
23
+
24
+ def dedent(*args)
25
+ Document::Dedent.new(*args)
26
+ end
27
+
28
+ def hardline(*args)
29
+ Document::Hardline.new(*args)
30
+ end
31
+
32
+ def softline(*args)
33
+ Document::Softline.new(*args)
34
+ end
35
+ end
36
+
37
+ class Builder
38
+ attr_reader :parts
39
+
40
+ include Enumerable
41
+
42
+ def initialize(*args)
43
+ @parts = args
44
+ end
45
+
46
+ def each
47
+ @parts.each
48
+ end
49
+
50
+ def inspect
51
+ inspect_children(self, indent_level: 0)
52
+ end
53
+
54
+ def has_group_part?
55
+ parts.any? { |p| p.is_a?(Group) }
56
+ end
57
+
58
+ def groups
59
+ parts.select { |p| p.is_a?(Group) } + parts.flat_map do |p|
60
+ p.groups if p.respond_to?(:groups)
61
+ end.compact
62
+ end
63
+
64
+ def max_group_depth
65
+ return 0 if !parts
66
+ return @max_group_depth if defined?(@max_group_depth)
67
+
68
+ has_groups = parts.any? { |p| p.is_a?(Group) }
69
+
70
+ total = if has_groups
71
+ 1
72
+ else
73
+ 0
74
+ end
75
+
76
+ # TODO swap filter/flat_map for single iteration
77
+ nested_total = parts.
78
+ filter { |p| p.respond_to?(:max_group_depth) }.
79
+ flat_map { |p| p.max_group_depth }.
80
+ max
81
+
82
+ @max_group_depth = total + (nested_total || 0)
83
+ end
84
+
85
+ private
86
+
87
+ def inspect_children(builder, indent_level:)
88
+ if builder.respond_to?(:parts)
89
+ children = if builder.parts
90
+ builder.parts.map do |p|
91
+ inspect_children(p, indent_level: indent_level + 1)
92
+ end.join("\n")
93
+ end
94
+
95
+ " " * indent_level + "(#{builder.class}\n#{" "*indent_level}#{children}\n #{" " * indent_level})"
96
+ else
97
+ " " * indent_level + builder.inspect
98
+ end
99
+ end
100
+ end
101
+
102
+ class Concat < Builder
103
+ end
104
+
105
+ class Group < Builder
106
+ attr_reader :joiner
107
+
108
+ def initialize(*args, joiner: "")
109
+ super(*args)
110
+ @joiner = joiner
111
+ end
112
+ end
113
+
114
+ class Join < Builder
115
+ attr_reader :separator
116
+
117
+ def initialize(separator: ",", parts:)
118
+ if parts.is_a?(Array)
119
+ super(*parts)
120
+ else
121
+ super(parts)
122
+ end
123
+ @separator = separator
124
+ end
125
+ end
126
+
127
+ class IfBreak
128
+ attr_reader :without_break, :with_break
129
+ def initialize(without_break:, with_break: )
130
+ @without_break = without_break
131
+ @with_break = with_break
132
+ end
133
+ end
134
+
135
+ class Indent < Builder
136
+ attr_reader :only_when_break
137
+
138
+ def initialize(*args, only_when_break: false)
139
+ @only_when_break = only_when_break
140
+ super(*args)
141
+ end
142
+ end
143
+
144
+ class Dedent < Builder
145
+ end
146
+
147
+ class Hardline < Builder
148
+ attr_reader :count, :skip_indent
149
+ def initialize(count: 1, skip_indent: false)
150
+ @count = count
151
+ @skip_indent = skip_indent
152
+ end
153
+ end
154
+
155
+ class Softline < Builder
156
+ attr_reader :fallback
157
+
158
+ def initialize(*args, fallback: nil)
159
+ super(args)
160
+ @fallback = fallback
161
+ end
162
+ end
163
+ end
164
+ end