//===- IndentedOstream.h - raw ostream wrapper to indent --------*- 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 // //===----------------------------------------------------------------------===// // // raw_ostream subclass that keeps track of indentation for textual output // where indentation helps readability. // //===----------------------------------------------------------------------===// #ifndef MLIR_SUPPORT_INDENTEDOSTREAM_H_ #define MLIR_SUPPORT_INDENTEDOSTREAM_H_ #include "mlir/Support/LLVM.h" #include "llvm/Support/raw_ostream.h" namespace mlir { /// raw_ostream subclass that simplifies indention a sequence of code. class raw_indented_ostream : public raw_ostream { public: explicit raw_indented_ostream(llvm::raw_ostream &os) : os(os) { SetUnbuffered(); } /// Simple RAII struct to use to indentation around entering/exiting region. struct DelimitedScope { explicit DelimitedScope(raw_indented_ostream &os, StringRef open = "", StringRef close = "", bool indent = true) : os(os), open(open), close(close), indent(indent) { os << open; if (indent) os.indent(); } ~DelimitedScope() { if (indent) os.unindent(); os << close; } raw_indented_ostream &os; private: StringRef open, close; bool indent; }; /// Returns the underlying (unindented) raw_ostream. raw_ostream &getOStream() const { return os; } /// Returns DelimitedScope. DelimitedScope scope(StringRef open = "", StringRef close = "", bool indent = true) { return DelimitedScope(*this, open, close, indent); } /// Prints a string re-indented to the current indent. Re-indents by removing /// the leading whitespace from the first non-empty line from every line of /// the string, skipping over empty lines at the start. Prefixes each line /// with extraPrefix after the indentation. raw_indented_ostream &printReindented(StringRef str, StringRef extraPrefix = ""); /// Increases the indent and returning this raw_indented_ostream. raw_indented_ostream &indent() { currentIndent += indentSize; return *this; } /// Decreases the indent and returning this raw_indented_ostream. raw_indented_ostream &unindent() { currentIndent = std::max(0, currentIndent - indentSize); return *this; } /// Emits whitespace and sets the indentation for the stream. raw_indented_ostream &indent(int with) { os.indent(with); atStartOfLine = false; currentIndent = with; return *this; } private: void write_impl(const char *ptr, size_t size) final; /// Return the current position within the stream, not counting the bytes /// currently in the buffer. uint64_t current_pos() const final { return os.tell(); } /// Constant indent added/removed. static constexpr int indentSize = 2; /// Tracker for current indentation. int currentIndent = 0; /// The leading whitespace of the string being printed, if reindent is used. int leadingWs = 0; /// The extra prefix to be printed, if reindent is used. StringRef currentExtraPrefix; /// Tracks whether at start of line and so indent is required or not. bool atStartOfLine = true; /// The underlying raw_ostream. raw_ostream &os; }; inline raw_indented_ostream & mlir::raw_indented_ostream::printReindented(StringRef str, StringRef extraPrefix) { StringRef output = str; // Skip empty lines. while (!output.empty()) { auto split = output.split('\n'); // Trim Windows \r characters from \r\n line endings. auto firstTrimmed = split.first.rtrim('\r'); size_t indent = firstTrimmed.find_first_not_of(" \t"); if (indent != StringRef::npos) { // Set an initial value. leadingWs = indent; break; } output = split.second; } // Determine the maximum indent. StringRef remaining = output; while (!remaining.empty()) { auto split = remaining.split('\n'); auto firstTrimmed = split.first.rtrim('\r'); size_t indent = firstTrimmed.find_first_not_of(" \t"); if (indent != StringRef::npos) leadingWs = std::min(leadingWs, static_cast(indent)); remaining = split.second; } // Print, skipping the empty lines. std::swap(currentExtraPrefix, extraPrefix); *this << output; std::swap(currentExtraPrefix, extraPrefix); leadingWs = 0; return *this; } inline void mlir::raw_indented_ostream::write_impl(const char *ptr, size_t size) { StringRef str(ptr, size); // Print out indented. auto print = [this](StringRef str) { if (atStartOfLine) os.indent(currentIndent) << currentExtraPrefix << str.substr(leadingWs); else os << str.substr(leadingWs); }; while (!str.empty()) { size_t idx = str.find('\n'); if (idx == StringRef::npos) { if (!str.substr(leadingWs).empty()) { print(str); atStartOfLine = false; } break; } auto split = std::make_pair(str.substr(0, idx), str.substr(idx + 1)); // Print empty new line without spaces if line only has spaces and no extra // prefix is requested. if (!split.first.ltrim().empty() || !currentExtraPrefix.empty()) print(split.first); os << '\n'; atStartOfLine = true; str = split.second; } } } // namespace mlir #endif // MLIR_SUPPORT_INDENTEDOSTREAM_H_