1  package frost.io
  2  
  3  uses frost.unsafe.Pointer
  4  
  5  ====================================================================================================
  6  An output stream which indents the lines it writes. `IndentedOutputStream` sits "on top of" another
  7  stream, directing its output to the underlying stream. Closing the `IndentedOutputStream` *does not*
  8  directly close the underlying stream, but if the `IndentedOutputStream` holds the only remaining
  9  reference to the underlying stream, then discarding the `IndentedOutputStream` will cause the
 10  underlying stream to likewise be discarded and thus closed.
 11  
 12  It is possible to continue writing directly to the underlying stream despite having created an
 13  `IndentedOutputStream` on top of it; naturally, such writes will not be processed by the
 14  `IndentedOutputStream` and thus may interact badly with it.
 15  ====================================================================================================
 16  class IndentedOutputStream : OutputStream {
 17      ================================================================================================
 18      The current indentation level, in units of `indentSize`. Each line written to the underlying
 19      stream will be prefixed by `indentSize * level` spaces.
 20      ================================================================================================
 21      var level := 0 -- FIXME make these properties and store the indentation string
 22  
 23      ================================================================================================
 24      The number of spaces represented by each increment of `level`. Defaults to `4`.
 25      ================================================================================================
 26      var indentSize := 4
 27  
 28      @private
 29      var indent := ""
 30  
 31      @private
 32      var atLineStart := true
 33  
 34      @private
 35      def out:OutputStream
 36  
 37      ================================================================================================
 38      Creates a new `IndentedOutputStream`.
 39  
 40      @param out the underlying output stream
 41      ================================================================================================
 42      init(out:OutputStream) {
 43          self.out := out
 44          super.init()
 45      }
 46  
 47      @override
 48      method write(b:UInt8):Error? {
 49          try {
 50              if b = 10 {
 51                  atLineStart := true
 52              }
 53              else {
 54                  indentIfNeeded()
 55              }
 56              out.write(b)
 57              return null
 58          }
 59          fail(error) {
 60              return error
 61          }
 62      }
 63  
 64      @override
 65      method write(ptr:Pointer<UInt8>, count:Int):Error? {
 66          try {
 67              var start := 0
 68              var current := start
 69              while current < count {< count {
 70                  if ptr[current] = 10 {
 71                      out.write(ptr + start, current - start)
 72                      start := current
 73                      atLineStart := true
 74                  }
 75                  else if atLineStart {
 76                      out.write(ptr + start, current - start)
 77                      start := current
 78                      indentIfNeeded()
 79                  }
 80                  current += 1
 81              }
 82              out.write(ptr + start, current - start)
 83              return null
 84          }
 85          fail(error) {
 86              return error
 87          }
 88      }
 89      
 90      @private
 91      method indentIfNeeded():Error? {
 92          if atLineStart {
 93              atLineStart := false
 94              if indent.byteLength != indentSize * level {
 95                  indent := " " * (indentSize * level)
 96              }
 97              return print(indent)
 98          }
 99          return null
100      }
101  }