format_subcommand.cpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. 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. /*include_diagnostic_kind=*/false);
  58. for (auto& f : options_.input_filenames) {
  59. // Push a result, which we'll update on failure.
  60. result.per_file_success.push_back({f.str(), true});
  61. // TODO: Consider refactoring this for sharing with compile.
  62. // TODO: Decide what to do with `-` when there are multiple arguments.
  63. auto source = SourceBuffer::MakeFromFileOrStdin(driver_env.fs, f, consumer);
  64. if (!source) {
  65. mark_per_file_error();
  66. continue;
  67. }
  68. SharedValueStores value_stores;
  69. auto tokens = Lex::Lex(value_stores, *source, consumer);
  70. std::string buffer_str;
  71. llvm::raw_string_ostream buffer(buffer_str);
  72. if (Format::Format(tokens, buffer)) {
  73. // TODO: Figure out a multi-file output setup that supports good
  74. // multi-file testing.
  75. // TODO: Use --output values (and default to overwrite).
  76. driver_env.output_stream << buffer_str;
  77. } else {
  78. mark_per_file_error();
  79. driver_env.output_stream << source->text();
  80. }
  81. }
  82. return result;
  83. }
  84. } // namespace Carbon