format_subcommand.cpp 3.1 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 "common/raw_string_ostream.h"
  7. #include "toolchain/base/shared_value_stores.h"
  8. #include "toolchain/diagnostics/diagnostic_consumer.h"
  9. #include "toolchain/format/format.h"
  10. #include "toolchain/lex/lex.h"
  11. #include "toolchain/source/source_buffer.h"
  12. namespace Carbon {
  13. auto FormatOptions::Build(CommandLine::CommandBuilder& b) -> void {
  14. b.AddStringPositionalArg(
  15. {
  16. .name = "FILE",
  17. .help = R"""(
  18. The input Carbon source file(s) to format.
  19. )""",
  20. },
  21. [&](auto& arg_b) {
  22. arg_b.Required(true);
  23. arg_b.Append(&input_filenames);
  24. });
  25. b.AddStringOption(
  26. {
  27. .name = "output",
  28. .value_name = "FILE",
  29. .help = R"""(
  30. The output filename for formatted output.
  31. By default, the input file is formatted. Passing `--output=-` will write the
  32. output to stdout.
  33. Not valid when multiple files are passed for formatting.
  34. )""",
  35. },
  36. [&](auto& arg_b) { arg_b.Set(&output_filename); });
  37. }
  38. static constexpr CommandLine::CommandInfo SubcommandInfo = {
  39. .name = "format",
  40. .help = R"""(
  41. Format Carbon source code.
  42. )""",
  43. };
  44. FormatSubcommand::FormatSubcommand() : DriverSubcommand(SubcommandInfo) {}
  45. auto FormatSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  46. DriverResult result = {.success = true};
  47. if (options_.input_filenames.size() > 1 &&
  48. !options_.output_filename.empty()) {
  49. CARBON_DIAGNOSTIC(FormatMultipleFilesToOneOutput, Error,
  50. "multiple input files are being provided; --output only "
  51. "works with one input");
  52. driver_env.emitter.Emit(FormatMultipleFilesToOneOutput);
  53. result.success = false;
  54. return result;
  55. }
  56. auto mark_per_file_error = [&]() {
  57. result.success = false;
  58. result.per_file_success.back().second = false;
  59. };
  60. for (auto& f : options_.input_filenames) {
  61. // Push a result, which we'll update on failure.
  62. result.per_file_success.push_back({f.str(), true});
  63. // TODO: Consider refactoring this for sharing with compile.
  64. // TODO: Decide what to do with `-` when there are multiple arguments.
  65. auto source = SourceBuffer::MakeFromFileOrStdin(*driver_env.fs, f,
  66. driver_env.consumer);
  67. if (!source) {
  68. mark_per_file_error();
  69. continue;
  70. }
  71. SharedValueStores value_stores;
  72. auto tokens = Lex::Lex(value_stores, *source, driver_env.consumer);
  73. RawStringOstream buffer;
  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.TakeStr();
  79. } else {
  80. buffer.clear();
  81. mark_per_file_error();
  82. *driver_env.output_stream << source->text();
  83. }
  84. }
  85. return result;
  86. }
  87. } // namespace Carbon