Implement `gn edit "rename attr attr"` Change-Id: Ia0d2208d15dbf6ff8ff9027051583b4e6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25461 Commit-Queue: Matt Stark <msta@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/docs/reference.md b/docs/reference.md index 26ab83c..7608924 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -760,6 +760,12 @@ Example: gn edit "remove deps //base" //src/tools:* + rename <from_attribute> <to_attribute> + Renames <from_attribute> to <to_attribute>. + + Example: + gn edit "rename srcs sources" //src/tools:* + set <attribute>[:list] <value(s)> Sets or overwrites the target's <attribute> to <value(s)>. If multiple values are provided, or if the ":list" suffix is
diff --git a/src/gn/edit_command.cc b/src/gn/edit_command.cc index 8f02524..d83bdc7 100644 --- a/src/gn/edit_command.cc +++ b/src/gn/edit_command.cc
@@ -67,6 +67,12 @@ " Example:\n" " gn edit \"remove deps //base\" //src/tools:*\n" "\n" + " rename <from_attribute> <to_attribute>\n" + " Renames <from_attribute> to <to_attribute>.\n" + "\n" + " Example:\n" + " gn edit \"rename srcs sources\" //src/tools:*\n" + "\n" " set <attribute>[:list] <value(s)>\n" " Sets or overwrites the target's <attribute> to <value(s)>.\n" " If multiple values are provided, or if the \":list\" suffix is\n"
diff --git a/src/gn/edit_command_unittest.cc b/src/gn/edit_command_unittest.cc index e6a0e2c..e856313 100644 --- a/src/gn/edit_command_unittest.cc +++ b/src/gn/edit_command_unittest.cc
@@ -367,6 +367,44 @@ "\"deps\".")}})); } +TEST_F(EditCommandTest, RenameSubcommand) { + EXPECT_SUCCESS(DoEdit("rename srcs sources", + R"( +executable("foo") { + # This comment should be preserved + srcs = [ "foo.cc" ] + if (is_linux) { + srcs += [ "linux.cc" ] + } +} +)"), + Edited(R"( +executable("foo") { + # This comment should be preserved + sources = [ "foo.cc" ] + if (is_linux) { + sources += [ "linux.cc" ] + } +} +)")); + + EXPECT_SUCCESS(DoEdit("rename nonexistent new_attr", {"//:foo"}, + R"( +executable("foo") { + sources = [ "foo.cc" ] +} +)"), + Edited(R"( +executable("foo") { + sources = [ "foo.cc" ] +} +)", + EditState{{}, + {Err(Location(), + "Target \"//:foo\" does not contain the " + "attribute \"nonexistent\".")}})); +} + TEST_F(EditCommandTest, SetSubcommand) { // New bool attribute EXPECT_SUCCESS(DoEdit("set testonly true",
diff --git a/src/gn/edit_subcommands.cc b/src/gn/edit_subcommands.cc index 34455f1..841f9eb 100644 --- a/src/gn/edit_subcommands.cc +++ b/src/gn/edit_subcommands.cc
@@ -234,6 +234,25 @@ }); } +EditCommand RenameAttributeCommand(std::string_view from_attribute, + std::string_view to_attribute) { + return EditTargetCommand([from_attribute = std::string(from_attribute), + to_attribute = std::string(to_attribute)]( + BuildFile& build_file, const EditTarget& target, + EditState& state) -> Err { + auto assignments = target.assignments(from_attribute); + for (auto& assignment : assignments) { + assignment->AsBinaryOpMut()->set_left( + build_file.create_identifier(to_attribute)); + } + if (assignments.empty() && target.is_explicit) { + target.add_warning( + state, "does not contain the attribute \"" + from_attribute + "\"."); + } + return Ok(); + }); +} + // Sets an attribute to a value. EditCommand SetCommand(std::string attribute, Value value) { return EditTargetCommand([=](BuildFile& build_file, const EditTarget& target, @@ -295,6 +314,12 @@ ASSIGN_OR_RETURN(std::vector<Value> values, ParseValues(base::make_span(args).subspan(2))); return RemoveFromAttributeCommand(args[1], std::move(values)); + } else if (args[0] == "rename") { + if (args.size() != 3) { + return Err(Location(), "Invalid rename command.", + "Usage: rename <from_attribute> <to_attribute>"); + } + return RenameAttributeCommand(args[1], args[2]); } else if (args[0] == "set") { if (args.size() < 3) { return Err(Location(),