Repository: fniessink/next-action

Issue:
Make --write-config-file take other command-line options into account
```console
$ next-action --write-config-file --style colourful --number 3
# Configuration file for Next-action. Edit the settings below as you like.\n"
file: ~/todo.txt
number: 3
style: colorful
```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b17215..906588e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [Unreleased] - 2018-06-30
+
+### Added
+
+- When generating a configuration file with `--write-config-file` add any other options on the command-line to the generated configuration file. Closes #78.
+
 ## [1.4.0] - 2018-06-25
 
 ### Added
diff --git a/README.md b/README.md
index 67e9fc7..a24da95 100644
--- a/README.md
+++ b/README.md
@@ -171,14 +171,14 @@ To limit the the tasks from which the next action is selected to actions with a
 
 ```console
 $ next-action @home --due
-(K) Pay July invoice @home due:2018-07-28
+(K) Pay July invoice @home due:2019-07-28
 ```
 
 Add a due date to select a next action from tasks due on or before that date:
 
 ```console
 $ next-action @home --due "june 2018"
-(L) Pay June invoice @home due:2018-06-28
+Nothing to do!
 ```
 
 To make sure you have no overdue actions, or work on overdue actions first, limit the tasks from which the next action is selected to overdue actions:
@@ -270,7 +270,10 @@ Not passing an argument to `--style` cancels the style that is configured in the
 
 ### Configuring *Next-action*
 
-In addition to specifying options on the command-line, you can also configure options in a configuration file. By default, *Next-action* tries to read a file called [.next-action.cfg](https://raw.githubusercontent.com/fniessink/next-action/master/docs/.next-action.cfg) in your home folder.
+In addition to specifying options on the command-line, you can also configure options in a configuration file.
+The configuration file format is [YAML](http://yaml.org). The options currently supported are which todo.txt files must be read, how many next actions should be shown, output styling, and context and/or project filters.
+
+#### Writing the configuration file
 
 To get started, you can tell *Next-action* to generate a configuration file with the default options:
 
@@ -280,11 +283,26 @@ $ next-action --write-config-file
 file: ~/todo.txt
 number: 1
 reference: multiple
-style: default
+style: native
 ```
 
 To make this the configuration that *Next-action* reads by default, redirect the output to `~/.next-action.cfg` like this: `next-action --write-config-file > ~/.next-action.cfg`.
 
+Any additional options specified on the command line are used to generate the configuration file:
+
+```console
+$ next-action --write-config-file --number 3 --file ~/tasks.txt --style fruity
+# Configuration file for Next-action. Edit the settings below as you like.
+file: ~/tasks.txt
+number: 3
+reference: multiple
+style: fruity
+```
+
+#### Reading the configuration file
+
+By default, *Next-action* tries to read a file called [.next-action.cfg](https://raw.githubusercontent.com/fniessink/next-action/master/docs/.next-action.cfg) in your home folder.
+
 If you want to use a configuration file that is not in the default location (`~/.next-action.cfg`), you'll need to explicitly specify its location:
 
 ```console
@@ -294,8 +312,6 @@ $ next-action --config-file docs/.next-action.cfg
 
 To skip reading the default configuration file, and also not read an alternative configuration file, use the `--config-file` option without arguments.
 
-The configuration file format is [YAML](http://yaml.org). The options currently supported are which todo.txt files must be read, how many next actions should be shown, output styling, and context and/or project filters.
-
 #### Configuring a default todo.txt
 
 A default todo.txt file to use can be specified like this in the configuration file:
@@ -403,14 +419,14 @@ There's two ways to help *Next-action* figure out what you mean. Either reverse
 
 ```console
 $ next-action @home --due
-(K) Pay July invoice @home due:2018-07-28
+(K) Pay July invoice @home due:2019-07-28
 ```
 
 Or use `--` to separate the option from the positional argument(s):
 
 ```console
 $ next-action --due -- @home
-(K) Pay July invoice @home due:2018-07-28
+(K) Pay July invoice @home due:2019-07-28
 ```
 
 ## Recent changes
@@ -429,9 +445,9 @@ To run the unit tests:
 
 ```console
 $ python -m unittest
-.....................................................................................................................................................................................................................................
+.........................................................................................................................................................................................................................................
 ----------------------------------------------------------------------
-Ran 229 tests in 2.728s
+Ran 233 tests in 2.206s
 
 OK
 ```
@@ -442,9 +458,9 @@ To create the unit test coverage report run the unit tests under coverage with:
 
 ```console
 $ coverage run --branch -m unittest
-.....................................................................................................................................................................................................................................
+.........................................................................................................................................................................................................................................
 ----------------------------------------------------------------------
-Ran 229 tests in 3.794s
+Ran 233 tests in 2.616s
 
 OK
 ```
@@ -456,7 +472,7 @@ $ coverage report --fail-under=100 --omit=".venv/*" --skip-covered
 Name    Stmts   Miss Branch BrPart  Cover
 -----------------------------------------
 -----------------------------------------
-TOTAL    1282      0    160      0   100%
+TOTAL    1316      0    162      0   100%
 
 25 files skipped due to complete coverage.
 ```
diff --git a/docs/todo.txt b/docs/todo.txt
index cd9b63c..cfc3281 100644
--- a/docs/todo.txt
+++ b/docs/todo.txt
@@ -5,8 +5,8 @@
 Get rid of old +DogHouse @home
 Borrow ladder from the neighbors +PaintHouse @home
 Buy flowers due:2018-02-14
-(L) Pay June invoice @home due:2018-06-28
-(K) Pay July invoice @home due:2018-07-28
+(L) Pay June invoice @home due:2019-06-28
+(K) Pay July invoice @home due:2019-07-28
 Buy groceries @store +DinnerParty before:meal
 Cook meal @home +DinnerParty id:meal due:2018-07-01
 Take out the garbage @home +DinnerParty due:2018-07-02
diff --git a/next_action/arguments/config.py b/next_action/arguments/config.py
index 5cf2f99..bf27d79 100644
--- a/next_action/arguments/config.py
+++ b/next_action/arguments/config.py
@@ -1,5 +1,6 @@
 """ Methods for reading, parsing, and validating Next-action configuration files. """
 
+import argparse
 import os
 import string
 import sys
@@ -26,11 +27,17 @@ def read_config_file(filename: str, default_filename: str, error: Callable[[str]
         error("can't parse {0}: {1}".format(filename, reason))
 
 
-def write_config_file() -> None:
+def write_config_file(namespace: argparse.Namespace) -> None:
     """ Generate a configuration file on standard out. """
     intro = "# Configuration file for Next-action. Edit the settings below as you like.\n"
-    config = yaml.dump(dict(file="~/todo.txt", number=1, reference="multiple", style="default"),
-                       default_flow_style=False)
+    options = dict(file=namespace.file[0] if len(namespace.file) == 1 else namespace.file,
+                   reference=namespace.reference, style=namespace.style or "default")
+    if namespace.number == sys.maxsize:
+        options["all"] = True
+    else:
+        options["number"] = namespace.number
+
+    config = yaml.dump(options, default_flow_style=False)
     sys.stdout.write(intro + config)
 
 
diff --git a/next_action/arguments/parser.py b/next_action/arguments/parser.py
index 682b5ae..aabbcdb 100644
--- a/next_action/arguments/parser.py
+++ b/next_action/arguments/parser.py
@@ -126,10 +126,10 @@ class NextActionArgumentParser(argparse.ArgumentParser):
         self.parse_remaining_args(remaining, namespace)
         if getattr(namespace, "config_file", self.get_default("config_file")) is not None:
             self.process_config_file(namespace)
+        self.fix_filenames(namespace)
         if namespace.write_config_file:
-            write_config_file()
+            write_config_file(namespace)
             self.exit()
-        self.fix_filenames(namespace)
         return namespace
 
     def parse_remaining_args(self, remaining: List[str], namespace: argparse.Namespace) -> None:


Test patch:
diff --git a/tests/unittests/arguments/test_config.py b/tests/unittests/arguments/test_config.py
index 42b5a9f..2cef50f 100644
--- a/tests/unittests/arguments/test_config.py
+++ b/tests/unittests/arguments/test_config.py
@@ -18,8 +18,8 @@ class ConfigTestCase(unittest.TestCase):
         os.environ['COLUMNS'] = "120"  # Fake that the terminal is wide enough.
 
 
-class ConfigFileTest(ConfigTestCase):
-    """ Unit tests for the config file. """
+class ReadConfigFileTest(ConfigTestCase):
+    """ Unit tests for reading the config file. """
 
     @patch.object(sys, "argv", ["next-action", "--config-file", "config.cfg"])
     @patch.object(config, "open", mock_open(read_data=""))
@@ -92,16 +92,61 @@ class ConfigFileTest(ConfigTestCase):
         parse_arguments()
         self.assertEqual([], mock_file_open.call_args_list)
 
+
+class WriteConfigFileTest(ConfigTestCase):
+    """ Unit tests for the write-config-file argument. """
+
     @patch.object(sys, "argv", ["next-action", "--write-config-file"])
     @patch.object(config, "open", mock_open(read_data=""))
     @patch.object(sys.stdout, "write")
-    def test_write_config(self, mock_stdout_write):
+    def test_default_file(self, mock_stdout_write):
         """ Test that a config file can be written to stdout. """
         self.assertRaises(SystemExit, parse_arguments)
         expected = "# Configuration file for Next-action. Edit the settings below as you like.\n"
         expected += "file: ~/todo.txt\nnumber: 1\nreference: multiple\nstyle: default\n"
         self.assertEqual([call(expected)], mock_stdout_write.call_args_list)
 
+    @patch.object(sys, "argv", ["next-action", "--write-config-file", "--number", "3", "--reference", "never",
+                                "--style", "fruity", "--file", "~/tasks.txt"])
+    @patch.object(config, "open", mock_open(read_data=""))
+    @patch.object(sys.stdout, "write")
+    def test_with_args(self, mock_stdout_write):
+        """ Test that the config file written to stdout includes the other arguments. """
+        self.assertRaises(SystemExit, parse_arguments)
+        expected = "# Configuration file for Next-action. Edit the settings below as you like.\n"
+        expected += "file: ~/tasks.txt\nnumber: 3\nreference: never\nstyle: fruity\n"
+        self.assertEqual([call(expected)], mock_stdout_write.call_args_list)
+
+    @patch.object(sys, "argv", ["next-action", "--write-config-file", "--file", "~/tasks.txt", "--file", "project.txt"])
+    @patch.object(config, "open", mock_open(read_data=""))
+    @patch.object(sys.stdout, "write")
+    def test_multiple_files(self, mock_stdout_write):
+        """ Test that the config file contains a list of files if multiple file arguments are passed. """
+        self.assertRaises(SystemExit, parse_arguments)
+        expected = "# Configuration file for Next-action. Edit the settings below as you like.\n"
+        expected += "file:\n- ~/tasks.txt\n- project.txt\nnumber: 1\nreference: multiple\nstyle: default\n"
+        self.assertEqual([call(expected)], mock_stdout_write.call_args_list)
+
+    @patch.object(sys, "argv", ["next-action", "--write-config-file", "--all"])
+    @patch.object(config, "open", mock_open(read_data=""))
+    @patch.object(sys.stdout, "write")
+    def test_show_all(self, mock_stdout_write):
+        """ Test that the config file contains "all" true" instead of a number. """
+        self.assertRaises(SystemExit, parse_arguments)
+        expected = "# Configuration file for Next-action. Edit the settings below as you like.\n"
+        expected += "all: true\nfile: ~/todo.txt\nreference: multiple\nstyle: default\n"
+        self.assertEqual([call(expected)], mock_stdout_write.call_args_list)
+
+    @patch.object(sys, "argv", ["next-action", "--write-config-file"])
+    @patch.object(config, "open", mock_open(read_data="number: 3"))
+    @patch.object(sys.stdout, "write")
+    def test_read_config(self, mock_stdout_write):
+        """ Test that the written config file contains the read config file. """
+        self.assertRaises(SystemExit, parse_arguments)
+        expected = "# Configuration file for Next-action. Edit the settings below as you like.\n"
+        expected += "file: ~/todo.txt\nnumber: 3\nreference: multiple\nstyle: default\n"
+        self.assertEqual([call(expected)], mock_stdout_write.call_args_list)
+
 
 class FilenameTest(ConfigTestCase):
     """ Unit tests for the config file parameter. """