Repository: fniessink/next-action

Issue:
Allow for showing all (next) actions
```console
$ next-action --all
(A) World peace
(B) End hunger
(C) Call mom @home
Make a pizza @home
```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 684bdb0..0c3dbfe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 ### Added
 
 - Specify the number of next actions to show: `next-action --number 3`. Closes #7.
+- Show all next actions: `next-action --all`. Closes #29.
 
 ## [0.0.7] - 2018-05-12
 
diff --git a/README.md b/README.md
index 1c06d62..4876ffb 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ Don't know what *Todo.txt* is? See <https://github.com/todotxt/todo.txt> for the
 
 ```console
 $ next-action --help
-usage: next-action [-h] [--version] [-f FILE] [-n N] [@CONTEXT [@CONTEXT ...]] [+PROJECT [+PROJECT ...]]
+uusage: next-action [-h] [--version] [-f FILE] [-n N | -a] [@CONTEXT [@CONTEXT ...]] [+PROJECT [+PROJECT ...]]
 
 Show the next action in your todo.txt
 
@@ -36,6 +36,7 @@ optional arguments:
   --version             show program's version number and exit
   -f FILE, --file FILE  filename of the todo.txt file to read (default: todo.txt)
   -n N, --number N      number of next actions to show (default: 1)
+  -a, --all             show all next actions (default: False)
 ```
 
 Assuming your todo.txt file is in the current folder, running *Next-action* without arguments will show the next action you should do based on your tasks' priorities:
@@ -74,6 +75,19 @@ $ next-action --number 3
 (C) Finish proposal for important client @work
 ```
 
+Or even show all next actions:
+
+```console
+$ next-action --number 3
+(A) Call mom @phone
+(B) Buy paint to +PaintHouse @store @weekend
+(C) Finish proposal for important client @work
+(G) Buy wood for new +DogHouse @store
+...
+```
+
+Note that completed tasks are never shown since they can't be a next action.
+
 Since *Next-action* is still pre-alpha-stage, this is it for the moment. Stay tuned for more options.
 
 ## Develop
diff --git a/next_action/arguments.py b/next_action/arguments.py
index 51249fc..ec87cb1 100644
--- a/next_action/arguments.py
+++ b/next_action/arguments.py
@@ -3,6 +3,7 @@
 import argparse
 import os
 import shutil
+import sys
 from typing import Any, Sequence, Union
 
 import next_action
@@ -17,12 +18,12 @@ class ContextProjectAction(argparse.Action):  # pylint: disable=too-few-public-m
         contexts = []
         projects = []
         for value in values:
-            if self.__is_valid("Context", "@", value, parser):
+            if self.__is_valid("context", "@", value, parser):
                 contexts.append(value.strip("@"))
-            elif self.__is_valid("Project", "+", value, parser):
+            elif self.__is_valid("project", "+", value, parser):
                 projects.append(value.strip("+"))
             else:
-                parser.error("Unrecognized argument '{0}'.".format(value))
+                parser.error("unrecognized argument: {0}".format(value))
         if namespace.contexts is None:
             namespace.contexts = contexts
         if namespace.projects is None:
@@ -35,7 +36,7 @@ class ContextProjectAction(argparse.Action):  # pylint: disable=too-few-public-m
             if len(value) > 1:
                 return True
             else:
-                parser.error("{0} name cannot be empty.".format(argument_type.capitalize()))
+                parser.error("{0} name cannot be empty".format(argument_type))
         return False
 
 
@@ -48,9 +49,14 @@ def parse_arguments() -> argparse.Namespace:
                                      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
     parser.add_argument("--version", action="version", version="%(prog)s {0}".format(next_action.__version__))
     parser.add_argument("-f", "--file", help="filename of the todo.txt file to read", type=str, default="todo.txt")
-    parser.add_argument("-n", "--number", metavar="N", help="number of next actions to show", type=int, default=1)
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument("-n", "--number", metavar="N", help="number of next actions to show", type=int, default=1)
+    group.add_argument("-a", "--all", help="show all next actions", action="store_true")
     parser.add_argument("contexts", metavar="@CONTEXT", help="show the next action in the specified contexts",
                         nargs="*", type=str, default=None, action=ContextProjectAction)
     parser.add_argument("projects", metavar="+PROJECT", help="show the next action for the specified projects",
                         nargs="*", type=str, default=None, action=ContextProjectAction)
-    return parser.parse_args()
+    namespace = parser.parse_args()
+    if namespace.all:
+        namespace.number = sys.maxsize
+    return namespace


Test patch:
diff --git a/tests/unittests/test_arguments.py b/tests/unittests/test_arguments.py
index 25d20ee..6fa5f60 100644
--- a/tests/unittests/test_arguments.py
+++ b/tests/unittests/test_arguments.py
@@ -11,7 +11,7 @@ from next_action.arguments import parse_arguments
 class ArgumentParserTest(unittest.TestCase):
     """ Unit tests for the argument parses. """
 
-    usage_message = "usage: next-action [-h] [--version] [-f FILE] [-n N] [@CONTEXT [@CONTEXT ...]] " \
+    usage_message = "usage: next-action [-h] [--version] [-f FILE] [-n N | -a] [@CONTEXT [@CONTEXT ...]] " \
                     "[+PROJECT [+PROJECT ...]]\n"
 
     @patch.object(sys, "argv", ["next-action"])
@@ -50,7 +50,7 @@ class ArgumentParserTest(unittest.TestCase):
         """ Test that the argument parser exits if the context is empty. """
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
         self.assertRaises(SystemExit, parse_arguments)
-        self.assertEqual([call(self.usage_message), call("next-action: error: Context name cannot be empty.\n")],
+        self.assertEqual([call(self.usage_message), call("next-action: error: context name cannot be empty\n")],
                          mock_stderr_write.call_args_list)
 
     @patch.object(sys, "argv", ["next-action", "+DogHouse"])
@@ -69,7 +69,7 @@ class ArgumentParserTest(unittest.TestCase):
         """ Test that the argument parser exits if the project is empty. """
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
         self.assertRaises(SystemExit, parse_arguments)
-        self.assertEqual([call(self.usage_message), call("next-action: error: Project name cannot be empty.\n")],
+        self.assertEqual([call(self.usage_message), call("next-action: error: project name cannot be empty\n")],
                          mock_stderr_write.call_args_list)
 
     @patch.object(sys, "argv", ["next-action", "+DogHouse", "@home", "+PaintHouse", "@weekend"])
@@ -84,7 +84,7 @@ class ArgumentParserTest(unittest.TestCase):
         """ Test that the argument parser exits if the option is faulty. """
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
         self.assertRaises(SystemExit, parse_arguments)
-        self.assertEqual([call(self.usage_message), call("next-action: error: Unrecognized argument 'home'.\n")],
+        self.assertEqual([call(self.usage_message), call("next-action: error: unrecognized argument: home\n")],
                          mock_stderr_write.call_args_list)
 
     @patch.object(sys, "argv", ["next-action"])
@@ -106,3 +106,19 @@ class ArgumentParserTest(unittest.TestCase):
         self.assertEqual([call(self.usage_message),
                           call("next-action: error: argument -n/--number: invalid int value: 'not_a_number'\n")],
                          mock_stderr_write.call_args_list)
+
+    @patch.object(sys, "argv", ["next-action", "--all"])
+    def test_all_actions(self):
+        """ Test that --all option also sets the number of actions to show to a very big number. """
+        self.assertTrue(parse_arguments().all)
+        self.assertEqual(sys.maxsize, parse_arguments().number)
+
+    @patch.object(sys, "argv", ["next-action", "--all", "--number", "3"])
+    @patch.object(sys.stderr, "write")
+    def test_all_and_number(self, mock_stderr_write):
+        """ Test that the argument parser exits if the both --all and --number are used. """
+        os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
+        self.assertRaises(SystemExit, parse_arguments)
+        self.assertEqual([call(self.usage_message),
+                          call("next-action: error: argument -n/--number: not allowed with argument -a/--all\n")],
+                         mock_stderr_write.call_args_list)
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
index 8f8e68f..1b80920 100644
--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -59,7 +59,7 @@ class CLITest(unittest.TestCase):
         """ Test the help message. """
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
         self.assertRaises(SystemExit, next_action)
-        self.assertEqual(call("""usage: next-action [-h] [--version] [-f FILE] [-n N] [@CONTEXT [@CONTEXT ...]] \
+        self.assertEqual(call("""usage: next-action [-h] [--version] [-f FILE] [-n N | -a] [@CONTEXT [@CONTEXT ...]] \
 [+PROJECT [+PROJECT ...]]
 
 Show the next action in your todo.txt
@@ -73,6 +73,7 @@ optional arguments:
   --version             show program's version number and exit
   -f FILE, --file FILE  filename of the todo.txt file to read (default: todo.txt)
   -n N, --number N      number of next actions to show (default: 1)
+  -a, --all             show all next actions (default: False)
 """),
                          mock_stdout_write.call_args_list[0])