From 407ef39c54ccbc35206330eb08c171ffe1df2deb Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Sun, 10 May 2009 01:54:43 -0400
Subject: [PATCH 1/4] Enhancement - edit command
- Mere beginnings of the edit command. No functionality yet.
---
src/command.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++
src/parse.cpp | 1 +
src/task.cpp | 5 ++++
src/task.h | 2 ++
src/util.cpp | 13 +++++++++
5 files changed, 91 insertions(+)
diff --git a/src/command.cpp b/src/command.cpp
index 3dbc6f2d9..a8e3a03a7 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -860,6 +860,76 @@ std::string handleDuplicate (TDB& tdb, T& task, Config& conf)
return out.str ();
}
+////////////////////////////////////////////////////////////////////////////////
+// Introducing the Silver Bullet. This feature is the catch-all fixative for
+// various other ills. This is like opening up the hood and going in with a
+// wrench. To be used sparingly.
+std::string handleEdit (TDB& tdb, T& task, Config& conf)
+{
+ std::stringstream out;
+ std::vector all;
+ tdb.pendingT (all);
+
+ filterSequence (all, task);
+ foreach (seq, all)
+ {
+ // Check for file permissions.
+ std::string dataLocation = expandPath (conf.get ("data.location"));
+ if (access (dataLocation.c_str (), X_OK))
+ throw std::string ("Your data.location directory is not writable.");
+
+ // TODO Create a temp file name in data.location.
+ std::stringstream pattern;
+ pattern << dataLocation
+ << "/task."
+ << seq->getId ()
+ << ".XXXXXX";
+ std::cout << "# pattern=" << pattern << std::endl;
+ char cpattern [PATH_MAX];
+ strcpy (cpattern, pattern.str ().c_str ());
+
+ char* file = mktemp (cpattern);
+ std::cout << "# file=" << file << std::endl;
+
+ // TODO Format the contents, T -> text.
+ std::stringstream before;
+ before << "# Edit only the items within the chevrons << >>. "
+ << "All other edits will be ignored." << std::endl
+ << "ID: " << seq->getId () << std::endl
+ << "Project: <<" << seq->getAttribute ("project") << ">>" << std::endl
+ << "Priority: <<" << seq->getAttribute ("priority") << ">>" << std::endl
+ << "Description: <<" << seq->getDescription () << ">>" << std::endl;
+
+ // Write to file.
+ spit (file, before.str ());
+
+ // TODO Determine correct editor: $EDITOR > $VISUAL > vi
+ std::string editor = "/usr/bin/vi";
+
+ // system ("$EDITOR $file");
+ std::string command = editor + " " + file;
+ system (command.c_str ());
+
+ // Slurp file.
+ std::vector after;
+ slurp (file, after, true);
+
+ // TODO Parse file, text -> T.
+ foreach (line, after)
+ {
+ // TODO Handle each type of line.
+ }
+
+ // Modify task.
+ tdb.modifyT (*seq);
+
+ // Cleanup.
+ unlink (file);
+ }
+
+ return out.str ();
+}
+
////////////////////////////////////////////////////////////////////////////////
std::string handleColor (Config& conf)
{
diff --git a/src/parse.cpp b/src/parse.cpp
index bb5dbdc45..e141139b8 100644
--- a/src/parse.cpp
+++ b/src/parse.cpp
@@ -129,6 +129,7 @@ static const char* commands[] =
"delete",
"done",
"duplicate",
+ "edit",
"export",
"help",
"history",
diff --git a/src/task.cpp b/src/task.cpp
index 92c34ed4b..b93835aaf 100644
--- a/src/task.cpp
+++ b/src/task.cpp
@@ -108,6 +108,10 @@ static std::string shortUsage (Config& conf)
table.addCell (row, 1, "task ID /from/to/g");
table.addCell (row, 2, "Performs all substitutions on the task description, for fixing mistakes");
+ row = table.addRow ();
+ table.addCell (row, 1, "task edit ID");
+ table.addCell (row, 2, "Launches an editor to let you modify all aspects of a task directly, therefore it is to be used carefully");
+
row = table.addRow ();
table.addCell (row, 1, "task duplicate ID [tags] [attrs] [desc...]");
table.addCell (row, 2, "Duplicates the specified task, and allows modifications");
@@ -889,6 +893,7 @@ std::string runTaskCommand (
else if (command == "undo") { cmdMod = true; out = handleUndo (tdb, task, conf); }
else if (command == "import") { cmdMod = true; out = handleImport (tdb, task, conf); }
else if (command == "duplicate") { cmdMod = true; out = handleDuplicate (tdb, task, conf); }
+ else if (command == "edit") { cmdMod = true; out = handleEdit (tdb, task, conf); }
// Command that display IDs and therefore need TDB::gc first.
else if (command == "completed") { if (gc) gcMod = tdb.gc (); out = handleCompleted (tdb, task, conf); }
diff --git a/src/task.h b/src/task.h
index a2b60c69d..bb727ad15 100644
--- a/src/task.h
+++ b/src/task.h
@@ -90,6 +90,7 @@ std::string handleUndo (TDB&, T&, Config&);
std::string handleColor (Config&);
std::string handleAnnotate (TDB&, T&, Config&);
std::string handleDuplicate (TDB&, T&, Config&);
+std::string handleEdit (TDB&, T&, Config&);
T findT (int, const std::vector &);
int deltaAppend (T&, T&);
int deltaDescription (T&, T&);
@@ -151,6 +152,7 @@ std::string expandPath (const std::string&);
#endif
bool slurp (const std::string&, std::vector &, bool trimLines = false);
+void spit (const std::string&, const std::string&);
// rules.cpp
void initializeColorRules (Config&);
diff --git a/src/util.cpp b/src/util.cpp
index 1fe260c89..87d0368f7 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -430,3 +430,16 @@ bool slurp (
}
////////////////////////////////////////////////////////////////////////////////
+void spit (const std::string& file, const std::string& contents)
+{
+ std::ofstream out (file.c_str ());
+ if (out.good ())
+ {
+ out << contents;
+ out.close ();
+ }
+ else
+ throw std::string ("Could not write file '") + file + "'";
+}
+
+////////////////////////////////////////////////////////////////////////////////
From 6762af8ffd8f71696f79f506caa7e35652b27358 Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Sun, 10 May 2009 16:26:48 -0400
Subject: [PATCH 2/4] Enhancement - Edit command
- Added more fields to the edit command.
- Added a more useful slurp implementation.
- Updated advanced.html with directions on use.
---
html/advanced.html | 16 +++++++++++++
src/command.cpp | 58 +++++++++++++++++++++++++++-------------------
src/task.h | 1 +
src/util.cpp | 25 ++++++++++++++++++++
4 files changed, 76 insertions(+), 24 deletions(-)
diff --git a/html/advanced.html b/html/advanced.html
index 976dcd2d6..2d9ac14d7 100644
--- a/html/advanced.html
+++ b/html/advanced.html
@@ -426,6 +426,22 @@ ID Project Pri Description
command.
+ % task <id> edit
+
+ This command allows you to use your text editor to edit all aspects
+ of a task. The specified task will be written to a file, and your
+ text editor will be invoked. If you modify the task in the text
+ editor, task will update accordingly.
+
+
+ Task will first check to see if you have defined a text editor
+ in the TASK_EDITOR environment variable. If not, task will
+ check to see if you defined a text editor in the VISUAL
+ environment variable. If not task will check to see if you
+ defined a text editor in the EDITOR environment variable.
+ If all those fail, task launches vi.
+
+
% task <id> fg:... bg:...
Not strictly a command, the setting of the fg and bg (foreground
diff --git a/src/command.cpp b/src/command.cpp
index a8e3a03a7..cd4fa07ae 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -860,6 +860,18 @@ std::string handleDuplicate (TDB& tdb, T& task, Config& conf)
return out.str ();
}
+////////////////////////////////////////////////////////////////////////////////
+static const char* leftDelim = "<<";
+static const char* rightDelim = ">>";
+static std::string findValue (const std::string& text, const std::string& name)
+{
+ // Look for /^\s+name:\s+<<(.*)>>/
+ // Extract
+ // Trim
+ // Join
+ return "";
+}
+
////////////////////////////////////////////////////////////////////////////////
// Introducing the Silver Bullet. This feature is the catch-all fixative for
// various other ills. This is like opening up the hood and going in with a
@@ -878,47 +890,45 @@ std::string handleEdit (TDB& tdb, T& task, Config& conf)
if (access (dataLocation.c_str (), X_OK))
throw std::string ("Your data.location directory is not writable.");
- // TODO Create a temp file name in data.location.
+ // Create a temp file name in data.location.
std::stringstream pattern;
- pattern << dataLocation
- << "/task."
- << seq->getId ()
- << ".XXXXXX";
- std::cout << "# pattern=" << pattern << std::endl;
+ pattern << dataLocation << "/task." << seq->getId () << ".XXXXXX";
char cpattern [PATH_MAX];
strcpy (cpattern, pattern.str ().c_str ());
-
char* file = mktemp (cpattern);
- std::cout << "# file=" << file << std::endl;
// TODO Format the contents, T -> text.
std::stringstream before;
- before << "# Edit only the items within the chevrons << >>. "
- << "All other edits will be ignored." << std::endl
- << "ID: " << seq->getId () << std::endl
- << "Project: <<" << seq->getAttribute ("project") << ">>" << std::endl
- << "Priority: <<" << seq->getAttribute ("priority") << ">>" << std::endl
- << "Description: <<" << seq->getDescription () << ">>" << std::endl;
+ before << "# Edit only the items within the marks "
+ << leftDelim << " " << rightDelim << "."
+ << "All other edits will be ignored." << std::endl
+ << "ID: " << seq->getId () << std::endl
+ << "Project: " << leftDelim << seq->getAttribute ("project") << rightDelim << std::endl
+ << "Priority: " << leftDelim << seq->getAttribute ("priority") << rightDelim << std::endl
+ << "Description: " << leftDelim << seq->getDescription () << rightDelim << std::endl;
// Write to file.
spit (file, before.str ());
- // TODO Determine correct editor: $EDITOR > $VISUAL > vi
- std::string editor = "/usr/bin/vi";
+ // Determine correct editor: $TASK_EDITOR > $VISUAL > $EDITOR > vi
+ const char* editor = getenv ("TASK_EDITOR");
+ if (!editor) editor = getenv ("VISUAL");
+ if (!editor) editor = getenv ("EDITOR");
+ if (!editor) editor = "vi";
- // system ("$EDITOR $file");
- std::string command = editor + " " + file;
+ // Launch the editor.
+ std::string command = editor;
+ command += " ";
+ command += file;
system (command.c_str ());
// Slurp file.
- std::vector after;
+ std::string after;
slurp (file, after, true);
- // TODO Parse file, text -> T.
- foreach (line, after)
- {
- // TODO Handle each type of line.
- }
+ seq->setAttribute ("Project", findValue (after, "Project"));
+ seq->setAttribute ("Priority", findValue (after, "Priority"));
+ seq->setDescription ( findValue (after, "Description"));
// Modify task.
tdb.modifyT (*seq);
diff --git a/src/task.h b/src/task.h
index bb727ad15..803aac0e6 100644
--- a/src/task.h
+++ b/src/task.h
@@ -152,6 +152,7 @@ std::string expandPath (const std::string&);
#endif
bool slurp (const std::string&, std::vector &, bool trimLines = false);
+bool slurp (const std::string&, std::string&, bool trimLines = false);
void spit (const std::string&, const std::string&);
// rules.cpp
diff --git a/src/util.cpp b/src/util.cpp
index 87d0368f7..cafd50d17 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -429,6 +429,31 @@ bool slurp (
return false;
}
+////////////////////////////////////////////////////////////////////////////////
+bool slurp (
+ const std::string& file,
+ std::string& contents,
+ bool trimLines /* = false */)
+{
+ contents = "";
+
+ std::ifstream in (file.c_str ());
+ if (in.good ())
+ {
+ std::string line;
+ while (getline (in, line))
+ {
+ if (trimLines) line = trim (line);
+ contents += line;
+ }
+
+ in.close ();
+ return true;
+ }
+
+ return false;
+}
+
////////////////////////////////////////////////////////////////////////////////
void spit (const std::string& file, const std::string& contents)
{
From e8da71498da04780729505846d39ba2389925988 Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Mon, 11 May 2009 21:22:06 -0400
Subject: [PATCH 3/4] Documentation Update
- Corrected precedence list on editors.
---
src/command.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/command.cpp b/src/command.cpp
index cd4fa07ae..acec89a08 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -910,7 +910,7 @@ std::string handleEdit (TDB& tdb, T& task, Config& conf)
// Write to file.
spit (file, before.str ());
- // Determine correct editor: $TASK_EDITOR > $VISUAL > $EDITOR > vi
+ // Determine correct editor: .taskrc:editor > $VISUAL > $EDITOR > vi
const char* editor = getenv ("TASK_EDITOR");
if (!editor) editor = getenv ("VISUAL");
if (!editor) editor = getenv ("EDITOR");
From c4e459e8d74ec3df020753714172c68eeeb7e81e Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Wed, 13 May 2009 11:39:07 -0400
Subject: [PATCH 4/4] Silver Bullet - added more fields.
- Added (presumable) all other fields. Now looking for a better
format, and a nice way to parse it all back.
---
src/command.cpp | 62 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/src/command.cpp b/src/command.cpp
index acec89a08..44315e447 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -359,7 +359,7 @@ std::string handleVersion (Config& conf)
"color.pri.L color.pri.M color.pri.none color.recurring color.tagged "
"confirmation curses data.location dateformat default.command "
"default.priority defaultwidth due echo.command locking monthsperline nag "
- "next project shadow.command shadow.file shadow.notify "
+ "next project shadow.command shadow.file shadow.notify editor "
"import.synonym.id import.synonym.uuid import.synonym.status "
"import.synonym.tags import.synonym.entry import.synonym.start "
"import.synonym.due import.synonym.recur import.synonym.end "
@@ -897,35 +897,67 @@ std::string handleEdit (TDB& tdb, T& task, Config& conf)
strcpy (cpattern, pattern.str ().c_str ());
char* file = mktemp (cpattern);
- // TODO Format the contents, T -> text.
+ // Format the contents, T -> text.
std::stringstream before;
- before << "# Edit only the items within the marks "
+ before << "# The 'task edit ' command allows you to modify all aspects of a task" << std::endl
+ << "# using a text editor. What is shown below is a representation of the" << std::endl
+ << "# task in all it's detail. Modify what you wish, and if you save and" << std::endl
+ << "# quit your editor, task will read this file and try to make sense of" << std::endl
+ << "# what changed, and apply those changes. If you quit your editor without" << std::endl
+ << "# saving or making any modifications, task will do nothing." << std::endl
+ << "#" << std::endl
+ << "# Lines that begin with # are comments, and will be ignored by task." << std::endl
+
+ << "# Edit only the items within the marks "
<< leftDelim << " " << rightDelim << "."
<< "All other edits will be ignored." << std::endl
- << "ID: " << seq->getId () << std::endl
- << "Project: " << leftDelim << seq->getAttribute ("project") << rightDelim << std::endl
- << "Priority: " << leftDelim << seq->getAttribute ("priority") << rightDelim << std::endl
- << "Description: " << leftDelim << seq->getDescription () << rightDelim << std::endl;
+
+ << "# Externally Visible" << std::endl
+ << " ID: " << seq->getId () << std::endl
+ << " Status: " << leftDelim << seq->getStatus () << rightDelim << std::endl
+ << " Project: " << leftDelim << seq->getAttribute ("project") << rightDelim << std::endl
+ << " Priority: " << leftDelim << seq->getAttribute ("priority") << rightDelim << std::endl;
+
+ std::vector tags;
+ seq->getTags (tags);
+ std::string allTags;
+ join (allTags, " ", tags);
+ before << " Tags: " << leftDelim << allTags << rightDelim << std::endl;
+
+ std::map annotations;
+ seq->getAnnotations (annotations);
+ foreach (anno, annotations)
+ before << " Annotation: " << leftDelim << anno->first << " " << anno->second << rightDelim << std::endl;
+
+ before << " Description: " << leftDelim << seq->getDescription () << rightDelim << std::endl
+
+ << "# Internals" << std::endl
+ << " Start: " << leftDelim << seq->getAttribute ("start") << rightDelim << std::endl
+ << " End: " << leftDelim << seq->getAttribute ("end") << rightDelim << std::endl
+ << " Due: " << leftDelim << seq->getAttribute ("due") << rightDelim << std::endl
+ << " Recur: " << leftDelim << seq->getAttribute ("recur") << rightDelim << std::endl
+ << " Mask: " << leftDelim << seq->getAttribute ("mask") << rightDelim << std::endl
+ << " iMask: " << leftDelim << seq->getAttribute ("imask") << rightDelim << std::endl;
// Write to file.
spit (file, before.str ());
// Determine correct editor: .taskrc:editor > $VISUAL > $EDITOR > vi
- const char* editor = getenv ("TASK_EDITOR");
- if (!editor) editor = getenv ("VISUAL");
- if (!editor) editor = getenv ("EDITOR");
- if (!editor) editor = "vi";
+ std::string editor = conf.get ("editor", "");
+ if (editor == "") editor = getenv ("VISUAL");
+ if (editor == "") editor = getenv ("EDITOR");
+ if (editor == "") editor = "vi";
// Launch the editor.
- std::string command = editor;
- command += " ";
- command += file;
- system (command.c_str ());
+ editor += " ";
+ editor += file;
+ system (editor.c_str ());
// Slurp file.
std::string after;
slurp (file, after, true);
+ // Update seq based on what can be parsed back out of the file.
seq->setAttribute ("Project", findValue (after, "Project"));
seq->setAttribute ("Priority", findValue (after, "Priority"));
seq->setDescription ( findValue (after, "Description"));