1 package frost.io
2
3 ====================================================================================================
4 A `InputStream` which sits on top of another stream, tracking the current line and column as it
5 reads from the underlying stream. Closing the `LineNumberInputStream` *does not* close the
6 underlying stream, but if the `LineNumberInputStream` holds the only remaining reference to the
7 underlying stream, then discarding the `LineNumberInputStream` will cause the underlying stream
8 to likewise be discarded and thus closed.
9
10 You should not read directly from the underlying stream after creating a `LineNumberInputStream` on
11 top of it, as doing so will naturally throw off the `LineNumberInputStream`'s calculations.
12 ====================================================================================================
13 class LineNumberInputStream : InputStream {
14 @private
15 def source:InputStream
16
17 ================================================================================================
18 The current line number (starting at 1).
19 ================================================================================================
20 var line := 1
21
22 ================================================================================================
23 The current column number (starting at 1).
24 ================================================================================================
25 var column := 1
26
27 ================================================================================================
28 The width of a tab character.
29 ================================================================================================
30 var tabSize := 4
31
32 ================================================================================================
33 Creates a new `LineNumberInputStream`.
34
35 @param source the underlying stream
36 ================================================================================================
37 init(source:InputStream) {
38 self.source := source
39 super.init()
40 }
41
42 @override
43 method read():UInt8? {
44 def result:UInt8? := source.read()
45 if result == null {
46 return null
47 }
48 match result {
49 when 10 {
50 line += 1
51 column := 1
52 }
53 when 9 {
54 column += tabSize - column % tabSize
55 }
56 otherwise {
57 column += 1
58 }
59 }
60 return result
61 }
62 }