aboutsummaryrefslogtreecommitdiff
path: root/llvm/include/llvm/Support/FormattedStream.h
blob: 850a18dbb94121758ce9e47749d27432c2acab03 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//===-- llvm/Support/FormattedStream.h - Formatted streams ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains raw_ostream implementations for streams to do
// things like pretty-print comments.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_FORMATTEDSTREAM_H
#define LLVM_SUPPORT_FORMATTEDSTREAM_H

#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>

namespace llvm {

/// formatted_raw_ostream - A raw_ostream that wraps another one and keeps track
/// of line and column position, allowing padding out to specific column
/// boundaries and querying the number of lines written to the stream. This
/// assumes that the contents of the stream is valid UTF-8 encoded text. This
/// doesn't attempt to handle everything Unicode can do (combining characters,
/// right-to-left markers, etc), but should cover the cases likely to appear in
/// source code or diagnostic messages.
class formatted_raw_ostream : public raw_ostream {
  /// TheStream - The real stream we output to. We set it to be
  /// unbuffered, since we're already doing our own buffering.
  ///
  raw_ostream *TheStream;

  /// Position - The current output column and line of the data that's
  /// been flushed and the portion of the buffer that's been
  /// scanned.  The line and column scheme is zero-based.
  ///
  std::pair<unsigned, unsigned> Position;

  /// Scanned - This points to one past the last character in the
  /// buffer we've scanned.
  ///
  const char *Scanned;

  /// PartialUTF8Char - Either empty or a prefix of a UTF-8 code unit sequence
  /// for a Unicode scalar value which should be prepended to the buffer for the
  /// next call to ComputePosition. This is needed when the buffer is flushed
  /// when it ends part-way through the UTF-8 encoding of a Unicode scalar
  /// value, so that we can compute the display width of the character once we
  /// have the rest of it.
  SmallString<4> PartialUTF8Char;

  /// DisableScan - Temporarily disable scanning of output. Used to ignore color
  /// codes.
  bool DisableScan;

  void write_impl(const char *Ptr, size_t Size) override;

  /// current_pos - Return the current position within the stream,
  /// not counting the bytes currently in the buffer.
  uint64_t current_pos() const override {
    // Our current position in the stream is all the contents which have been
    // written to the underlying stream (*not* the current position of the
    // underlying stream).
    return TheStream->tell();
  }

  /// ComputePosition - Examine the given output buffer and figure out the new
  /// position after output. This is safe to call multiple times on the same
  /// buffer, as it records the most recently scanned character and resumes from
  /// there when the buffer has not been flushed.
  void ComputePosition(const char *Ptr, size_t size);

  /// UpdatePosition - scan the characters in [Ptr, Ptr+Size), and update the
  /// line and column numbers. Unlike ComputePosition, this must be called
  /// exactly once on each region of the buffer.
  void UpdatePosition(const char *Ptr, size_t Size);

  void setStream(raw_ostream &Stream) {
    releaseStream();

    TheStream = &Stream;

    // This formatted_raw_ostream inherits from raw_ostream, so it'll do its
    // own buffering, and it doesn't need or want TheStream to do another
    // layer of buffering underneath. Resize the buffer to what TheStream
    // had been using, and tell TheStream not to do its own buffering.
    if (size_t BufferSize = TheStream->GetBufferSize())
      SetBufferSize(BufferSize);
    else
      SetUnbuffered();
    TheStream->SetUnbuffered();

    enable_colors(TheStream->colors_enabled());

    Scanned = nullptr;
  }

  void PreDisableScan() {
    assert(!DisableScan);
    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
    assert(PartialUTF8Char.empty());
    DisableScan = true;
  }

  void PostDisableScan() {
    assert(DisableScan);
    DisableScan = false;
    Scanned = getBufferStart() + GetNumBytesInBuffer();
  }

  struct DisableScanScope {
    formatted_raw_ostream *S;

    DisableScanScope(formatted_raw_ostream *FRO) : S(FRO) {
      S->PreDisableScan();
    }
    ~DisableScanScope() { S->PostDisableScan(); }
  };

public:
  /// formatted_raw_ostream - Open the specified file for
  /// writing. If an error occurs, information about the error is
  /// put into ErrorInfo, and the stream should be immediately
  /// destroyed; the string will be empty if no error occurred.
  ///
  /// As a side effect, the given Stream is set to be Unbuffered.
  /// This is because formatted_raw_ostream does its own buffering,
  /// so it doesn't want another layer of buffering to be happening
  /// underneath it.
  ///
  formatted_raw_ostream(raw_ostream &Stream)
      : TheStream(nullptr), Position(0, 0), DisableScan(false) {
    setStream(Stream);
  }
  explicit formatted_raw_ostream()
      : TheStream(nullptr), Position(0, 0), Scanned(nullptr),
        DisableScan(false) {}

  ~formatted_raw_ostream() override {
    flush();
    releaseStream();
  }

  /// PadToColumn - Align the output to some column number.  If the current
  /// column is already equal to or more than NewCol, PadToColumn inserts one
  /// space.
  ///
  /// \param NewCol - The column to move to.
  formatted_raw_ostream &PadToColumn(unsigned NewCol);

  unsigned getColumn() {
    // Calculate current position, taking buffer contents into account.
    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
    return Position.first;
  }

  unsigned getLine() {
    // Calculate current position, taking buffer contents into account.
    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
    return Position.second;
  }

  raw_ostream &resetColor() override {
    if (colors_enabled()) {
      DisableScanScope S(this);
      raw_ostream::resetColor();
    }
    return *this;
  }

  raw_ostream &reverseColor() override {
    if (colors_enabled()) {
      DisableScanScope S(this);
      raw_ostream::reverseColor();
    }
    return *this;
  }

  raw_ostream &changeColor(enum Colors Color, bool Bold, bool BG) override {
    if (colors_enabled()) {
      DisableScanScope S(this);
      raw_ostream::changeColor(Color, Bold, BG);
    }
    return *this;
  }

  bool is_displayed() const override {
    return TheStream->is_displayed();
  }

private:
  void releaseStream() {
    // Transfer the buffer settings from this raw_ostream back to the underlying
    // stream.
    if (!TheStream)
      return;
    if (size_t BufferSize = GetBufferSize())
      TheStream->SetBufferSize(BufferSize);
    else
      TheStream->SetUnbuffered();
  }
};

/// fouts() - This returns a reference to a formatted_raw_ostream for
/// standard output.  Use it like: fouts() << "foo" << "bar";
formatted_raw_ostream &fouts();

/// ferrs() - This returns a reference to a formatted_raw_ostream for
/// standard error.  Use it like: ferrs() << "foo" << "bar";
formatted_raw_ostream &ferrs();

/// fdbgs() - This returns a reference to a formatted_raw_ostream for
/// debug output.  Use it like: fdbgs() << "foo" << "bar";
formatted_raw_ostream &fdbgs();

} // end llvm namespace


#endif