prettyrb 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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