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 }