mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-20 22:33:08 +02:00
Merge branch 'silver-bullet' into 1.7.0
Conflicts: src/command.cpp
This commit is contained in:
commit
4346f83f6e
6 changed files with 176 additions and 1 deletions
|
@ -433,6 +433,22 @@ ID Project Pri Description
|
|||
command.
|
||||
</p>
|
||||
|
||||
<strong>% task <id> edit</strong>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<strong>% task <id> fg:... bg:...</strong>
|
||||
<p>
|
||||
Not strictly a command, the setting of the fg and bg (foreground
|
||||
|
|
114
src/command.cpp
114
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 weekstart "
|
||||
"next project shadow.command shadow.file shadow.notify weekstart 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 "
|
||||
|
@ -860,6 +860,118 @@ 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
|
||||
// wrench. To be used sparingly.
|
||||
std::string handleEdit (TDB& tdb, T& task, Config& conf)
|
||||
{
|
||||
std::stringstream out;
|
||||
std::vector <T> 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.");
|
||||
|
||||
// Create a temp file name in data.location.
|
||||
std::stringstream pattern;
|
||||
pattern << dataLocation << "/task." << seq->getId () << ".XXXXXX";
|
||||
char cpattern [PATH_MAX];
|
||||
strcpy (cpattern, pattern.str ().c_str ());
|
||||
char* file = mktemp (cpattern);
|
||||
|
||||
// Format the contents, T -> text.
|
||||
std::stringstream before;
|
||||
before << "# The 'task edit <id>' 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
|
||||
|
||||
<< "# 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 <std::string> tags;
|
||||
seq->getTags (tags);
|
||||
std::string allTags;
|
||||
join (allTags, " ", tags);
|
||||
before << " Tags: " << leftDelim << allTags << rightDelim << std::endl;
|
||||
|
||||
std::map <time_t, std::string> 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
|
||||
std::string editor = conf.get ("editor", "");
|
||||
if (editor == "") editor = getenv ("VISUAL");
|
||||
if (editor == "") editor = getenv ("EDITOR");
|
||||
if (editor == "") editor = "vi";
|
||||
|
||||
// Launch the editor.
|
||||
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"));
|
||||
|
||||
// Modify task.
|
||||
tdb.modifyT (*seq);
|
||||
|
||||
// Cleanup.
|
||||
unlink (file);
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleColor (Config& conf)
|
||||
{
|
||||
|
|
|
@ -129,6 +129,7 @@ static const char* commands[] =
|
|||
"delete",
|
||||
"done",
|
||||
"duplicate",
|
||||
"edit",
|
||||
"export",
|
||||
"help",
|
||||
"history",
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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 <T>&);
|
||||
int deltaAppend (T&, T&);
|
||||
int deltaDescription (T&, T&);
|
||||
|
@ -151,6 +152,8 @@ std::string expandPath (const std::string&);
|
|||
#endif
|
||||
|
||||
bool slurp (const std::string&, std::vector <std::string>&, bool trimLines = false);
|
||||
bool slurp (const std::string&, std::string&, bool trimLines = false);
|
||||
void spit (const std::string&, const std::string&);
|
||||
|
||||
// rules.cpp
|
||||
void initializeColorRules (Config&);
|
||||
|
|
38
src/util.cpp
38
src/util.cpp
|
@ -434,3 +434,41 @@ bool slurp (
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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)
|
||||
{
|
||||
std::ofstream out (file.c_str ());
|
||||
if (out.good ())
|
||||
{
|
||||
out << contents;
|
||||
out.close ();
|
||||
}
|
||||
else
|
||||
throw std::string ("Could not write file '") + file + "'";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue