format_subcommand.cpp 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/driver/format_subcommand.h"
  5. #include <string>
  6. #include "toolchain/base/value_store.h"
  7. #include "toolchain/diagnostics/diagnostic_consumer.h"
  8. #include "toolchain/format/format.h"
  9. #include "toolchain/lex/lex.h"
  10. #include "toolchain/source/source_buffer.h"
  11. namespace Carbon {
  12. constexpr CommandLine::CommandInfo FormatOptions::Info = {
  13. .name = "format",
  14. .help = R"""(
  15. Format Carbon source code.
  16. )""",
  17. };
  18. auto FormatOptions::Build(CommandLine::CommandBuilder& b) -> void {
  19. b.AddStringPositionalArg(
  20. {
  21. .name = "FILE",
  22. .help = R"""(
  23. The input Carbon source file(s) to format.
  24. )""",
  25. },
  26. [&](auto& arg_b) {
  27. arg_b.Required(true);
  28. arg_b.Append(&input_filenames);
  29. });
  30. b.AddStringOption(
  31. {
  32. .name = "output",
  33. .value_name = "FILE",
  34. .help = R"""(
  35. The output filename for formatted output.
  36. By default, the input file is formatted. Passing `--output=-` will write the
  37. output to stdout.
  38. Not valid when multiple files are passed for formatting.
  39. )""",
  40. },
  41. [&](auto& arg_b) { arg_b.Set(&output_filename); });
  42. }
  43. auto FormatSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  44. DriverResult result = {.success = true};
  45. if (options_.input_filenames.size() > 1 &&
  46. !options_.output_filename.empty()) {
  47. driver_env.error_stream << "error: cannot format multiple input files when "
  48. "--output is set\n";
  49. result.success = false;
  50. return result;
  51. }
  52. auto mark_per_file_error = [&]() {
  53. result.success = false;
  54. result.per_file_success.back().second = false;
  55. };
  56. StreamDiagnosticConsumer consumer(driver_env.error_stream);
  57. for (auto& f : options_.input_filenames) {
  58. // Push a result, which we'll update on failure.
  59. result.per_file_success.push_back({f.str(), true});
  60. // TODO: Consider refactoring this for sharing with compile.
  61. // TODO: Decide what to do with `-` when there are multiple arguments.
  62. auto source = SourceBuffer::MakeFromFileOrStdin(driver_env.fs, f, consumer);
  63. if (!source) {
  64. mark_per_file_error();
  65. continue;
  66. }
  67. SharedValueStores value_stores;
  68. auto tokens = Lex::Lex(value_stores, *source, consumer);
  69. std::string buffer_str;
  70. llvm::raw_string_ostream buffer(buffer_str);
  71. if (Format::Format(tokens, buffer)) {
  72. // TODO: Figure out a multi-file output setup that supports good
  73. // multi-file testing.
  74. // TODO: Use --output values (and default to overwrite).
  75. driver_env.output_stream << buffer_str;
  76. } else {
  77. mark_per_file_error();
  78. driver_env.output_stream << source->text();
  79. }
  80. }
  81. return result;
  82. }
  83. } // namespace Carbon