1 // Copyright 2014 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chromeos-dbus-bindings/indented_text.h" 6 7 #include <string> 8 #include <utility> 9 #include <vector> 10 11 #include <base/logging.h> 12 #include <base/strings/string_util.h> 13 #include <brillo/strings/string_utils.h> 14 15 using std::string; 16 using std::vector; 17 18 namespace chromeos_dbus_bindings { 19 20 IndentedText::IndentedText() : offset_(0) {} 21 22 void IndentedText::AddBlankLine() { 23 AddLine(""); 24 } 25 26 void IndentedText::AddBlock(const IndentedText& block) { 27 AddBlockWithOffset(block, 0); 28 } 29 30 void IndentedText::AddBlockWithOffset(const IndentedText& block, size_t shift) { 31 for (const auto& member : block.contents_) { 32 AddLineWithOffset(member.first, member.second + shift); 33 } 34 } 35 36 void IndentedText::AddLine(const std::string& line) { 37 AddLineWithOffset(line, 0); 38 } 39 40 void IndentedText::AddLineWithOffset(const std::string& line, size_t shift) { 41 contents_.emplace_back(line, shift + offset_); 42 } 43 44 void IndentedText::AddLineAndPushOffsetTo(const std::string& line, 45 size_t occurrence, 46 char c) { 47 AddLine(line); 48 size_t pos = 0; 49 while (occurrence > 0) { 50 pos = line.find(c, pos); 51 CHECK(pos != string::npos); 52 pos++; 53 occurrence--; 54 } 55 PushOffset(pos); 56 } 57 58 void IndentedText::AddComments(const std::string& doc_string) { 59 // Try to retain indentation in the comments. Find the first non-empty line 60 // of the comment and find its whitespace indentation prefix. 61 // For all subsequent lines, remove the same whitespace prefix as found 62 // at the first line of the comment but keep any additional spaces to 63 // maintain the comment layout. 64 auto lines = brillo::string_utils::Split(doc_string, "\n", false, false); 65 vector<string> lines_out; 66 lines_out.reserve(lines.size()); 67 bool first_nonempty_found = false; 68 std::string trim_prefix; 69 for (string line : lines) { 70 base::TrimWhitespaceASCII(line, base::TRIM_TRAILING, &line); 71 if (!first_nonempty_found) { 72 size_t pos = line.find_first_not_of(" \t"); 73 if (pos != std::string::npos) { 74 first_nonempty_found = true; 75 trim_prefix = line.substr(0, pos); 76 lines_out.push_back(line.substr(pos)); 77 } 78 } else { 79 if (base::StartsWith(line, trim_prefix, 80 base::CompareCase::INSENSITIVE_ASCII)) { 81 line = line.substr(trim_prefix.length()); 82 } else { 83 base::TrimWhitespaceASCII(line, base::TRIM_LEADING, &line); 84 } 85 lines_out.push_back(line); 86 } 87 } 88 89 // We already eliminated all empty lines at the beginning of the comment 90 // block. Now remove the trailing empty lines. 91 while (!lines_out.empty() && lines_out.back().empty()) 92 lines_out.pop_back(); 93 94 for (const string& line : lines_out) { 95 const bool all_whitespace = (line.find_first_not_of(" \t") == string::npos); 96 if (all_whitespace) { 97 AddLine("//"); 98 } else { 99 AddLine("// " + line); 100 } 101 } 102 } 103 104 string IndentedText::GetContents() const { 105 string output; 106 for (const string& line : GetLines()) { 107 output.append(line); 108 output.append("\n"); 109 } 110 return output; 111 } 112 113 std::vector<std::string> IndentedText::GetLines() const { 114 vector<string> result; 115 for (const auto& member : contents_) { 116 const string& line = member.first; 117 size_t shift = line.empty() ? 0 : member.second; 118 string indent(shift, ' '); 119 result.push_back(indent + line); 120 } 121 return result; 122 } 123 124 void IndentedText::PushOffset(size_t shift) { 125 offset_ += shift; 126 offset_history_.push_back(shift); 127 } 128 129 void IndentedText::PopOffset() { 130 CHECK(!offset_history_.empty()); 131 offset_ -= offset_history_.back(); 132 offset_history_.pop_back(); 133 } 134 135 void IndentedText::Reset() { 136 offset_ = 0; 137 offset_history_.clear(); 138 contents_.clear(); 139 } 140 141 } // namespace chromeos_dbus_bindings 142