format_subcommand.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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/shared_value_stores.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. auto FormatOptions::Build(CommandLine::CommandBuilder& b) -> void {
  13. b.AddStringPositionalArg(
  14. {
  15. .name = "FILE",
  16. .help = R"""(
  17. The input Carbon source file(s) to format.
  18. )""",
  19. },
  20. [&](auto& arg_b) {
  21. arg_b.Required(true);
  22. arg_b.Append(&input_filenames);
  23. });
  24. b.AddStringOption(
  25. {
  26. .name = "output",
  27. .value_name = "FILE",
  28. .help = R"""(
  29. The output filename for formatted output.
  30. By default, the input file is formatted. Passing `--output=-` will write the
  31. output to stdout.
  32. Not valid when multiple files are passed for formatting.
  33. )""",
  34. },
  35. [&](auto& arg_b) { arg_b.Set(&output_filename); });
  36. }
  37. static constexpr CommandLine::CommandInfo SubcommandInfo = {
  38. .name = "format",
  39. .help = R"""(
  40. Format Carbon source code.
  41. )""",
  42. };
  43. FormatSubcommand::FormatSubcommand() : DriverSubcommand(SubcommandInfo) {}
  44. auto FormatSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  45. DriverResult result = {.success = true};
  46. if (options_.input_filenames.size() > 1 &&
  47. !options_.output_filename.empty()) {
  48. driver_env.error_stream << "error: cannot format multiple input files when "
  49. "--output is set\n";
  50. result.success = false;
  51. return result;
  52. }
  53. auto mark_per_file_error = [&]() {
  54. result.success = false;
  55. result.per_file_success.back().second = false;
  56. };
  57. StreamDiagnosticConsumer consumer(driver_env.error_stream,
  58. /*include_diagnostic_kind=*/false);
  59. for (auto& f : options_.input_filenames) {
  60. // Push a result, which we'll update on failure.
  61. result.per_file_success.push_back({f.str(), true});
  62. // TODO: Consider refactoring this for sharing with compile.
  63. // TODO: Decide what to do with `-` when there are multiple arguments.
  64. auto source =
  65. SourceBuffer::MakeFromFileOrStdin(*driver_env.fs, f, consumer);
  66. if (!source) {
  67. mark_per_file_error();
  68. continue;
  69. }
  70. SharedValueStores value_stores;
  71. auto tokens = Lex::Lex(value_stores, *source, consumer);
  72. std::string buffer_str;
  73. llvm::raw_string_ostream buffer(buffer_str);
  74. if (Format::Format(tokens, buffer)) {
  75. // TODO: Figure out a multi-file output setup that supports good
  76. // multi-file testing.
  77. // TODO: Use --output values (and default to overwrite).
  78. driver_env.output_stream << buffer_str;
  79. } else {
  80. mark_per_file_error();
  81. driver_env.output_stream << source->text();
  82. }
  83. }
  84. return result;
  85. }
  86. } // namespace Carbon