From 7d5a9d9f7ff52e8b60878c742c7fb71d198f9ff6 Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Sat, 26 Dec 2020 23:49:27 +0100 Subject: [PATCH] Support editing multi-line annotations (Closes: #2283) Since e4b9c1f annotations where JSON encoded in task edit to escape new lines (\n). But other strings where mangled as well, like https:// becoming https:\/\/, making it hard to edit. This patch removes the JSON encoding and indents new lines instead. --- src/commands/CmdEdit.cpp | 32 ++++++++++++++++++++++++++++---- src/commands/CmdEdit.h | 2 ++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index d00af530b..6673d190c 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -51,6 +51,8 @@ #define STRING_EDIT_UNTIL_MOD "Until date modified." #define STRING_EDIT_WAIT_MOD "Wait date modified." +const std::string CmdEdit::ANNOTATION_EDIT_MARKER = "\n "; + //////////////////////////////////////////////////////////////////////////////// CmdEdit::CmdEdit () { @@ -170,6 +172,23 @@ std::vector CmdEdit::findValues ( return results; } +//////////////////////////////////////////////////////////////////////////////// +std::string CmdEdit::replaceString ( + std::string text, + const std::string& search, + const std::string& replacement) +{ + std::string::size_type found = 0; + + while ((found = text.find (search, found)) != std::string::npos) + { + text.replace (found, search.length (), replacement); + found += replacement.length (); + } + + return text; +} + //////////////////////////////////////////////////////////////////////////////// std::string CmdEdit::formatDate ( Task& task, @@ -246,13 +265,14 @@ std::string CmdEdit::formatTask (Task task, const std::string& dateformat) if (verbose) before << "# Annotations look like this: -- and there can be any number of them.\n" "# The ' -- ' separator between the date and text field should not be removed.\n" + "# Multiline annotations need to be indented up to (" << ANNOTATION_EDIT_MARKER.length () - 1 << " spaces).\n" "# A \"blank slot\" for adding an annotation follows for your convenience.\n"; for (auto& anno : task.getAnnotations ()) { Datetime dt (strtol (anno.first.substr (11).c_str (), nullptr, 10)); before << " Annotation: " << dt.toString (dateformat) - << " -- " << json::encode (anno.second) << '\n'; + << " -- " << replaceString (anno.second, "\n", ANNOTATION_EDIT_MARKER) << '\n'; } Datetime now; @@ -610,10 +630,14 @@ void CmdEdit::parseTask (Task& task, const std::string& after, const std::string { found += 14; // Length of "\n Annotation:". - auto eol = after.find ('\n', found + 1); + auto eol = found; + while ((eol = after.find ('\n', ++eol)) != std::string::npos) + if (after.substr (eol, ANNOTATION_EDIT_MARKER.length ()) != ANNOTATION_EDIT_MARKER) + break; + if (eol != std::string::npos) { - auto value = Lexer::trim (after.substr (found, eol - found), "\t "); + auto value = Lexer::trim (replaceString (after.substr (found, eol - found), ANNOTATION_EDIT_MARKER, "\n"), "\t "); auto gap = value.find (" -- "); if (gap != std::string::npos) { @@ -641,7 +665,7 @@ void CmdEdit::parseTask (Task& task, const std::string& after, const std::string while (annotations.find (name.str ()) != annotations.end ()); auto text = Lexer::trim (value.substr (gap + 4), "\t "); - annotations.insert (std::make_pair (name.str (), json::decode (text))); + annotations.insert (std::make_pair (name.str (), text)); } } } diff --git a/src/commands/CmdEdit.h b/src/commands/CmdEdit.h index 06cf0aae1..0884a504a 100644 --- a/src/commands/CmdEdit.h +++ b/src/commands/CmdEdit.h @@ -41,12 +41,14 @@ class CmdEdit : public Command std::string findValue (const std::string&, const std::string&); std::string findMultilineValue (const std::string&, const std::string&, const std::string&); std::vector findValues (const std::string&, const std::string&); + std::string replaceString (std::string text, const std::string& search, const std::string& replacement); std::string formatDate (Task&, const std::string&, const std::string&); std::string formatDuration (Task&, const std::string&); std::string formatTask (Task, const std::string&); void parseTask (Task&, const std::string&, const std::string&); enum class editResult { error, changes, nochanges }; editResult editFile (Task&); + static const std::string ANNOTATION_EDIT_MARKER; }; #endif