From 48e8a268138f80c12efed5ba6dba82b8501b61a6 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 25 Oct 2021 12:16:20 +0200
Subject: [PATCH] command/playlist: allow range in playlistdelete

---
 NEWS                             |  1 +
 doc/protocol.rst                 |  4 +++-
 src/PlaylistFile.cxx             | 11 +++++++++++
 src/PlaylistFile.hxx             |  2 ++
 src/command/PlaylistCommands.cxx |  4 ++--
 5 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index c5ebc9136..b63e85ccf 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 ver 0.23.3 (not yet released)
 * protocol
   - add optional position parameter to "add" and "playlistadd"
+  - allow range in "playlistdelete"
 * output
   - alsa: add option "stop_dsd_silence" to work around DSD DAC noise
 * macOS: fix libfmt related build failure
diff --git a/doc/protocol.rst b/doc/protocol.rst
index 896b38b31..256beabbc 100644
--- a/doc/protocol.rst
+++ b/doc/protocol.rst
@@ -929,7 +929,7 @@ remote playlists (absolute URI with a supported scheme).
     inserted into the queue; it can be relative as described in
     :ref:`addid <command_addid>`.  (This requires specifying the range
     as well; the special value `0:` can be used if the whole playlist
-    shall be loaded at a certain queue position.)  [#since_0_23_1]
+    shall be loaded at a certain queue position.)  [#since_0_23_1]_
 
 .. _command_playlistadd:
 
@@ -953,6 +953,8 @@ remote playlists (absolute URI with a supported scheme).
     Deletes ``SONGPOS`` from the
     playlist `NAME.m3u`.
 
+    The second parameter can be a range. [#since_0_23_3]_
+
 .. _command_playlistmove:
 
 :command:`playlistmove {NAME} {FROM} {TO}`
diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx
index 08c91c6ae..bd7e0b19b 100644
--- a/src/PlaylistFile.cxx
+++ b/src/PlaylistFile.cxx
@@ -26,6 +26,7 @@
 #include "song/DetachedSong.hxx"
 #include "SongLoader.hxx"
 #include "Mapper.hxx"
+#include "protocol/RangeArg.hxx"
 #include "fs/io/TextFile.hxx"
 #include "fs/io/FileOutputStream.hxx"
 #include "fs/io/BufferedOutputStream.hxx"
@@ -314,6 +315,16 @@ PlaylistFileEditor::RemoveIndex(unsigned i)
 	contents.erase(std::next(contents.begin(), i));
 }
 
+void
+PlaylistFileEditor::RemoveRange(RangeArg range)
+{
+	if (!range.CheckClip(size()))
+		throw PlaylistError::BadRange();
+
+	contents.erase(std::next(contents.begin(), range.start),
+		       std::next(contents.begin(), range.end));
+}
+
 void
 PlaylistFileEditor::Save()
 {
diff --git a/src/PlaylistFile.hxx b/src/PlaylistFile.hxx
index 063a482d4..e96f27ddd 100644
--- a/src/PlaylistFile.hxx
+++ b/src/PlaylistFile.hxx
@@ -26,6 +26,7 @@
 #include <string>
 
 struct ConfigData;
+struct RangeArg;
 class DetachedSong;
 class SongLoader;
 class PlaylistVector;
@@ -60,6 +61,7 @@ public:
 
 	void MoveIndex(unsigned src, unsigned dest);
 	void RemoveIndex(unsigned i);
+	void RemoveRange(RangeArg range);
 
 	void Save();
 
diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx
index 1654aeddc..95e9eda2b 100644
--- a/src/command/PlaylistCommands.cxx
+++ b/src/command/PlaylistCommands.cxx
@@ -175,10 +175,10 @@ handle_playlistdelete([[maybe_unused]] Client &client,
 		      Request args, [[maybe_unused]] Response &r)
 {
 	const char *const name = args[0];
-	unsigned from = args.ParseUnsigned(1);
+	const auto range = args.ParseRange(1);
 
 	PlaylistFileEditor editor(name, PlaylistFileEditor::LoadMode::YES);
-	editor.RemoveIndex(from);
+	editor.RemoveRange(range);
 	editor.Save();
 	return CommandResult::OK;
 }