prettier_print 0.1.0 → 1.0.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: 907080c33747b11f43d7165ca49678637e042a6f72e49af0f07ada0c8a41326f
4
- data.tar.gz: fce6553a15d281741ba58aa27572824457ff74546c5bb4d5ef0e5ba6a4dc67c8
3
+ metadata.gz: 9996ce5492af60b6cea9099175af9c31752da258be9de73b9f8e66d55ec5cecb
4
+ data.tar.gz: 472a728da7f9c4c8fba5fe93664e50a984cb80ecec14efedfcd94decc72c7593
5
5
  SHA512:
6
- metadata.gz: afadb2cc911c46b55a2abf07818481fc41472f050ac18bfb6a08016a4586222832d937a816a47b1ce313dbfbfe9c1577cf37b646ebbc6dbff4b2a4f9b57c78e1
7
- data.tar.gz: 31076563a9afa16bd4ba137c13ac27eaa3e45cb392923d02a9097260b945bb133829e02258284210ac1661ebb87fa5db0cd2f7803197e40780d116202e42ce41
6
+ metadata.gz: 57bb92ec550e11a69502c3f9e7668e0f4c6528025a3a0e45986c4f32f755e43d47342128d1c75fde952853ffe5eb4dddd4ff9f235ab1d30bef66a212f41f9f86
7
+ data.tar.gz: eb6ad1645c41cc33ada93ea8570c626968cc09f1f374d11f5bc5c526d375febb43629b09cdbf15e941e8f2d6ec27294e7c8755da4c6e2e94594094b15de23572
data/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.0] - 2022-10-17
10
+
11
+ ### Added
12
+
13
+ * `breakable_space` - a shortcut for `breakable`
14
+ * `breakable_empty` - a shortcut for `breakable("")`
15
+ * `breakable_force` - a shortcut for `breakable("", force: true)`
16
+ * `breakable_return` - a shortcut for `breakable(" ", indent: false, force: true)`
17
+ * Strings can now be added directly to the output buffer, which means they don't have to be placed into the `Text` node. This cuts down on quite a bit of allocations.
18
+
19
+ ### Changed
20
+
21
+ * `trim` now strips its whitespace using `rstrip!` instead of a custom `gsub!`. This means that other forms of whitespace beyond tabs and spaces are included. This shouldn't really impact anyone unless they're using vertical tab or something in combination with `trim` and wanted them to stay in.
22
+
23
+ ### Removed
24
+
25
+ * There is no longer a `PrettierPrint::DefaultBuffer` class. Since there were only ever two implementations, those implementations now no longer share a parent.
26
+ * `PrettierPrint::IndentLevel` is now entirely gone. This was mostly an implementation detail, and no one should have been relying on it anyway. However, it means that the ability to use nest with a string literal is now gone as well. It can be created again by using seplist though, so the functionality just isn't there in the shortcut version. This means we're able to keep track of indentation as a single integer again, which drastically simplifies the code.
27
+
9
28
  ## [0.1.0] - 2022-05-13
10
29
 
11
30
  ### Added
data/Gemfile.lock CHANGED
@@ -1,17 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prettier_print (0.1.0)
4
+ prettier_print (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  power_assert (2.0.1)
10
10
  rake (13.0.6)
11
- test-unit (3.5.3)
11
+ test-unit (3.5.5)
12
12
  power_assert
13
13
 
14
14
  PLATFORMS
15
+ arm64-darwin-21
15
16
  x86_64-darwin-21
16
17
  x86_64-linux
17
18
 
data/README.md CHANGED
@@ -137,11 +137,7 @@ This node increases the indentation by a fixed number of spaces or a string. It
137
137
  q.nest(2) {}
138
138
  ```
139
139
 
140
- It accepts a block that specifies the contents of the alignment node. The value that you're indenting by can be positive or negative. It can also be a string, in which case that value will be used at the beginning of each line, as in:
141
-
142
- ```ruby
143
- q.nest("-->") {}
144
- ```
140
+ It accepts a block that specifies the contents of the alignment node. The value that you're indenting by can be positive or negative.
145
141
 
146
142
  #### `BreakParent`
147
143
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class PrettierPrint
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -120,6 +120,13 @@ class PrettierPrint
120
120
  end
121
121
  end
122
122
 
123
+ # Below here are the most common combination of options that are created when
124
+ # creating new breakables. They are here to cut down on some allocations.
125
+ BREAKABLE_SPACE = Breakable.new(" ", 1, indent: true, force: false)
126
+ BREAKABLE_EMPTY = Breakable.new("", 0, indent: true, force: false)
127
+ BREAKABLE_FORCE = Breakable.new(" ", 1, indent: true, force: true)
128
+ BREAKABLE_RETURN = Breakable.new(" ", 1, indent: false, force: true)
129
+
123
130
  # A node in the print tree that forces the surrounding group to print out in
124
131
  # the "break" mode as opposed to the "flat" mode. Useful for when you need to
125
132
  # force a newline into a group.
@@ -129,6 +136,10 @@ class PrettierPrint
129
136
  end
130
137
  end
131
138
 
139
+ # Since there's really no difference in these instances, just using the same
140
+ # one saves on some allocations.
141
+ BREAK_PARENT = BreakParent.new
142
+
132
143
  # A node in the print tree that represents a group of items which the printer
133
144
  # should try to fit onto one line. This is the basic command to tell the
134
145
  # printer when to break. Groups are usually nested, and the printer will try
@@ -254,19 +265,23 @@ class PrettierPrint
254
265
  end
255
266
  end
256
267
 
268
+ # Since all of the instances here are the same, we can reuse the same one to
269
+ # cut down on allocations.
270
+ TRIM = Trim.new
271
+
257
272
  # When building up the contents in the output buffer, it's convenient to be
258
273
  # able to trim trailing whitespace before newlines. If the output object is a
259
274
  # string or array or strings, then we can do this with some gsub calls. If
260
275
  # not, then this effectively just wraps the output object and forwards on
261
276
  # calls to <<.
262
277
  module Buffer
263
- # This is the default output buffer that provides a base implementation of
264
- # trim! that does nothing. It's effectively a wrapper around whatever output
265
- # object was given to the format command.
266
- class DefaultBuffer
278
+ # This is an output buffer that wraps a string output object. It provides a
279
+ # trim! method that trims off trailing whitespace from the string using
280
+ # gsub!.
281
+ class StringBuffer
267
282
  attr_reader :output
268
283
 
269
- def initialize(output = [])
284
+ def initialize(output = "".dup)
270
285
  @output = output
271
286
  end
272
287
 
@@ -274,22 +289,9 @@ class PrettierPrint
274
289
  @output << object
275
290
  end
276
291
 
277
- def trim!
278
- 0
279
- end
280
- end
281
-
282
- # This is an output buffer that wraps a string output object. It provides a
283
- # trim! method that trims off trailing whitespace from the string using
284
- # gsub!.
285
- class StringBuffer < DefaultBuffer
286
- def initialize(output = "".dup)
287
- super(output)
288
- end
289
-
290
292
  def trim!
291
293
  length = output.length
292
- output.gsub!(/[\t ]*\z/, "")
294
+ output.rstrip!
293
295
  length - output.length
294
296
  end
295
297
  end
@@ -298,9 +300,15 @@ class PrettierPrint
298
300
  # trim! method that trims off trailing whitespace from the last element in
299
301
  # the array if it's an unfrozen string using the same method as the
300
302
  # StringBuffer.
301
- class ArrayBuffer < DefaultBuffer
303
+ class ArrayBuffer
304
+ attr_reader :output
305
+
302
306
  def initialize(output = [])
303
- super(output)
307
+ @output = output
308
+ end
309
+
310
+ def <<(object)
311
+ @output << object
304
312
  end
305
313
 
306
314
  def trim!
@@ -315,7 +323,7 @@ class PrettierPrint
315
323
 
316
324
  if output.any? && output.last.is_a?(String) && !output.last.frozen?
317
325
  length = output.last.length
318
- output.last.gsub!(/[\t ]*\z/, "")
326
+ output.last.rstrip!
319
327
  trimmed += length - output.last.length
320
328
  end
321
329
 
@@ -326,14 +334,7 @@ class PrettierPrint
326
334
  # This is a switch for building the correct output buffer wrapper class for
327
335
  # the given output object.
328
336
  def self.for(output)
329
- case output
330
- when String
331
- StringBuffer.new(output)
332
- when Array
333
- ArrayBuffer.new(output)
334
- else
335
- DefaultBuffer.new(output)
336
- end
337
+ output.is_a?(String) ? StringBuffer.new(output) : ArrayBuffer.new(output)
337
338
  end
338
339
  end
339
340
 
@@ -482,88 +483,6 @@ class PrettierPrint
482
483
  end
483
484
  end
484
485
 
485
- # This object represents the current level of indentation within the printer.
486
- # It has the ability to generate new levels of indentation through the #align
487
- # and #indent methods.
488
- class IndentLevel
489
- IndentPart = Object.new
490
- DedentPart = Object.new
491
-
492
- StringAlignPart = Struct.new(:n)
493
- NumberAlignPart = Struct.new(:n)
494
-
495
- attr_reader :genspace, :value, :length, :queue, :root
496
-
497
- def initialize(
498
- genspace:,
499
- value: genspace.call(0),
500
- length: 0,
501
- queue: [],
502
- root: nil
503
- )
504
- @genspace = genspace
505
- @value = value
506
- @length = length
507
- @queue = queue
508
- @root = root
509
- end
510
-
511
- # This can accept a whole lot of different kinds of objects, due to the
512
- # nature of the flexibility of the Align node.
513
- def align(n)
514
- case n
515
- when NilClass
516
- self
517
- when String
518
- indent(StringAlignPart.new(n))
519
- else
520
- indent(n < 0 ? DedentPart : NumberAlignPart.new(n))
521
- end
522
- end
523
-
524
- def indent(part = IndentPart)
525
- next_value = genspace.call(0)
526
- next_length = 0
527
- next_queue = (part == DedentPart ? queue[0...-1] : [*queue, part])
528
-
529
- last_spaces = 0
530
-
531
- add_spaces = ->(count) do
532
- next_value << genspace.call(count)
533
- next_length += count
534
- end
535
-
536
- flush_spaces = -> do
537
- add_spaces[last_spaces] if last_spaces > 0
538
- last_spaces = 0
539
- end
540
-
541
- next_queue.each do |next_part|
542
- case next_part
543
- when IndentPart
544
- flush_spaces.call
545
- add_spaces.call(2)
546
- when StringAlignPart
547
- flush_spaces.call
548
- next_value += next_part.n
549
- next_length += next_part.n.length
550
- when NumberAlignPart
551
- last_spaces += next_part.n
552
- end
553
- end
554
-
555
- flush_spaces.call
556
-
557
- IndentLevel.new(
558
- genspace: genspace,
559
- value: next_value,
560
- length: next_length,
561
- queue: next_queue,
562
- root: root
563
- )
564
- end
565
- end
566
-
567
486
  # When printing, you can optionally specify the value that should be used
568
487
  # whenever a group needs to be broken onto multiple lines. In this case the
569
488
  # default is \n.
@@ -734,7 +653,7 @@ class PrettierPrint
734
653
 
735
654
  # This is our command stack. A command consists of a triplet of an
736
655
  # indentation level, the mode (break or flat), and a doc node.
737
- commands = [[IndentLevel.new(genspace: genspace), MODE_BREAK, doc]]
656
+ commands = [[0, MODE_BREAK, doc]]
738
657
 
739
658
  # This is a small optimization boolean. It keeps track of whether or not
740
659
  # when we hit a group node we should check if it fits on the same line.
@@ -750,49 +669,35 @@ class PrettierPrint
750
669
  # priority set on the line suffix and the index it was in the original
751
670
  # array.
752
671
  line_suffix_sort = ->(line_suffix) do
753
- [-line_suffix.last, -line_suffixes.index(line_suffix)]
672
+ [-line_suffix.last.priority, -line_suffixes.index(line_suffix)]
754
673
  end
755
674
 
756
675
  # This is a linear stack instead of a mutually recursive call defined on
757
676
  # the individual doc nodes for efficiency.
758
677
  while (indent, mode, doc = commands.pop)
759
678
  case doc
760
- when Text
761
- doc.objects.each { |object| buffer << object }
762
- position += doc.width
763
- when Array
764
- doc.reverse_each { |part| commands << [indent, mode, part] }
765
- when Indent
766
- commands << [indent.indent, mode, doc.contents]
767
- when Align
768
- commands << [indent.align(doc.indent), mode, doc.contents]
769
- when Trim
770
- position -= buffer.trim!
679
+ when String
680
+ buffer << doc
681
+ position += doc.length
771
682
  when Group
772
683
  if mode == MODE_FLAT && !should_remeasure
773
- commands << [
774
- indent,
775
- doc.break? ? MODE_BREAK : MODE_FLAT,
776
- doc.contents
777
- ]
684
+ next_mode = doc.break? ? MODE_BREAK : MODE_FLAT
685
+ commands += doc.contents.reverse.map { |part| [indent, next_mode, part] }
778
686
  else
779
687
  should_remeasure = false
780
- next_cmd = [indent, MODE_FLAT, doc.contents]
781
- commands << if !doc.break? &&
782
- fits?(next_cmd, commands, maxwidth - position)
783
- next_cmd
688
+
689
+ if doc.break?
690
+ commands += doc.contents.reverse.map { |part| [indent, MODE_BREAK, part] }
784
691
  else
785
- [indent, MODE_BREAK, doc.contents]
692
+ next_commands = doc.contents.reverse.map { |part| [indent, MODE_FLAT, part] }
693
+
694
+ if fits?(next_commands, commands, maxwidth - position)
695
+ commands += next_commands
696
+ else
697
+ commands += next_commands.map { |command| command[1] = MODE_BREAK; command }
698
+ end
786
699
  end
787
700
  end
788
- when IfBreak
789
- if mode == MODE_BREAK && doc.break_contents.any?
790
- commands << [indent, mode, doc.break_contents]
791
- elsif mode == MODE_FLAT && doc.flat_contents.any?
792
- commands << [indent, mode, doc.flat_contents]
793
- end
794
- when LineSuffix
795
- line_suffixes << [indent, mode, doc.contents, doc.priority]
796
701
  when Breakable
797
702
  if mode == MODE_FLAT
798
703
  if doc.force?
@@ -813,28 +718,45 @@ class PrettierPrint
813
718
  # to flush them now, as we are about to add a newline.
814
719
  if line_suffixes.any?
815
720
  commands << [indent, mode, doc]
816
- commands += line_suffixes.sort_by(&line_suffix_sort)
817
- line_suffixes = []
721
+
722
+ line_suffixes.sort_by(&line_suffix_sort).each do |(indent, mode, doc)|
723
+ commands += doc.contents.reverse.map { |part| [indent, mode, part] }
724
+ end
725
+
726
+ line_suffixes.clear
818
727
  next
819
728
  end
820
729
 
821
730
  if !doc.indent?
822
731
  buffer << newline
823
-
824
- if indent.root
825
- buffer << indent.root.value
826
- position = indent.root.length
827
- else
828
- position = 0
829
- end
732
+ position = 0
830
733
  else
831
734
  position -= buffer.trim!
832
735
  buffer << newline
833
- buffer << indent.value
834
- position = indent.length
736
+ buffer << genspace.call(indent)
737
+ position = indent
738
+ end
739
+ when Indent
740
+ next_indent = indent + 2
741
+ commands += doc.contents.reverse.map { |part| [next_indent, mode, part] }
742
+ when Align
743
+ next_indent = indent + doc.indent
744
+ commands += doc.contents.reverse.map { |part| [next_indent, mode, part] }
745
+ when Trim
746
+ position -= buffer.trim!
747
+ when IfBreak
748
+ if mode == MODE_BREAK && doc.break_contents.any?
749
+ commands += doc.break_contents.reverse.map { |part| [indent, mode, part] }
750
+ elsif mode == MODE_FLAT && doc.flat_contents.any?
751
+ commands += doc.flat_contents.reverse.map { |part| [indent, mode, part] }
835
752
  end
753
+ when LineSuffix
754
+ line_suffixes << [indent, mode, doc]
836
755
  when BreakParent
837
756
  # do nothing
757
+ when Text
758
+ doc.objects.each { |object| buffer << object }
759
+ position += doc.width
838
760
  else
839
761
  # Special case where the user has defined some way to get an extra doc
840
762
  # node that we don't explicitly support into the list. In this case
@@ -861,13 +783,41 @@ class PrettierPrint
861
783
  # Helper node builders
862
784
  # ----------------------------------------------------------------------------
863
785
 
786
+ # The vast majority of breakable calls you receive while formatting are a
787
+ # space in flat mode and a newline in break mode. Since this is so common,
788
+ # we have a method here to skip past unnecessary calculation.
789
+ def breakable_space
790
+ target << BREAKABLE_SPACE
791
+ end
792
+
793
+ # Another very common breakable call you receive while formatting is an
794
+ # empty string in flat mode and a newline in break mode. Similar to
795
+ # breakable_space, this is here for avoid unnecessary calculation.
796
+ def breakable_empty
797
+ target << BREAKABLE_EMPTY
798
+ end
799
+
800
+ # The final of the very common breakable calls you receive while formatting
801
+ # is the normal breakable space but with the addition of the break_parent.
802
+ def breakable_force
803
+ target << BREAKABLE_FORCE
804
+ break_parent
805
+ end
806
+
807
+ # This is the same shortcut as breakable_force, except that it doesn't indent
808
+ # the next line. This is necessary if you're trying to preserve some custom
809
+ # formatting like a multi-line string.
810
+ def breakable_return
811
+ target << BREAKABLE_RETURN
812
+ end
813
+
864
814
  # A convenience method which is same as follows:
865
815
  #
866
816
  # text(",")
867
817
  # breakable
868
818
  def comma_breakable
869
819
  text(",")
870
- breakable
820
+ breakable_space
871
821
  end
872
822
 
873
823
  # This is similar to #breakable except the decision to break or not is
@@ -896,18 +846,18 @@ class PrettierPrint
896
846
  queue = [node]
897
847
  width = 0
898
848
 
899
- until queue.empty?
900
- doc = queue.shift
901
-
849
+ while (doc = queue.shift)
902
850
  case doc
903
- when Text
904
- width += doc.width
905
- when Indent, Align, Group
851
+ when String
852
+ width += doc.length
853
+ when Group, Indent, Align
906
854
  queue = doc.contents + queue
907
- when IfBreak
908
- queue = doc.break_contents + queue
909
855
  when Breakable
910
856
  width = 0
857
+ when IfBreak
858
+ queue = doc.break_contents + queue
859
+ when Text
860
+ width += doc.width
911
861
  end
912
862
  end
913
863
 
@@ -918,26 +868,16 @@ class PrettierPrint
918
868
  # no newlines are present in the output. If a newline is being forced into
919
869
  # the output, the replace value will be used.
920
870
  def remove_breaks(node, replace = "; ")
921
- marker = Object.new
922
- stack = [node]
923
-
924
- while stack.any?
925
- doc = stack.pop
926
-
927
- if doc == marker
928
- stack.pop
929
- next
930
- end
931
-
932
- stack += [doc, marker]
871
+ queue = [node]
933
872
 
873
+ while (doc = queue.shift)
934
874
  case doc
935
875
  when Align, Indent, Group
936
876
  doc.contents.map! { |child| remove_breaks_with(child, replace) }
937
- stack += doc.contents.reverse
877
+ queue += doc.contents
938
878
  when IfBreak
939
879
  doc.flat_contents.map! { |child| remove_breaks_with(child, replace) }
940
- stack += doc.flat_contents.reverse
880
+ queue += doc.flat_contents
941
881
  end
942
882
  end
943
883
  end
@@ -967,13 +907,14 @@ class PrettierPrint
967
907
  # q.comma_breakable
968
908
  # xxx 3
969
909
  def seplist(list, sep=nil, iter_method=:each) # :yield: element
970
- sep ||= lambda { comma_breakable }
971
910
  first = true
972
911
  list.__send__(iter_method) {|*v|
973
912
  if first
974
913
  first = false
975
- else
914
+ elsif sep
976
915
  sep.call
916
+ else
917
+ comma_breakable
977
918
  end
978
919
  RUBY_VERSION >= "3.0" ? yield(*v, **{}) : yield(*v)
979
920
  }
@@ -1013,26 +954,20 @@ class PrettierPrint
1013
954
  indent: true,
1014
955
  force: false
1015
956
  )
1016
- doc = Breakable.new(separator, width, indent: indent, force: !!force)
1017
-
1018
- target << doc
957
+ target << Breakable.new(separator, width, indent: indent, force: !!force)
1019
958
  break_parent if force == true
1020
-
1021
- doc
1022
959
  end
1023
960
 
1024
961
  # This inserts a BreakParent node into the print tree which forces the
1025
962
  # surrounding and all parent group nodes to break.
1026
963
  def break_parent
1027
- doc = BreakParent.new
964
+ doc = BREAK_PARENT
1028
965
  target << doc
1029
966
 
1030
967
  groups.reverse_each do |group|
1031
968
  break if group.break?
1032
969
  group.break
1033
970
  end
1034
-
1035
- doc
1036
971
  end
1037
972
 
1038
973
  # This inserts a Trim node into the print tree which, when printed, will clear
@@ -1040,10 +975,7 @@ class PrettierPrint
1040
975
  # case where you need to delete printed indentation and force the next node
1041
976
  # to start at the beginning of the line.
1042
977
  def trim
1043
- doc = Trim.new
1044
- target << doc
1045
-
1046
- doc
978
+ target << TRIM
1047
979
  end
1048
980
 
1049
981
  # ----------------------------------------------------------------------------
@@ -1089,15 +1021,36 @@ class PrettierPrint
1089
1021
  # A small DSL-like object used for specifying the alternative contents to be
1090
1022
  # printed if the surrounding group doesn't break for an IfBreak node.
1091
1023
  class IfBreakBuilder
1092
- attr_reader :builder, :if_break
1024
+ attr_reader :q, :flat_contents
1093
1025
 
1094
- def initialize(builder, if_break)
1095
- @builder = builder
1096
- @if_break = if_break
1026
+ def initialize(q, flat_contents)
1027
+ @q = q
1028
+ @flat_contents = flat_contents
1097
1029
  end
1098
1030
 
1099
- def if_flat(&block)
1100
- builder.with_target(if_break.flat_contents, &block)
1031
+ def if_flat
1032
+ q.with_target(flat_contents) { yield }
1033
+ end
1034
+ end
1035
+
1036
+ # When we already know that groups are broken, we don't actually need to track
1037
+ # the flat versions of the contents. So this builder version is effectively a
1038
+ # no-op, but we need it to maintain the same API. The only thing this can
1039
+ # impact is that if there's a forced break in the flat contents, then we need
1040
+ # to propagate that break up the whole tree.
1041
+ class IfFlatIgnore
1042
+ attr_reader :q
1043
+
1044
+ def initialize(q)
1045
+ @q = q
1046
+ end
1047
+
1048
+ def if_flat
1049
+ contents = []
1050
+ group = Group.new(0, contents: contents)
1051
+
1052
+ q.with_target(contents) { yield }
1053
+ q.break_parent if group.break?
1101
1054
  end
1102
1055
  end
1103
1056
 
@@ -1111,31 +1064,50 @@ class PrettierPrint
1111
1064
  # In the example above, if the surrounding group is broken it will print 'do'
1112
1065
  # and if it is not it will print '{'.
1113
1066
  def if_break
1114
- doc = IfBreak.new
1067
+ break_contents = []
1068
+ flat_contents = []
1069
+
1070
+ doc = IfBreak.new(break_contents: break_contents, flat_contents: flat_contents)
1115
1071
  target << doc
1116
1072
 
1117
- with_target(doc.break_contents) { yield }
1118
- IfBreakBuilder.new(self, doc)
1073
+ with_target(break_contents) { yield }
1074
+
1075
+ if groups.last.break?
1076
+ IfFlatIgnore.new(self)
1077
+ else
1078
+ IfBreakBuilder.new(self, flat_contents)
1079
+ end
1119
1080
  end
1120
1081
 
1121
1082
  # This is similar to if_break in that it also inserts an IfBreak node into the
1122
1083
  # print tree, however it's starting from the flat contents, and cannot be used
1123
1084
  # to build the break contents.
1124
1085
  def if_flat
1125
- doc = IfBreak.new
1126
- target << doc
1086
+ if groups.last.break?
1087
+ contents = []
1088
+ group = Group.new(0, contents: contents)
1127
1089
 
1128
- with_target(doc.flat_contents) { yield }
1090
+ with_target(contents) { yield }
1091
+ break_parent if group.break?
1092
+ else
1093
+ flat_contents = []
1094
+ doc = IfBreak.new(break_contents: [], flat_contents: flat_contents)
1095
+ target << doc
1096
+
1097
+ with_target(flat_contents) { yield }
1098
+ doc
1099
+ end
1129
1100
  end
1130
1101
 
1131
1102
  # Very similar to the #nest method, this indents the nested content by one
1132
1103
  # level by inserting an Indent node into the print tree. The contents of the
1133
1104
  # node are determined by the block.
1134
1105
  def indent
1135
- doc = Indent.new
1106
+ contents = []
1107
+ doc = Indent.new(contents: contents)
1136
1108
  target << doc
1137
1109
 
1138
- with_target(doc.contents) { yield }
1110
+ with_target(contents) { yield }
1139
1111
  doc
1140
1112
  end
1141
1113
 
@@ -1152,10 +1124,11 @@ class PrettierPrint
1152
1124
  # Increases left margin after newline with +indent+ for line breaks added in
1153
1125
  # the block.
1154
1126
  def nest(indent)
1155
- doc = Align.new(indent: indent)
1127
+ contents = []
1128
+ doc = Align.new(indent: indent, contents: contents)
1156
1129
  target << doc
1157
1130
 
1158
- with_target(doc.contents) { yield }
1131
+ with_target(contents) { yield }
1159
1132
  doc
1160
1133
  end
1161
1134
 
@@ -1192,7 +1165,7 @@ class PrettierPrint
1192
1165
  # fit onto the remaining space on the current line. If we finish printing
1193
1166
  # all of the commands or if we hit a newline, then we return true. Otherwise
1194
1167
  # if we continue printing past the remaining space, we return false.
1195
- def fits?(next_command, rest_commands, remaining)
1168
+ def fits?(next_commands, rest_commands, remaining)
1196
1169
  # This is the index in the remaining commands that we've handled so far.
1197
1170
  # We reverse through the commands and add them to the stack if we've run
1198
1171
  # out of nodes to handle.
@@ -1200,7 +1173,7 @@ class PrettierPrint
1200
1173
 
1201
1174
  # This is our stack of commands, very similar to the commands list in the
1202
1175
  # print method.
1203
- commands = [next_command]
1176
+ commands = [*next_commands]
1204
1177
 
1205
1178
  # This is our output buffer, really only necessary to keep track of
1206
1179
  # because we could encounter a Trim doc node that would actually add
@@ -1219,25 +1192,12 @@ class PrettierPrint
1219
1192
  indent, mode, doc = commands.pop
1220
1193
 
1221
1194
  case doc
1222
- when Text
1223
- doc.objects.each { |object| fit_buffer << object }
1224
- remaining -= doc.width
1225
- when Array
1226
- doc.reverse_each { |part| commands << [indent, mode, part] }
1227
- when Indent
1228
- commands << [indent.indent, mode, doc.contents]
1229
- when Align
1230
- commands << [indent.align(doc.indent), mode, doc.contents]
1231
- when Trim
1232
- remaining += fit_buffer.trim!
1195
+ when String
1196
+ fit_buffer << doc
1197
+ remaining -= doc.length
1233
1198
  when Group
1234
- commands << [indent, doc.break? ? MODE_BREAK : mode, doc.contents]
1235
- when IfBreak
1236
- if mode == MODE_BREAK && doc.break_contents.any?
1237
- commands << [indent, mode, doc.break_contents]
1238
- elsif mode == MODE_FLAT && doc.flat_contents.any?
1239
- commands << [indent, mode, doc.flat_contents]
1240
- end
1199
+ next_mode = doc.break? ? MODE_BREAK : mode
1200
+ commands += doc.contents.reverse.map { |part| [indent, next_mode, part] }
1241
1201
  when Breakable
1242
1202
  if mode == MODE_FLAT && !doc.force?
1243
1203
  fit_buffer << doc.separator
@@ -1246,6 +1206,23 @@ class PrettierPrint
1246
1206
  end
1247
1207
 
1248
1208
  return true
1209
+ when Indent
1210
+ next_indent = indent + 2
1211
+ commands += doc.contents.reverse.map { |part| [next_indent, mode, part] }
1212
+ when Align
1213
+ next_indent = indent + doc.indent
1214
+ commands += doc.contents.reverse.map { |part| [next_indent, mode, part] }
1215
+ when Trim
1216
+ remaining += fit_buffer.trim!
1217
+ when IfBreak
1218
+ if mode == MODE_BREAK && doc.break_contents.any?
1219
+ commands += doc.break_contents.reverse.map { |part| [indent, mode, part] }
1220
+ elsif mode == MODE_FLAT && doc.flat_contents.any?
1221
+ commands += doc.flat_contents.reverse.map { |part| [indent, mode, part] }
1222
+ end
1223
+ when Text
1224
+ doc.objects.each { |object| fit_buffer << object }
1225
+ remaining -= doc.width
1249
1226
  end
1250
1227
  end
1251
1228
 
@@ -1255,8 +1232,9 @@ class PrettierPrint
1255
1232
  # Resets the group stack and target array so that this pretty printer object
1256
1233
  # can continue to be used before calling flush again if desired.
1257
1234
  def reset
1258
- @groups = [Group.new(0)]
1259
- @target = @groups.last.contents
1235
+ contents = []
1236
+ @groups = [Group.new(0, contents: contents)]
1237
+ @target = contents
1260
1238
  end
1261
1239
 
1262
1240
  def remove_breaks_with(doc, replace)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prettier_print
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-14 00:00:00.000000000 Z
11
+ date: 2022-10-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -50,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
50
  - !ruby/object:Gem::Version
51
51
  version: '0'
52
52
  requirements: []
53
- rubygems_version: 3.4.0.dev
53
+ rubygems_version: 3.3.21
54
54
  signing_key:
55
55
  specification_version: 4
56
56
  summary: A drop-in replacement for the prettyprint gem with more functionality.