| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/console/format_settings.h" |
| |
| #include "src/developer/debug/zxdb/client/session.h" |
| #include "src/developer/debug/zxdb/client/setting_schema.h" |
| #include "src/developer/debug/zxdb/client/setting_store.h" |
| #include "src/developer/debug/zxdb/client/system.h" |
| #include "src/developer/debug/zxdb/client/target.h" |
| #include "src/developer/debug/zxdb/client/thread.h" |
| #include "src/developer/debug/zxdb/common/err.h" |
| #include "src/developer/debug/zxdb/console/command.h" |
| #include "src/developer/debug/zxdb/console/command_utils.h" |
| #include "src/developer/debug/zxdb/console/format_table.h" |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/developer/debug/zxdb/console/string_util.h" |
| #include "src/lib/fxl/strings/join_strings.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| std::vector<std::string> ListToBullet(const std::vector<std::string>& list) { |
| std::vector<std::string> output; |
| output.reserve(list.size()); |
| auto bullet = GetBullet(); |
| for (const std::string& item : list) { |
| output.emplace_back( |
| fxl::StringPrintf("%s %s", bullet.c_str(), FormatConsoleString(item).c_str())); |
| } |
| return output; |
| } |
| |
| // |add_heading| refers whether it should show the setting name or just list the values. |
| void AddSettingToTable(ConsoleContext* context, const std::string& name, const SettingValue& value, |
| std::vector<std::vector<OutputBuffer>>* rows, bool add_heading) { |
| // TODO(donosoc): We need to check what level the setting comes from so we can highlight it in the |
| // listing. |
| if (!value.is_list()) { |
| // Normal values as just entered as key-value pairs. |
| auto& row = rows->emplace_back(); |
| if (add_heading) |
| row.emplace_back(Syntax::kVariable, name); |
| row.emplace_back(FormatSettingValue(context, value)); |
| } else { |
| // List get special treatment so that we can show them as bullet lists. This make reading them |
| // much easier when the elements of the lists are long (eg. paths). |
| auto bullet_list = ListToBullet(value.get_list()); |
| // Special case for empty list. |
| if (bullet_list.empty()) { |
| auto& row = rows->emplace_back(); |
| if (add_heading) |
| row.emplace_back(Syntax::kVariable, name); |
| row.emplace_back(Syntax::kComment, "<empty>"); |
| } else { |
| for (size_t i = 0; i < bullet_list.size(); i++) { |
| auto& row = rows->emplace_back(); |
| |
| if (add_heading) { |
| // The first entry has the setting name. |
| auto title = i == 0 ? OutputBuffer(Syntax::kVariable, name) : OutputBuffer(); |
| auto it = row.emplace_back(std::move(title)); |
| } |
| row.emplace_back(std::move(bullet_list[i])); |
| } |
| } |
| } |
| } |
| |
| } // namespace |
| |
| OutputBuffer FormatSettingStore(ConsoleContext* context, const SettingStore& store) { |
| std::vector<std::vector<OutputBuffer>> rows; |
| for (auto [key, _] : store.schema()->settings()) { |
| auto value = store.GetValue(key); |
| FX_DCHECK(!value.is_null()); |
| AddSettingToTable(context, key, value, &rows, true); |
| } |
| |
| OutputBuffer table; |
| FormatTable({ColSpec(Align::kLeft, 0, std::string(), 2), ColSpec()}, rows, &table); |
| return table; |
| } |
| |
| OutputBuffer FormatSetting(ConsoleContext* context, const std::string& name, |
| const std::string& description, const SettingValue& value) { |
| // Heading, type, and help description. |
| OutputBuffer out; |
| out.Append(Syntax::kHeading, name); |
| out.Append(Syntax::kComment, fxl::StringPrintf(" (%s)\n\n", SettingTypeToString(value.type()))); |
| |
| out.Append(description); |
| out.Append(OutputBuffer("\n\n")); |
| |
| out.Append(Syntax::kVariable, name); |
| out.Append(" = "); |
| |
| // Nonempty lists are written on the following line. Everything else goes on the same line. |
| if (value.is_list() && !value.get_list().empty()) |
| out.Append("\n"); |
| out.Append(FormatSettingShort(context, name, value, 2)); |
| |
| return out; |
| } |
| |
| OutputBuffer FormatSettingShort(ConsoleContext* context, const std::string& name, |
| const SettingValue& value, int list_indent) { |
| FX_DCHECK(!value.is_null()); |
| |
| // Only indent nonempty lists. |
| int pad_left = value.is_list() && !value.get_list().empty() ? list_indent : 0; |
| |
| OutputBuffer out; |
| std::vector<std::vector<OutputBuffer>> rows; |
| AddSettingToTable(context, name, value, &rows, false); |
| FormatTable(std::vector<ColSpec>{ColSpec(Align::kLeft, 0, std::string(), pad_left)}, |
| std::move(rows), &out); |
| return out; |
| } |
| |
| OutputBuffer FormatSettingValue(ConsoleContext* context, const SettingValue& value) { |
| switch (value.type()) { |
| case SettingType::kBoolean: { |
| return OutputBuffer(BoolToString(value.get_bool())); |
| } |
| case SettingType::kInteger: { |
| return std::to_string(value.get_int()); |
| } |
| case SettingType::kString: { |
| auto string = value.get_string(); |
| return string.empty() ? OutputBuffer(Syntax::kComment, "\"\"") |
| : OutputBuffer(FormatConsoleString(string)); |
| } |
| case SettingType::kList: { |
| const auto& list = value.get_list(); |
| std::string result; |
| for (size_t i = 0; i < list.size(); i++) { |
| if (i > 0) |
| result += " "; |
| result += FormatConsoleString(list[i]); |
| } |
| return OutputBuffer(result); |
| } |
| case SettingType::kExecutionScope: { |
| return ExecutionScopeToString(context, value.get_execution_scope()); |
| } |
| case SettingType::kInputLocations: { |
| return FormatInputLocations(value.get_input_locations()); |
| } |
| case SettingType::kNull: { |
| return OutputBuffer(Syntax::kComment, "<null>"); |
| } |
| } |
| } |
| |
| } // namespace zxdb |