Commit 49bfbc25 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'column-ui/master'

Conflicts:
	.travis.yml
	setup.py
parents 69951ef5 7a32e218
......@@ -12,5 +12,6 @@ exclude_lines =
if __name__ == .__main__.:
omit =
topydo/ui/*.py
topydo/commands/ExitCommand.py
topydo/lib/Version.py
......@@ -8,8 +8,9 @@ python:
install:
- "python -m pip install pip --upgrade"
- "pip install ."
- "pip install .[columns]"
- "pip install .[ical]"
- "pip install .[prompt_toolkit]"
- "pip install .[prompt]"
- "pip install .[test]"
- "pip install pylint"
- "pip install codecov"
......
......@@ -36,6 +36,9 @@ Simply install with:
(not supported for PyPy3).
* [prompt-toolkit][6] : For topydo's _prompt_ mode, which offers a shell-like
interface with auto-completion.
* [arrow][8] : Used to turn dates into a human readable version.
* [urwid][12] : For topydo's _columns_ mode, a TUI with columns for
your todo items.
* [backports.shutil_get_terminal_size][9] : Used to determine your terminal
window size. This function was
added to the standard library in
......@@ -61,3 +64,4 @@ Demo
[9]: https://github.com/chrippa/backports.shutil_get_terminal_size
[10]: https://dateutil.readthedocs.org/
[11]: https://github.com/testing-cabal/mock
[12]: https://github.com/urwid/urwid
......@@ -34,8 +34,9 @@ setup(
extras_require = {
':sys_platform=="win32"': ['colorama>=0.2.5'],
':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'],
'columns': ['urwid >= 1.3.0'],
'ical': ['icalendar'],
'prompt_toolkit': ['prompt_toolkit >= 0.53'],
'prompt': ['prompt_toolkit >= 0.53'],
'test': ['coverage', 'freezegun', 'green', ],
'test:python_version=="3.2"': ['mock'],
},
......
......@@ -25,9 +25,14 @@ class CommandTest(TopydoTest):
self.errors = ""
def out(self, p_output):
if p_output:
self.output += escape_ansi(p_output + "\n")
if isinstance(p_output, list) and p_output:
self.output += escape_ansi(
"\n".join([str(s) for s in p_output]) + "\n")
elif p_output:
self.output += str(p_output) + "\n"
def error(self, p_error):
if p_error:
self.errors += escape_ansi(p_error + "\n")
if isinstance(p_error, list) and p_error:
self.errors += escape_ansi(p_error + "\n") + "\n"
elif p_error:
self.errors += str(p_error) + "\n"
[column_keymap]
up = up
<Left> = prev_column
<Esc>d = delete_column
......@@ -52,4 +52,4 @@ def todolist_to_string(p_list):
def print_view(p_view):
printer = PrettyPrinter()
return printer.print_list(p_view.todos)
return "\n".join([str(s) for s in printer.print_list(p_view.todos)])
......@@ -30,8 +30,8 @@ class ArchiveCommandTest(CommandTest):
command = ArchiveCommand(todolist, archive)
command.execute()
self.assertTrue(todolist.is_dirty())
self.assertTrue(archive.is_dirty())
self.assertTrue(todolist.dirty)
self.assertTrue(archive.dirty)
self.assertEqual(todolist.print_todos(), "x Not complete\n(C) Active")
self.assertEqual(archive.print_todos(), "x 2014-10-19 Complete\nx 2014-10-20 Another one complete")
......
This diff is collapsed.
......@@ -45,8 +45,7 @@ class ConfigTest(TopydoTest):
""" Bad colour switch value. """
# boolean settings must first be typecast to integers, because all
# strings evaulate to 'True'
self.assertEqual(config("test/data/ConfigTest4.conf").colors(),
bool(int(config().defaults["topydo"]["colors"])))
self.assertEqual(config("test/data/ConfigTest4.conf").colors(), 16)
def test_config06(self):
""" Bad auto creation date switch value. """
......@@ -83,62 +82,76 @@ class ConfigTest(TopydoTest):
self.assertEqual(config("test/data/ConfigTest4.conf").append_parent_contexts(),
bool(int(config().defaults["dep"]["append_parent_contexts"])))
@skip("Error checking not yet implemented")
def test_config14(self):
""" Bad priority color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").priority_colors(),
config().defaults["colorscheme"]["priority_colors"])
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('A').color, 6)
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('B').color, 3)
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('C').color, 4)
@skip("Error checking not yet implemented")
def test_config15(self):
""" Bad project color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").project_color(),
config().defaults["colorscheme"]["project_color"])
self.assertTrue(config("test/data/ConfigTest4.conf").project_color().is_neutral())
@skip("Error checking not yet implemented")
def test_config16(self):
""" Bad context color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").context_color(),
config().defaults["colorscheme"]["context_color"])
self.assertTrue(config("test/data/ConfigTest4.conf").context_color().is_neutral())
@skip("Error checking not yet implemented")
def test_config17(self):
""" Bad metadata color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").metadata_color(),
config().defaults["colorscheme"]["metadata_color"])
self.assertTrue(config("test/data/ConfigTest4.conf").metadata_color().is_neutral())
@skip("Error checking not yet implemented")
def test_config18(self):
""" Bad link color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").link_color(),
config().defaults["colorscheme"]["link_color"])
self.assertTrue(config("test/data/ConfigTest4.conf").link_color().is_neutral())
@skip("Test not yet implemented")
# the test needs to be of the internal function _str_to_dict
def test_config19(self):
""" No priority color value. """
self.assertEqual(config("test/data/ConfigTest4.conf").priority_colors(),
config().defaults["colorscheme"]["priority_colors"])
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('A').color, 6)
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('B').color, 3)
self.assertEqual(config("test/data/ConfigTest4.conf").priority_color('C').color, 4)
def test_config20(self):
""" No project color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").project_color(),
config().defaults["colorscheme"]["project_color"])
self.assertEqual(config("test/data/ConfigTest5.conf").project_color().color, 1)
def test_config21(self):
""" No context color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").context_color(),
config().defaults["colorscheme"]["context_color"])
self.assertEqual(config("test/data/ConfigTest5.conf").context_color().color, 5)
def test_config22(self):
""" No metadata color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").metadata_color(),
config().defaults["colorscheme"]["metadata_color"])
self.assertEqual(config("test/data/ConfigTest5.conf").metadata_color().color, 2)
def test_config23(self):
""" No link color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").link_color(),
config().defaults["colorscheme"]["link_color"])
self.assertEqual(config("test/data/ConfigTest5.conf").link_color().color, 6)
def test_config24(self):
""" column_keymap test. """
keymap, keystates = config("test/data/ConfigTest6.conf").column_keymap()
self.assertEqual(keymap['pp'], 'postpone')
self.assertEqual(keymap['ps'], 'postpone_s')
self.assertEqual(keymap['pr'], 'pri')
self.assertEqual(keymap['pra'], 'cmd pri {} a')
self.assertIn('p', keystates)
self.assertIn('g', keystates)
self.assertIn('pp', keystates)
self.assertIn('ps', keystates)
self.assertIn('pr', keystates)
self.assertEqual(keymap['up'], 'up')
self.assertIn('u', keystates)
self.assertEqual(keymap['<Left>'], 'prev_column')
self.assertNotIn('<Lef', keystates)
self.assertEqual(keymap['<Esc>d'], 'delete_column')
self.assertNotIn('<Esc', keystates)
self.assertIn('<Esc>', keystates)
if __name__ == '__main__':
unittest.main()
......@@ -48,7 +48,7 @@ class DeleteCommandTest(CommandTest):
_no_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).source(), "Bar")
self.assertEqual(self.output, "| 2| Bar p:1\nRemoved: Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -58,7 +58,7 @@ class DeleteCommandTest(CommandTest):
_no_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).source(), "Bar")
self.assertEqual(self.output, "| 2| Bar p:1\nRemoved: Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -68,7 +68,7 @@ class DeleteCommandTest(CommandTest):
_yes_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 2)
self.assertEqual(self.output,
"| 2| Bar p:1\nRemoved: Bar\nRemoved: Foo\n")
......@@ -79,7 +79,7 @@ class DeleteCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 3) # force won't delete subtasks
self.assertEqual(self.output, "| 2| Bar p:1\nRemoved: Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -89,7 +89,7 @@ class DeleteCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 3) # force won't delete subtasks
self.assertEqual(self.output, "| 2| Bar p:1\nRemoved: Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -98,7 +98,7 @@ class DeleteCommandTest(CommandTest):
command = DeleteCommand(["2"], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).source(), "Foo")
self.assertEqual(self.output, "Removed: Bar p:1\nThe following todo item(s) became active:\n| 1| Foo\n")
self.assertEqual(self.errors, "")
......@@ -107,7 +107,7 @@ class DeleteCommandTest(CommandTest):
command = DeleteCommand(["99"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -115,7 +115,7 @@ class DeleteCommandTest(CommandTest):
command = DeleteCommand(["A"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -159,7 +159,7 @@ class DeleteCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given: 99.\n")
......@@ -169,7 +169,7 @@ class DeleteCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given: 99.\nInvalid todo number given: A.\n")
......@@ -181,7 +181,7 @@ class DeleteCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -193,7 +193,7 @@ class DeleteCommandTest(CommandTest):
result = "Removed: a @test with due:2015-06-03\nRemoved: a @test with +project\n"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 2)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -203,7 +203,7 @@ class DeleteCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Removed: a @test with due:2015-06-03\n")
self.assertEqual(self.errors, "")
......@@ -212,7 +212,7 @@ class DeleteCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_del4(self):
""" Remove only relevant todo items. """
......@@ -222,7 +222,7 @@ class DeleteCommandTest(CommandTest):
result = "Foo"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 1)
self.assertEqual(self.todolist.print_todos(), result)
......@@ -232,14 +232,14 @@ class DeleteCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.count(), 0)
def test_empty(self):
command = DeleteCommand([], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......
......@@ -40,7 +40,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(4).has_tag('p', '1'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -50,7 +50,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(4).has_tag('p', '1'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -60,7 +60,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -69,7 +69,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -77,7 +77,7 @@ class DepCommandTest(CommandTest):
command = DepCommand(["add", "1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -86,7 +86,7 @@ class DepCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(4).has_tag('p', '1'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -96,7 +96,7 @@ class DepCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(1).has_tag('p', '2'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -106,7 +106,7 @@ class DepCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(1).has_tag('p', '2'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -116,7 +116,7 @@ class DepCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(4).has_tag('p', '1'))
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -190,7 +190,7 @@ class DepCommandTest(CommandTest):
command = DepCommand(p_args, self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(1).has_tag('id', '1'))
self.assertFalse(self.todolist.todo(3).has_tag('p', '1'))
self.assertEqual(self.output, "")
......@@ -213,7 +213,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -222,7 +222,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -230,7 +230,7 @@ class DepCommandTest(CommandTest):
command = DepCommand(["rm", "1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -239,7 +239,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "| 2| Bar p:1\n| 3| Baz p:1\n")
self.assertEqual(self.errors, "")
......@@ -248,7 +248,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -257,7 +257,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "| 1| Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -266,7 +266,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -274,7 +274,7 @@ class DepCommandTest(CommandTest):
command = DepCommand(["ls", "1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -282,7 +282,7 @@ class DepCommandTest(CommandTest):
command = DepCommand(["ls"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -291,7 +291,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -300,7 +300,7 @@ class DepCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertFalse(self.output)
self.assertFalse(self.errors)
self.assertFalse(self.todolist.todo(5).has_tag('p', '99'))
......@@ -317,7 +317,7 @@ class DepCommandTest(CommandTest):
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_no_subsubcommand(self):
command = DepCommand([], self.todolist, self.out, self.error)
......@@ -325,7 +325,7 @@ class DepCommandTest(CommandTest):
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_help(self):
command = DepCommand(["help"], self.todolist, self.out, self.error)
......
......@@ -39,7 +39,7 @@ class DepriCommandTest(CommandTest):
command = DepriCommand(["1"], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).priority(), None)
self.assertEqual(self.output, "Priority removed.\n| 1| Foo\n")
self.assertEqual(self.errors, "")
......@@ -48,7 +48,7 @@ class DepriCommandTest(CommandTest):
command = DepriCommand(["2"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.todolist.todo(2).priority(), None)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "")
......@@ -57,7 +57,7 @@ class DepriCommandTest(CommandTest):
command = DepriCommand(["Foo"], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).priority(), None)
self.assertEqual(self.output, "Priority removed.\n| 1| Foo\n")
self.assertEqual(self.errors, "")
......@@ -67,7 +67,7 @@ class DepriCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.todo(1).priority(), None)
self.assertEqual(self.todolist.todo(3).priority(), None)
self.assertEqual(self.output, "Priority removed.\n| 1| Foo\nPriority removed.\n| 3| Baz\n")
......@@ -80,7 +80,7 @@ class DepriCommandTest(CommandTest):
result = "Priority removed.\n| 4| a @test with due:2015-06-03\nPriority removed.\n| 5| a @test with +project p:1\n"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -91,7 +91,7 @@ class DepriCommandTest(CommandTest):
result = "Priority removed.\n| 4| a @test with due:2015-06-03\n"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -100,7 +100,7 @@ class DepriCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_depri4(self):
""" Don't remove priority from unrelevant todo items. """
......@@ -108,7 +108,7 @@ class DepriCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_depri5(self):
""" Force unprioritizing unrelevant items with additional -x flag. """
......@@ -116,7 +116,7 @@ class DepriCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Priority removed.\n| 6| Bax id:1\n")
self.assertEqual(self.errors, "")
......@@ -124,7 +124,7 @@ class DepriCommandTest(CommandTest):
command = DepriCommand(["99"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -133,7 +133,7 @@ class DepriCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given: 99.\n")
......@@ -142,7 +142,7 @@ class DepriCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given: 99.\nInvalid todo number given: FooBar.\n")
......@@ -154,7 +154,7 @@ class DepriCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -163,7 +163,7 @@ class DepriCommandTest(CommandTest):
command = DepriCommand([], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......
......@@ -61,7 +61,7 @@ class DoCommandTest(CommandTest):
_no_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(3).is_completed())
self.assertEqual(self.output,
"Completed: x {} Baz p:1\n".format(self.today))
......@@ -77,7 +77,7 @@ class DoCommandTest(CommandTest):
for number in [1, 2, 3]:
self.assertTrue(self.todolist.todo(number).is_completed())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertFalse(self.todolist.todo(4).is_completed())
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -89,7 +89,7 @@ class DoCommandTest(CommandTest):
result = "| 2| Bar p:1\n| 3| Baz p:1\nCompleted: x {} Foo id:1\n".format(self.today)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(1).is_completed())
self.assertFalse(self.todolist.todo(2).is_completed())
self.assertFalse(self.todolist.todo(3).is_completed())
......@@ -130,7 +130,7 @@ class DoCommandTest(CommandTest):
command = DoCommand(p_flags, self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.errors, "")
self.assertEqual(self.todolist.count(), 12)
......@@ -162,7 +162,7 @@ class DoCommandTest(CommandTest):
command = DoCommand(["99"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -170,7 +170,7 @@ class DoCommandTest(CommandTest):
command = DoCommand(["AAA"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -179,7 +179,7 @@ class DoCommandTest(CommandTest):
_yes_prompt)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -209,7 +209,7 @@ class DoCommandTest(CommandTest):
command = DoCommand(["5"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.todolist.todo(5).completion_date(),
date(2014, 10, 18))
self.assertFalse(self.output)
......@@ -219,7 +219,7 @@ class DoCommandTest(CommandTest):
command = DoCommand(["baz"], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertTrue(self.todolist.todo(3).is_completed())
self.assertEqual(self.output,
"Completed: x {} Baz p:1\n".format(self.today))
......@@ -230,7 +230,7 @@ class DoCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Completed: x 2014-11-18 Baz p:1\n")
self.assertEqual(self.errors, "")
......@@ -239,7 +239,7 @@ class DoCommandTest(CommandTest):
self.error, _yes_prompt)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 2| Bar p:1\n| 3| Baz p:1\nCompleted: x 2014-11-18 Bar p:1\nCompleted: x 2014-11-18 Baz p:1\nCompleted: x 2014-11-18 Foo id:1\n")
self.assertEqual(self.errors, "")
......@@ -248,7 +248,7 @@ class DoCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Completed: x 2014-11-18 Baz p:1\n")
self.assertEqual(self.errors, "")
......@@ -257,7 +257,7 @@ class DoCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Completed: x {} Baz p:1\n".format(self.today))
self.assertEqual(self.errors, "")
......@@ -271,7 +271,7 @@ class DoCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 12| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEqual(self.errors, "")
......@@ -285,7 +285,7 @@ class DoCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 12| {today} Recurring! rec:1d due:{today}\nCompleted: x {yesterday} Recurring! rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEqual(self.errors, "")
......@@ -298,7 +298,7 @@ class DoCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 12| {today} Strict due:2014-01-02 rec:1d\nCompleted: x {yesterday} Strict due:2014-01-01 rec:1d\n".format(today=self.today, yesterday=self.yesterday))
self.assertEqual(self.errors, "")
......@@ -310,7 +310,7 @@ class DoCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Completed: x {} Baz p:1\n".format(self.yesterday))
self.assertEqual(self.errors, "")
......@@ -323,7 +323,7 @@ class DoCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Completed: x {} Baz p:1\n".format(self.yesterday))
self.assertEqual(self.errors, "")
......@@ -385,7 +385,7 @@ class DoCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -394,7 +394,7 @@ class DoCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Completed: x {t} a @test with due:2015-06-03\nCompleted: x {t} a @test with +project\n".format(t=self.today))
self.assertEqual(self.errors, "")
......@@ -403,7 +403,7 @@ class DoCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Completed: x {} a @test with due:2015-06-03\n".format(self.today))
self.assertEqual(self.errors, "")
......@@ -412,7 +412,7 @@ class DoCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_do4(self):
""" Don't do anything with unrelevant todo items. """
......@@ -420,7 +420,7 @@ class DoCommandTest(CommandTest):
None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_do5(self):
""" Force marking unrelevant items as done with additional -x flag. """
......@@ -430,7 +430,7 @@ class DoCommandTest(CommandTest):
result = "| 2| Bar p:1\n| 3| Baz p:1\nCompleted: x {t} Bar p:1\nCompleted: x {t} Baz p:1\nCompleted: x {t} Foo id:1\n".format(t=self.today)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -450,7 +450,7 @@ class DoCommandTest(CommandTest):
command = DoCommand([], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......
......@@ -56,7 +56,7 @@ class EditCommandTest(CommandTest):
command.execute()
self.assertEqual(self.errors, "")
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.todolist.print_todos(), u"Bar p:1 @test\nBaz @test\nFo\u00f3B\u0105\u017a\nFoo id:1")
@mock.patch('topydo.commands.EditCommand._is_edited')
......@@ -72,7 +72,7 @@ class EditCommandTest(CommandTest):
None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.errors, "")
self.assertEqual(self.todolist.print_todos(), u"Foo id:1\nBaz @test\nFo\u00f3B\u0105\u017a\nLazy Cat")
......@@ -82,7 +82,7 @@ class EditCommandTest(CommandTest):
None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.errors, "Invalid todo number given.\n")
def test_edit04(self):
......@@ -91,7 +91,7 @@ class EditCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.errors, "Invalid todo number given: 5.\n")
def test_edit05(self):
......@@ -102,7 +102,7 @@ class EditCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -119,7 +119,7 @@ class EditCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.errors, "")
self.assertEqual(self.todolist.print_todos(),
u"Foo id:1\nBar p:1 @test\nBaz @test\nLazy Cat")
......@@ -137,7 +137,7 @@ class EditCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.errors, "Editing aborted. Nothing to do.\n")
self.assertEqual(self.todolist.print_todos(), u"Foo id:1\nBar p:1 @test\nBaz @test\nFo\u00f3B\u0105\u017a")
......@@ -158,7 +158,7 @@ class EditCommandTest(CommandTest):
expected = u"| 3| Lazy Cat\n| 4| Lazy Dog\n"
self.assertEqual(self.errors, "")
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, expected)
self.assertEqual(self.todolist.print_todos(), u"Foo id:1\nFo\u00f3B\u0105\u017a\nLazy Cat\nLazy Dog")
......
This diff is collapsed.
......@@ -678,7 +678,26 @@ C -
"""
self.assertEqual(self.output, result)
def test_list_format45(self):
@mock.patch('topydo.lib.ListFormat.get_terminal_size')
def test_list_format45(self, mock_terminal_size):
""" Colorblocks should not affect truncating or right_alignment. """
self.maxDiff = None
mock_terminal_size.return_value = self.terminal_size(100, 25)
config(p_overrides={('ls', 'list_format'): '%z|%I| %x %p %S %k\\t%{(}h{)}'})
command = ListCommand(["-x"], self.todolist, self.out, self.error)
command.execute()
result = u""" | 1| D Bar @Context1 +Project2 (due a month ago, started a month ago)
| 2| Z Lorem ipsum dolorem sit amet. Red @fox +jumpe... lazy:bar (due in 2 days, starts in a day)
| 3| C Foo @Context2 Not@Context +Project1 Not+Project
| 4| C Baz @Context1 +Project1 key:value
| 5| Drink beer @ home
| 6| x 2014-12-12 Completed but with date:2014-12-12
"""
self.assertEqual(self.output, result)
def test_list_format46(self):
command = ListCommand(["-x", "-F", "%r"], self.todolist, self.out, self.error)
command.execute()
......
......@@ -49,7 +49,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 1| Foo due:{}\n".format(due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -61,7 +61,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 2| Bar due:{}\n".format(due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -73,7 +73,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 2| Bar due:{}\n".format(due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -85,7 +85,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 3| Baz due:{} t:{}\n".format(due.isoformat(), self.start.isoformat()))
self.assertEqual(self.errors, "")
......@@ -97,7 +97,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
start = self.start + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
# pylint: disable=E1103
self.assertEqual(self.output, "| 3| Baz due:{} t:{}\n".format(due.isoformat(), start.isoformat()))
self.assertEqual(self.errors, "")
......@@ -109,7 +109,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 4| Past due:{}\n".format(due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -121,7 +121,7 @@ class PostponeCommandTest(CommandTest):
due = self.future + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
# pylint: disable=E1103
self.assertEqual(self.output, "| 5| Future due:{} t:{}\n".format(due.isoformat(), self.future_start.isoformat()))
self.assertEqual(self.errors, "")
......@@ -134,7 +134,7 @@ class PostponeCommandTest(CommandTest):
due = self.future + timedelta(7)
start = self.future_start + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
# pylint: disable=E1103
self.assertEqual(self.output, "| 5| Future due:{} t:{}\n".format(due.isoformat(), start.isoformat()))
self.assertEqual(self.errors, "")
......@@ -144,7 +144,7 @@ class PostponeCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid date pattern given.\n")
......@@ -153,7 +153,7 @@ class PostponeCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -162,7 +162,7 @@ class PostponeCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -170,7 +170,7 @@ class PostponeCommandTest(CommandTest):
command = PostponeCommand(["1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -181,7 +181,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 1| Foo due:{}\n".format(due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -193,7 +193,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 1| Foo due:{}\n| 2| Bar due:{}\n".format(due.isoformat(), due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -204,7 +204,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 1| Foo due:{}\n| 2| Bar due:{}\n".format(due.isoformat(), due.isoformat()))
self.assertEqual(self.errors, "")
......@@ -216,7 +216,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
start = self.start + timedelta(7)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
# pylint: disable=E1103
self.assertEqual(self.output, "| 2| Bar due:{}\n| 3| Baz due:{} t:{}\n".format(due.isoformat(), due.isoformat(), start.isoformat()))
self.assertEqual(self.errors, "")
......@@ -226,7 +226,7 @@ class PostponeCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid date pattern given.\n")
......@@ -235,7 +235,7 @@ class PostponeCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given: 99.\nInvalid todo number given: 123.\n")
......@@ -244,7 +244,7 @@ class PostponeCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given: Zoo.\nInvalid todo number given: 99.\nInvalid todo number given: 123.\n")
......@@ -254,7 +254,7 @@ class PostponeCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -267,7 +267,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(14)
result = "| 2| Bar due:{d}\n| 3| Baz due:{d} t:{s}\n".format(d=due.isoformat(), s=self.start.isoformat())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -282,7 +282,7 @@ class PostponeCommandTest(CommandTest):
result = "| 3| Baz due:{} t:{}\n".format(due.isoformat(),
self.start.isoformat())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -291,7 +291,7 @@ class PostponeCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_postpone4(self):
""" Don't postpone unrelevant todo items. """
......@@ -299,7 +299,7 @@ class PostponeCommandTest(CommandTest):
self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_postpone5(self):
""" Force postponing unrelevant items with additional -x flag. """
......@@ -310,7 +310,7 @@ class PostponeCommandTest(CommandTest):
due = self.today + timedelta(7)
result = "| 6| FutureStart t:{} due:{}\n".format(self.future.isoformat(), due.isoformat())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......
......@@ -39,7 +39,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Priority changed from A to B\n| 1| (B) Foo\n")
self.assertEqual(self.errors, "")
......@@ -49,7 +49,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Priority set to Z.\n| 2| (Z) Bar\n")
self.assertEqual(self.errors, "")
......@@ -58,7 +58,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Priority changed from A to B\n| 1| (B) Foo\n")
self.assertEqual(self.errors, "")
......@@ -68,7 +68,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "| 1| (A) Foo\n")
self.assertEqual(self.errors, "")
......@@ -77,7 +77,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Priority changed from A to C\n| 1| (C) Foo\nPriority set to C.\n| 2| (C) Bar\n")
self.assertEqual(self.errors, "")
......@@ -87,7 +87,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Priority changed from A to C\n| 1| (C) Foo\nPriority set to C.\n| 2| (C) Bar\n")
self.assertEqual(self.errors, "")
......@@ -97,7 +97,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "Priority changed from A to C\n| 1| (C) Foo\nPriority set to C.\n| 2| (C) Bar\n")
self.assertEqual(self.errors, "")
......@@ -108,7 +108,7 @@ class PriorityCommandTest(CommandTest):
result = "Priority changed from B to C\n| 3| (C) a @test with due:2015-06-03\nPriority set to C.\n| 4| (C) a @test with +project p:1\n"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -119,7 +119,7 @@ class PriorityCommandTest(CommandTest):
result = "Priority changed from B to C\n| 3| (C) a @test with due:2015-06-03\n"
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, result)
self.assertEqual(self.errors, "")
......@@ -129,7 +129,7 @@ class PriorityCommandTest(CommandTest):
None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_prio4(self):
""" Don't prioritize unrelevant todo items. """
......@@ -137,7 +137,7 @@ class PriorityCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_expr_prio5(self):
""" Force prioritizing unrelevant items with additional -x flag. """
......@@ -145,7 +145,7 @@ class PriorityCommandTest(CommandTest):
self.error, None)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"Priority set to D.\n| 5| (D) Baz id:1\n")
self.assertEqual(self.errors, "")
......@@ -155,7 +155,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given.\n")
......@@ -164,7 +164,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given: 99.\n")
......@@ -173,7 +173,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number given: 98.\nInvalid todo number given: 99.\n")
......@@ -182,7 +182,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid priority given.\n")
......@@ -190,7 +190,7 @@ class PriorityCommandTest(CommandTest):
command = PriorityCommand(["A"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -198,7 +198,7 @@ class PriorityCommandTest(CommandTest):
command = PriorityCommand(["1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......@@ -210,7 +210,7 @@ class PriorityCommandTest(CommandTest):
self.todolist, self.out, self.error, None)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors,
u"Invalid todo number given: Fo\u00d3B\u0105r.\n")
......@@ -224,7 +224,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid priority given.\n")
......@@ -237,7 +237,7 @@ class PriorityCommandTest(CommandTest):
self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid priority given.\n")
......@@ -245,7 +245,7 @@ class PriorityCommandTest(CommandTest):
command = PriorityCommand([], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, command.usage() + "\n")
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2016 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from freezegun import freeze_time
import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config
from topydo.lib.ProgressColor import progress_color
from topydo.lib.Todo import Todo
def set_256_colors():
config(p_overrides={('topydo', 'colors'): '256'})
@freeze_time('2016, 01, 01')
class ProgressColorTest(TopydoTest):
def test_progress1(self):
""" Test progress of task with no length """
color = progress_color(Todo('Foo'))
self.assertEqual(color.color, 2)
def test_progress2(self):
""" Test progress of task with no length (but with creation date). """
color = progress_color(Todo('2016-02-11 Foo'))
self.assertEqual(color.color, 2)
def test_progress3(self):
""" Test progress of task with no length """
set_256_colors()
color = progress_color(Todo('Foo'))
self.assertEqual(color.color, 22)
def test_progress4(self):
""" Test progress of task with no length (but with creation date). """
set_256_colors()
color = progress_color(Todo('2016-02-11 Foo'))
self.assertEqual(color.color, 22)
def test_progress5(self):
""" Test overdue tasks """
color = progress_color(Todo('Foo due:2015-12-31'))
self.assertEqual(color.color, 1)
def test_progress6(self):
""" Test overdue tasks """
set_256_colors()
color = progress_color(Todo('Foo due:2015-12-31'))
self.assertEqual(color.color, 196)
def test_progress7(self):
""" Due today """
color = progress_color(Todo('Foo due:2016-01-01'))
self.assertEqual(color.color, 3)
def test_progress8(self):
""" Due today (256) """
set_256_colors()
color = progress_color(Todo('Foo due:2016-01-01'))
self.assertEqual(color.color, 202)
def test_progress9(self):
""" Due tomorrow """
color = progress_color(Todo('Foo due:2016-01-02'))
# a length of 14 days is assumed
self.assertEqual(color.color, 3)
def test_progress10(self):
set_256_colors()
color = progress_color(Todo('Foo due:2016-01-02'))
# a length of 14 days is assumed
self.assertEqual(color.color, 208)
def test_progress11(self):
""" Due tomorrow (creation date) """
color = progress_color(Todo('2016-01-01 Foo due:2016-01-02'))
# a length of 14 days is assumed
self.assertEqual(color.color, 2)
def test_progress12(self):
""" Due tomorrow (creation date) """
set_256_colors()
color = progress_color(Todo('2016-01-01 Foo due:2016-01-02'))
self.assertEqual(color.color, 22)
def test_progress13(self):
""" Due tomorrow (recurrence) """
color = progress_color(Todo('Foo due:2016-01-02 rec:1d'))
self.assertEqual(color.color, 2)
def test_progress14(self):
""" Due tomorrow (recurrence) """
set_256_colors()
color = progress_color(Todo('Foo due:2016-01-02 rec:1d'))
self.assertEqual(color.color, 22)
def test_progress15(self):
""" Due tomorrow (creation date + recurrence) """
color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 rec:1d'))
self.assertEqual(color.color, 2)
def test_progress16(self):
""" Due tomorrow (creation date + recurrence) """
set_256_colors()
color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 rec:1d'))
self.assertEqual(color.color, 22)
def test_progress17(self):
""" Due tomorrow (creation date + recurrence + start date) """
color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 rec:1d t:2016-01-02'))
self.assertEqual(color.color, 2)
def test_progress18(self):
""" Due tomorrow (creation date + recurrence + start date) """
set_256_colors()
color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 rec:1d t:2016-01-02'))
self.assertEqual(color.color, 22)
def test_progress19(self):
""" Due tomorrow (creation date + strict recurrence + start date) """
color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 rec:+1d t:2016-01-02'))
self.assertEqual(color.color, 2)
def test_progress20(self):
""" Due tomorrow (creation date + strict recurrence + start date) """
set_256_colors()
color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 rec:+1d t:2016-01-02'))
self.assertEqual(color.color, 22)
def test_progress21(self):
""" Due tomorrow (creation date + start date) """
color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 t:2016-01-02'))
self.assertEqual(color.color, 2)
def test_progress22(self):
""" Due tomorrow (creation date + start date) """
set_256_colors()
color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 t:2016-01-02'))
self.assertEqual(color.color, 22)
def test_progress23(self):
""" Due tomorrow (creation date + start date) """
color = progress_color(Todo('2016-12-01 Foo due:2016-01-02 t:2015-12-31'))
self.assertEqual(color.color, 10)
def test_progress24(self):
""" Due tomorrow (creation date + start date) """
set_256_colors()
color = progress_color(Todo('2015-12-01 Foo due:2016-01-02 t:2015-12-31'))
self.assertEqual(color.color, 118)
def test_progress25(self):
""" Start date after due date """
color = progress_color(Todo('Foo due:2016-01-02 t:2016-01-03'))
# a length of 14 days is assumed
self.assertEqual(color.color, 3)
def test_progress26(self):
""" Start date after due date """
set_256_colors()
color = progress_color(Todo('Foo due:2016-01-02 t:2016-01-03'))
# a length of 14 days is assumed
self.assertEqual(color.color, 208)
def test_progress27(self):
""" Creation date after due date """
set_256_colors()
color = progress_color(Todo('2016-01-03 Foo due:2016-01-02'))
# a length of 14 days is assumed
self.assertEqual(color.color, 208)
if __name__ == '__main__':
unittest.main()
......@@ -42,7 +42,7 @@ class TagCommandTest(CommandTest):
self.assertEqual(self.todolist.todo(1).source(), "Foo due:2014-10-22")
self.assertEqual(self.output, "| 1| Foo due:2014-10-22\n")
self.assertEqual(self.errors, "")
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_add_tag2(self):
command = TagCommand(["Foo", "due", "2014-10-22"], self.todolist,
......@@ -52,7 +52,7 @@ class TagCommandTest(CommandTest):
self.assertEqual(self.todolist.todo(1).source(), "Foo due:2014-10-22")
self.assertEqual(self.output, "| 1| Foo due:2014-10-22\n")
self.assertEqual(self.errors, "")
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_add_tag3(self):
command = TagCommand(["-a", "2", "due", "2014-10-19"], self.todolist,
......@@ -64,14 +64,14 @@ class TagCommandTest(CommandTest):
self.assertEqual(self.output,
"| 2| Bar due:2014-10-22 due:2014-10-19\n")
self.assertEqual(self.errors, "")
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_add_tag4(self):
command = TagCommand(["Foox", "due", "2014-10-22"], self.todolist,
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertFalse(self.output)
self.assertEqual(self.errors, "Invalid todo number.\n")
......@@ -91,7 +91,7 @@ class TagCommandTest(CommandTest):
self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "| 3| Baz due:2014-10-20\n")
self.assertEqual(self.errors, "")
......@@ -100,7 +100,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "all")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-20\n")
self.assertEqual(self.errors, "")
......@@ -109,7 +109,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "1")
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -118,7 +118,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "2")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-20\n")
self.assertEqual(self.errors, "")
......@@ -127,7 +127,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "")
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -136,7 +136,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "99")
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -145,7 +145,7 @@ class TagCommandTest(CommandTest):
self.out, self.error, lambda t: "99")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
"| 4| Fnord due:2014-10-20 due:2014-10-20\n")
self.assertEqual(self.errors, "")
......@@ -156,7 +156,7 @@ class TagCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 3| Baz due:2015-11-19\n")
self.assertEqual(self.errors, "")
......@@ -169,7 +169,7 @@ class TagCommandTest(CommandTest):
self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 3| Baz due:2014-10-20 foo:today\n")
self.assertEqual(self.errors, "")
......@@ -177,7 +177,7 @@ class TagCommandTest(CommandTest):
command = TagCommand(["1", "due"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "| 1| Foo\n")
self.assertEqual(self.errors, "")
......@@ -185,7 +185,7 @@ class TagCommandTest(CommandTest):
command = TagCommand(["2", "due"], self.todolist, self.out, self.error)
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 2| Bar\n")
self.assertEqual(self.errors, "")
......@@ -194,7 +194,7 @@ class TagCommandTest(CommandTest):
self.error, lambda t: "all")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output,
" 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord\n")
self.assertEqual(self.errors, "")
......@@ -204,7 +204,7 @@ class TagCommandTest(CommandTest):
lambda t: "1")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -213,7 +213,7 @@ class TagCommandTest(CommandTest):
lambda t: "99")
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -222,7 +222,7 @@ class TagCommandTest(CommandTest):
lambda t: "A")
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, " 1. 2014-10-20\n 2. 2014-10-22\n| 4| Fnord due:2014-10-20 due:2014-10-22\n")
self.assertEqual(self.errors, "")
......@@ -230,7 +230,7 @@ class TagCommandTest(CommandTest):
command = TagCommand(["5", "due"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number.\n")
......@@ -238,7 +238,7 @@ class TagCommandTest(CommandTest):
command = TagCommand(["A", "due"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number.\n")
......@@ -247,7 +247,7 @@ class TagCommandTest(CommandTest):
self.error, lambda t: "A")
command.execute()
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertEqual(self.output, "| 4| Fnord\n")
self.assertEqual(self.errors, "")
......@@ -255,7 +255,7 @@ class TagCommandTest(CommandTest):
command = TagCommand(["4"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
......
......@@ -40,12 +40,12 @@ class TodoListTester(TopydoTest):
def test_contexts(self):
self.assertEqual(set(['Context1', 'Context2']),
self.todolist.contexts())
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_projects(self):
self.assertEqual(set(['Project1', 'Project2']),
self.todolist.projects())
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_add1(self):
text = "(C) Adding a new task @Context3 +Project3"
......@@ -58,7 +58,7 @@ class TodoListTester(TopydoTest):
self.assertEqual(set(['Context1', 'Context2', 'Context3']),
self.todolist.contexts())
self.assertEqual(self.todolist.number(todo), 6)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_add2(self):
text = str(self.todolist)
......@@ -101,7 +101,7 @@ class TodoListTester(TopydoTest):
self.assertEqual(self.todolist.todo(2).source(),
"(C) Baz @Context1 +Project1 key:value")
self.assertEqual(self.todolist.count(), count - 1)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
self.assertRaises(InvalidTodoException, self.todolist.number, todo)
def test_delete2(self):
......@@ -112,7 +112,7 @@ class TodoListTester(TopydoTest):
self.todolist.delete(todo)
self.assertEqual(self.todolist.count(), count)
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_append1(self):
todo = self.todolist.todo(3)
......@@ -122,7 +122,7 @@ class TodoListTester(TopydoTest):
"(C) Baz @Context1 +Project1 key:value @Context3")
self.assertEqual(set(['Context1', 'Context2', 'Context3']),
self.todolist.contexts())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_append2(self):
todo = self.todolist.todo(3)
......@@ -145,7 +145,7 @@ class TodoListTester(TopydoTest):
self.assertRaises(InvalidTodoException, self.todolist.todo,
count + 100)
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_count(self):
""" Test that empty lines are not counted. """
......@@ -167,26 +167,26 @@ class TodoListTester(TopydoTest):
todo = self.todolist.todo(1)
self.todolist.set_todo_completed(todo)
self.assertTrue(self.todolist.todo(1).is_completed())
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_todo_priority1(self):
todo = self.todolist.todo(1)
self.todolist.set_priority(todo, 'F')
self.assertEqual(self.todolist.todo(1).priority(), 'F')
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_todo_priority2(self):
todo = self.todolist.todo(1)
self.todolist.set_priority(todo, 'C')
self.assertFalse(self.todolist.is_dirty())
self.assertFalse(self.todolist.dirty)
def test_erase(self):
self.todolist.erase()
self.assertEqual(self.todolist.count(), 0)
self.assertTrue(self.todolist.is_dirty())
self.assertTrue(self.todolist.dirty)
def test_regex1(self):
""" Multiple hits should result in None. """
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from test.topydo_testcase import TopydoTest
from topydo.lib.Utils import translate_key_to_config
class UtilsTest(TopydoTest):
def test_key_to_cfg(self):
ctrl_s = translate_key_to_config('ctrl s')
meta_d = translate_key_to_config('meta d')
esc = translate_key_to_config('esc')
f4 = translate_key_to_config('f4')
self.assertEqual(ctrl_s, '<C-s>')
self.assertEqual(meta_d, '<M-d>')
self.assertEqual(esc, '<Esc>')
self.assertEqual(f4, '<F4>')
if __name__ == '__main__':
unittest.main()
......@@ -4,7 +4,7 @@
default_command = ls
; filename = todo.txt
; archive_filename = done.txt
colors = 1
colors = auto
; identifiers can be 'linenumber' or 'text'
identifiers = linenumber
backup_count = 5
......@@ -42,6 +42,8 @@ append_parent_contexts = 0
; [light-]gray, darkgray or numbers from 0 to 255. When number is specified color
; is matched from Xterm color chart available here:
; http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
; When using values between 16 and 256, make sure to set colors = 256 in the
; [topydo] section.
; priority_colors = A:cyan,B:yellow,C:blue
; project_color = red
......@@ -63,3 +65,32 @@ append_parent_contexts = 0
;listcon = lscon
;listcontext = lscon
;listcontexts = lscon
[column_keymap]
; Keymap configuration for column-mode
gg = home
G = end
j = down
k = up
d = cmd del {}
e = cmd edit {}
u = cmd revert
x = cmd do {}
pp = postpone
ps = postpone_s
pr = pri
m = mark
0 = first_column
$ = last_column
h = prev_column
l = next_column
A = append_column
I = insert_column
E = edit_column
D = delete_column
Y = copy_column
L = swap_left
R = swap_right
<Left> = prev_column
<Right> = next_column
<Esc> = reset
......@@ -22,7 +22,10 @@ I/O on the command-line.
import getopt
import sys
MAIN_OPTS = "ac:d:ht:v"
from topydo.lib.Color import AbstractColor, Color
from topydo.lib.TopydoString import TopydoString
MAIN_OPTS = "ac:C:d:ht:v"
READ_ONLY_COMMANDS = ('List', 'ListContext', 'ListProject')
......@@ -30,12 +33,14 @@ def usage():
""" Prints the command-line usage of topydo. """
print("""\
Synopsis: topydo [-a] [-c <config>] [-d <archive>] [-t <todo.txt>] subcommand [help|args]
Synopsis: topydo [-a] [-c <config>] [-C <colormode>] [-d <archive>] [-t <todo.txt>] subcommand [help|args]
topydo -h
topydo -v
-a : Do not archive todo items on completion.
-c : Specify an alternative configuration file.
-C : Specify color mode (0 = disable, 1 = enable 16 colors,
16 = enable 16 colors, 256 = enable 256 colors, auto (default))
-d : Specify an alternative archive file (done.txt)
-h : This help text
-t : Specify an alternative todo file
......@@ -62,20 +67,59 @@ Available commands:
Run `topydo help <subcommand>` for command-specific help.\
""")
def write(p_file, p_string):
"""
Write p_string to file p_file, trailed by a newline character.
ANSI codes are removed when the file is not a TTY.
ANSI codes are removed when the file is not a TTY (and colors are
automatically determined).
"""
if not p_file.isatty():
if not config().colors(p_file.isatty()):
p_string = escape_ansi(p_string)
if p_string:
p_file.write(p_string + "\n")
def lookup_color(p_color):
"""
Converts an AbstractColor to a normal Color. Returns the Color itself
when a normal color is passed.
"""
if not lookup_color.colors:
lookup_color.colors[AbstractColor.NEUTRAL] = Color('NEUTRAL')
lookup_color.colors[AbstractColor.PROJECT] = config().project_color()
lookup_color.colors[AbstractColor.CONTEXT] = config().context_color()
lookup_color.colors[AbstractColor.META] = config().metadata_color()
lookup_color.colors[AbstractColor.LINK] = config().link_color()
try:
return lookup_color.colors[p_color]
except KeyError:
return p_color
lookup_color.colors = {}
def insert_ansi(p_string):
""" Returns a string with color information at the right positions. """
result = p_string.data
for pos, color in sorted(p_string.colors.items(), reverse=True):
color = lookup_color(color)
result = result[:pos] + color.as_ansi() + result[pos:]
return result
def output(p_string):
if isinstance(p_string, list):
p_string = "\n".join([insert_ansi(s) for s in p_string])
elif isinstance(p_string, TopydoString):
# convert color codes to ANSI
p_string = insert_ansi(p_string)
write(sys.stdout, p_string)
def error(p_string):
""" Writes an error on the standard error. """
write(sys.stderr, p_string)
......@@ -140,6 +184,9 @@ class CLIApplicationBase(object):
self.do_archive = False
elif opt == "-c":
alt_config_path = value
elif opt == "-C":
overrides[('topydo', 'force_colors')] = '1'
overrides[('topydo', 'colors')] = value
elif opt == "-t":
overrides[('topydo', 'filename')] = value
elif opt == "-d":
......@@ -174,7 +221,7 @@ class CLIApplicationBase(object):
command = ArchiveCommand(self.todolist, archive)
command.execute()
if archive.is_dirty():
if archive.dirty:
archive_file.write(archive.print_todos())
def _help(self, args):
......@@ -189,21 +236,24 @@ class CLIApplicationBase(object):
READ_ONLY_COMMANDS)
return p_command.__module__.endswith(read_only_commands)
def _execute(self, p_command, p_args):
"""
Execute a subcommand with arguments. p_command is a class (not an
object).
"""
def _backup(self, p_command, p_args):
if config().backup_count() > 0 and p_command and not self.is_read_only(p_command):
call = [p_command.__module__.lower()[16:-7]] + p_args # strip "topydo.commands" and "Command"
from topydo.lib.ChangeSet import ChangeSet
self.backup = ChangeSet(self.todolist, p_call=call)
def _execute(self, p_command, p_args):
"""
Execute a subcommand with arguments. p_command is a class (not an
object).
"""
self._backup(p_command, p_args)
command = p_command(
p_args,
self.todolist,
lambda o: write(sys.stdout, o),
output,
error,
input)
......@@ -219,7 +269,7 @@ class CLIApplicationBase(object):
to the todo.txt file.
"""
if self.todolist.is_dirty():
if self.todolist.dirty:
# do not archive when the value of the filename is an empty string
# (i.e. explicitly left empty in the configuration
if self.do_archive and config().archive():
......@@ -233,6 +283,7 @@ class CLIApplicationBase(object):
self.backup.save(self.todolist)
self.todofile.write(self.todolist.print_todos())
self.todolist.dirty = False
self.backup = None
......
......@@ -101,6 +101,7 @@ class PromptApplication(CLIApplicationBase):
continue
except ValueError as verr:
error('Error: ' + str(verr))
continue
mtime_after = _todotxt_mtime()
......
......@@ -45,6 +45,12 @@ def main():
PromptApplication().run()
except ImportError:
error("You have to install prompt-toolkit to run prompt mode.")
elif args[0] == 'columns':
try:
from topydo.ui.Main import UIApplication
UIApplication().run()
except ImportError:
error("You have to install urwid to run column mode.")
else:
CLIApplication().run()
except IndexError:
......
......@@ -69,7 +69,7 @@ class PostponeCommand(MultiCommand):
# pylint: disable=E1103
todo.set_tag(config().tag_due(), new_due.isoformat())
self.todolist.set_dirty()
self.todolist.dirty = True
self.out(self.printer.print_todo(todo))
else:
self.error("Invalid date pattern given.")
......
......@@ -106,7 +106,7 @@ class TagCommand(Command):
self.todo.set_tag(self.tag, self.value, self.force_add, p_old_value)
if old_src != self.todo.source():
self.todolist.set_dirty()
self.todolist.dirty = True
def _set(self):
if len(self.current_values) > 1 and not self.force_add:
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2016 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" This module provides a class that represents a color. """
class AbstractColor:
NEUTRAL = 0
PROJECT = 1
CONTEXT = 2
META = 3
LINK = 4
class Color:
color_names_dict = {
'black': 0,
'red': 1,
'green': 2,
'yellow': 3,
'blue': 4,
'magenta': 5,
'cyan': 6,
'gray': 7,
'darkgray': 8,
'light-red': 9,
'light-green': 10,
'light-yellow': 11,
'light-blue': 12,
'light-magenta': 13,
'light-cyan': 14,
'white': 15,
}
def __init__(self, p_value=None):
""" p_value is user input, be it a word color or an xterm code """
self._value = None
self.color = p_value
@property
def color(self):
return self._value
@color.setter
def color(self, p_value):
try:
if not p_value:
self._value = None
elif p_value in Color.color_names_dict:
self._value = Color.color_names_dict[p_value]
else:
self._value = int(p_value)
# values not in the 256 range are normalized to be neutral
if not 0 <= self._value < 256:
raise ValueError
except ValueError:
# garbage was entered, make it neutral, so at least some
# highlighting may take place
self._value = -1
def is_neutral(self):
"""
A neutral color is the default color on the shell, setting this color
will reset all other attributes (background, foreground, decoration).
"""
return self._value == -1
def is_valid(self):
"""
Whether the color is a valid color.
"""
return self._value is not None
def as_ansi(self, p_decoration='normal', p_background=False):
if not self.is_valid():
return ''
elif self.is_neutral():
return '\033[0m'
is_high_color = 8 <= self._value < 16
is_256 = 16 <= self._value < 255
decoration_dict = {
'normal': '0',
'bold': '1',
'faint': '2',
'italic': '3',
'underline': '4',
}
decoration = decoration_dict[p_decoration]
base = 40 if p_background else 30
if is_high_color:
color = '1;{}'.format(base + self._value - 8)
elif is_256:
color = '{};5;{}'.format(base + 8, self._value)
else:
# it's a low color
color = str(base + self._value)
return '\033[{};{}m'.format(
decoration,
color
)
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" This module serves for managing output colors. """
from topydo.lib.Config import config
NEUTRAL_COLOR = '\033[0m'
class Colors(object):
def __init__(self):
self.priority_colors = config().priority_colors()
self.project_color = config().project_color()
self.context_color = config().context_color()
self.metadata_color = config().metadata_color()
self.link_color = config().link_color()
def _int_to_ansi(self, p_int, p_decorator='normal', p_safe=True):
"""
Returns ansi code for color based on xterm color id (0-255) and
decoration, where decoration can be one of: normal, bold, faint,
italic, or underline. When p_safe is True, resulting ansi code is
constructed in most compatible way, but with support for only base 16
colors.
"""
decoration_dict = {
'normal': '0',
'bold': '1',
'faint': '2',
'italic': '3',
'underline': '4'
}
decoration = decoration_dict[p_decorator]
try:
if p_safe:
if 8 > int(p_int) >= 0:
return '\033[{};3{}m'.format(decoration, str(p_int))
elif 16 > int(p_int):
p_int = int(p_int) - 8
return '\033[{};1;3{}m'.format(decoration, str(p_int))
if 256 > int(p_int) >= 0:
return '\033[{};38;5;{}m'.format(decoration, str(p_int))
else:
return NEUTRAL_COLOR
except ValueError:
return None
def _name_to_int(self, p_color_name):
""" Returns xterm color id from color name. """
color_names_dict = {
'black': 0,
'red': 1,
'green': 2,
'yellow': 3,
'blue': 4,
'magenta': 5,
'cyan': 6,
'gray': 7,
'darkgray': 8,
'light-red': 9,
'light-green': 10,
'light-yellow': 11,
'light-blue': 12,
'light-magenta': 13,
'light-cyan': 14,
'white': 15,
}
try:
return color_names_dict[p_color_name]
except KeyError:
return 404
def _name_to_ansi(self, p_color_name, p_decorator):
""" Returns ansi color code from color name. """
number = self._name_to_int(p_color_name)
return self._int_to_ansi(number, p_decorator)
def _get_ansi(self, p_color, p_decorator):
""" Returns ansi color code from color name or xterm color id. """
if p_color == '':
ansi = ''
else:
ansi = self._int_to_ansi(p_color, p_decorator, False)
if not ansi:
ansi = self._name_to_ansi(p_color, p_decorator)
return ansi
def get_priority_colors(self):
pri_ansi_colors = dict()
for pri in self.priority_colors:
color = self._get_ansi(self.priority_colors[pri], 'normal')
if color == '':
color = NEUTRAL_COLOR
pri_ansi_colors[pri] = color
return pri_ansi_colors
def get_project_color(self):
return self._get_ansi(self.project_color, 'bold')
def get_context_color(self):
return self._get_ansi(self.context_color, 'bold')
def get_metadata_color(self):
return self._get_ansi(self.metadata_color, 'bold')
def get_link_color(self):
return self._get_ansi(self.link_color, 'underline')
......@@ -16,8 +16,16 @@
import configparser
import os
import re
import shlex
from itertools import accumulate
from string import ascii_lowercase
from topydo.lib.Color import Color
def home_config_path(p_filename):
return os.path.join(os.path.expanduser('~'), p_filename)
class ConfigError(Exception):
def __init__(self, p_text):
......@@ -54,7 +62,8 @@ class _Config:
self.defaults = {
'topydo': {
'default_command': 'ls',
'colors': '1',
'colors': 'auto',
'force_colors': '0',
'filename': 'todo.txt',
'archive_filename': 'done.txt',
'identifiers': 'linenumber',
......@@ -107,11 +116,43 @@ class _Config:
'listcontext': 'lscon',
'listcontexts': 'lscon',
},
'column_keymap': {
'gg': 'home',
'G': 'end',
'j': 'down',
'k': 'up',
'd': 'cmd del {}',
'e': 'cmd edit {}',
'u': 'cmd revert',
'x': 'cmd do {}',
'm': 'mark',
'.': 'repeat',
'pp': 'postpone',
'ps': 'postpone_s',
'pr': 'pri',
'0': 'first_column',
'$': 'last_column',
'h': 'prev_column',
'l': 'next_column',
'A': 'append_column',
'I': 'insert_column',
'E': 'edit_column',
'D': 'delete_column',
'Y': 'copy_column',
'L': 'swap_left',
'R': 'swap_right',
'<Left>': 'prev_column',
'<Right>': 'next_column',
'<Esc>': 'reset',
},
}
self.config = {}
self.cp = configparser.RawConfigParser()
# allow uppercase config keys
self.cp.optionxform = lambda option: option
for section in self.defaults:
self.cp.add_section(section)
......@@ -121,7 +162,8 @@ class _Config:
files = [
"/etc/topydo.conf",
self._home_config_path(),
home_config_path('.config/topydo/config'),
home_config_path('.topydo'),
".topydo",
"topydo.conf",
"topydo.ini",
......@@ -144,17 +186,44 @@ class _Config:
if not self.cp.has_section(section):
self.cp.add_section(section)
def _home_config_path(self):
return os.path.join(os.path.expanduser('~'), '.topydo')
def default_command(self):
return self.cp.get('topydo', 'default_command')
def colors(self):
def colors(self, p_hint_possible=True):
"""
Returns 0, 16 or 256 representing the number of colors that should be
used in the output.
A hint can be passed whether the device that will output the text
supports colors.
"""
lookup = {
'false': 0,
'no': 0,
'0': 0,
'1': 16,
'true': 16,
'yes': 16,
'16': 16,
'256': 256,
}
try:
forced = self.cp.get('topydo', 'force_colors') == '1'
except ValueError:
forced = self.defaults['topydo']['force_colors'] == '1'
try:
return self.cp.getboolean('topydo', 'colors')
colors = lookup[self.cp.get('topydo', 'colors').lower()] # pylint: disable=no-member
except ValueError:
return self.defaults['topydo']['colors'] == '1'
colors = lookup[self.defaults['topydo']['colors'].lower()] # pylint: disable=no-member
except KeyError:
# for invalid values or 'auto'
colors = 16 if p_hint_possible else 0
# disable colors when no colors are enforced on the commandline and
# color support is determined automatically
return 0 if not forced and not p_hint_possible else colors
def todotxt(self):
return os.path.expanduser(self.cp.get('topydo', 'filename'))
......@@ -237,53 +306,53 @@ class _Config:
return [] if hidden_tags == '' else [tag.strip() for tag in
hidden_tags.split(',')]
def priority_colors(self):
def priority_color(self, p_priority):
"""
Returns a dict with priorities as keys and color numbers as value.
"""
pri_colors_str = self.cp.get('colorscheme', 'priority_colors')
def _str_to_dict(p_string):
pri_colors_dict = dict()
for pri_color in p_string.split(','):
pri, color = pri_color.split(':')
pri_colors_dict[pri] = color
pri_colors_dict[pri] = Color(color)
return pri_colors_dict
try:
pri_colors_str = self.cp.get('colorscheme', 'priority_colors')
if pri_colors_str == '':
pri_colors_dict = {'A': '', 'B': '', 'C': ''}
pri_colors_dict = _str_to_dict('A:-1,B:-1,C:-1')
else:
pri_colors_dict = _str_to_dict(pri_colors_str)
except ValueError:
pri_colors_dict = _str_to_dict(self.defaults['colorscheme']['priority_colors'])
return pri_colors_dict
return pri_colors_dict[p_priority] if p_priority in pri_colors_dict else Color('NEUTRAL')
def project_color(self):
try:
return self.cp.get('colorscheme', 'project_color')
return Color(self.cp.getint('colorscheme', 'project_color'))
except ValueError:
return int(self.defaults['colorscheme']['project_color'])
return Color(self.cp.get('colorscheme', 'project_color'))
def context_color(self):
try:
return self.cp.get('colorscheme', 'context_color')
return Color(self.cp.getint('colorscheme', 'context_color'))
except ValueError:
return int(self.defaults['colorscheme']['context_color'])
return Color(self.cp.get('colorscheme', 'context_color'))
def metadata_color(self):
try:
return self.cp.get('colorscheme', 'metadata_color')
return Color(self.cp.getint('colorscheme', 'metadata_color'))
except ValueError:
return int(self.defaults['colorscheme']['metadata_color'])
return Color(self.cp.get('colorscheme', 'metadata_color'))
def link_color(self):
try:
return self.cp.get('colorscheme', 'link_color')
return Color(self.cp.getint('colorscheme', 'link_color'))
except ValueError:
return int(self.defaults['colorscheme']['link_color'])
return Color(self.cp.get('colorscheme', 'link_color'))
def auto_creation_date(self):
try:
......@@ -314,6 +383,28 @@ class _Config:
""" Returns the list format used by `ls` """
return self.cp.get('ls', 'list_format')
def column_keymap(self):
""" Returns keymap and keystates used in column mode """
keystates = set()
shortcuts = self.cp.items('column_keymap')
keymap_dict = dict(shortcuts)
for combo, action in shortcuts:
# add all possible prefixes to keystates
combo_as_list = re.split('(<[A-Z].+?>|.)', combo)[1::2]
if len(combo_as_list) > 1:
keystates |= set(accumulate(combo_as_list[:-1]))
if action in ['pri', 'postpone', 'postpone_s']:
keystates.add(combo)
if action == 'pri':
for c in ascii_lowercase:
keymap_dict[combo + c] = 'cmd pri {} ' + c
return (keymap_dict, keystates)
def config(p_path=None, p_overrides=None):
"""
......
......@@ -45,40 +45,12 @@ class ExpressionCommand(Command):
def _filters(self):
filters = []
def arg_filters():
result = []
if self.last_argument:
args = self.args[:-1]
else:
args = self.args
for arg in args:
# when a word starts with -, it should be negated
is_negated = len(arg) > 1 and arg[0] == '-'
arg = arg[1:] if is_negated else arg
argfilter = None
for match, _filter in Filter.MATCHES:
if re.match(match, arg):
argfilter = _filter(arg)
break
if not argfilter:
argfilter = Filter.GrepFilter(arg)
if is_negated:
argfilter = Filter.NegationFilter(argfilter)
result.append(argfilter)
return result
if not self.show_all:
filters.append(Filter.DependencyFilter(self.todolist))
filters.append(Filter.RelevanceFilter())
filters += arg_filters()
args = self.args[:-1] if self.last_argument else self.args
filters += Filter.get_filter_list(args)
if not self.show_all:
filters.append(Filter.LimitFilter(self.limit))
......
......@@ -318,3 +318,32 @@ MATCHES = [
(_ORDINAL_TAG_MATCH, OrdinalTagFilter),
(_PRIORITY_MATCH, PriorityFilter),
]
def get_filter_list(p_expression):
"""
Returns a list of GrepFilters, OrdinalTagFilters or NegationFilters based
on the given filter expression.
The filter expression is a list of strings.
"""
result = []
for arg in p_expression:
# when a word starts with -, it should be negated
is_negated = len(arg) > 1 and arg[0] == '-'
arg = arg[1:] if is_negated else arg
argfilter = None
for match, _filter in MATCHES:
if re.match(match, arg):
argfilter = _filter(arg)
break
if not argfilter:
argfilter = GrepFilter(arg)
if is_negated:
argfilter = NegationFilter(argfilter)
result.append(argfilter)
return result
......@@ -112,7 +112,7 @@ class IcalPrinter(Printer):
if not uid:
uid = generate_uid()
p_todo.set_tag('ical', uid)
self.todolist.set_dirty()
self.todolist.dirty = True
return uid
......
......@@ -20,7 +20,8 @@ import arrow
import re
from topydo.lib.Config import config
from topydo.lib.Utils import get_terminal_size
from topydo.lib.ProgressColor import progress_color
from topydo.lib.Utils import get_terminal_size, escape_ansi
MAIN_PATTERN = (r'^({{(?P<before>.+?)}})?'
r'(?P<placeholder>{ph}|\[{ph}\])'
......@@ -107,7 +108,7 @@ def _truncate(p_str, p_repl):
Place of the truncation is calculated depending on p_max_width.
"""
# 4 is for '...' and an extra space at the end
text_lim = _columns() - len(p_str) - 4
text_lim = _columns() - len(escape_ansi(p_str)) - 4
truncated_str = re.sub(re.escape(p_repl), p_repl[:text_lim] + '...', p_str)
return truncated_str
......@@ -119,7 +120,7 @@ def _right_align(p_str):
Right alignment is done using proper number of spaces calculated from
'line_width' attribute.
"""
to_fill = _columns() - len(p_str)
to_fill = _columns() - len(escape_ansi(p_str))
if to_fill > 0:
p_str = re.sub('\t', ' '*to_fill, p_str)
......@@ -128,6 +129,12 @@ def _right_align(p_str):
return p_str
def color_block(p_todo):
return '{} {}'.format(
progress_color(p_todo).as_ansi(p_background=True),
config().priority_color(p_todo.priority()).as_ansi(),
)
class ListFormatParser(object):
""" Parser of format string. """
def __init__(self, p_todolist, p_format=None):
......@@ -196,6 +203,8 @@ class ListFormatParser(object):
# relative completion date
'X': lambda t: 'x ' + humanize_date(t.completion_date()) if t.is_completed() else '',
'z': lambda t: color_block(t) if config().colors() else ' ',
}
self.format_list = self._preprocess_format()
......@@ -272,7 +281,7 @@ class ListFormatParser(object):
parsed_str = _unescape_percent_sign(''.join(parsed_list))
parsed_str = _remove_redundant_spaces(parsed_str)
if self.one_line and len(parsed_str) >= _columns():
if self.one_line and len(escape_ansi(parsed_str)) >= _columns():
parsed_str = _truncate(parsed_str, repl_trunc)
if re.search('.*\t', parsed_str):
......
......@@ -16,6 +16,7 @@
from topydo.lib.prettyprinters.Colors import PrettyPrinterColorFilter
from topydo.lib.prettyprinters.Numbers import PrettyPrinterNumbers
from topydo.lib.TopydoString import TopydoString
class Printer(object):
......@@ -29,11 +30,8 @@ class Printer(object):
raise NotImplementedError
def print_list(self, p_todos):
"""
Given a list of todo items, pretty print it and return a list of
formatted strings.
"""
return "\n".join([self.print_todo(todo) for todo in p_todos])
for todo in p_todos:
self.print_todo(todo)
class PrettyPrinter(Printer):
......@@ -68,7 +66,15 @@ class PrettyPrinter(Printer):
for ppf in self.filters:
todo_str = ppf.filter(todo_str, p_todo)
return todo_str
return TopydoString(todo_str)
def print_list(self, p_todos):
"""
Given a list of todo items, pretty print it and return a list of
formatted TopydoStrings. The output function in the UI should convert
the colors inside properly.
"""
return [self.print_todo(todo) for todo in p_todos]
def pretty_printer_factory(p_todolist, p_additional_filters=None):
......
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2016 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from topydo.lib.Color import Color
from topydo.lib.Config import config
from topydo.lib.Recurrence import relative_date_to_date
# when a todo item has not enough information to determine the length, assume
# this length
ASSUMED_TODO_LENGTH = 14 # days
def progress_color(p_todo):
color16_range = [
2, # green
10, # light green
3, # yellow
1, # red
]
# https://upload.wikimedia.org/wikipedia/en/1/15/Xterm_256color_chart.svg
# a gradient from green to yellow to red
color256_range = \
[22, 28, 34, 40, 46, 82, 118, 154, 190, 226, 220, 214, 208, 202, 196]
def get_length():
"""
Returns the length of the p_todo item in days, based on the recurrence
period + due date, or the start/due date.
"""
result = 0
def diff_days(p_start, p_end):
"""
Returns the difference in days between p_start and p_end, where
start is before due.
"""
diff = p_end - p_start
return diff.days
does_recur = p_todo.has_tag('rec')
start_date = p_todo.start_date()
due_date = p_todo.due_date()
creation_date = p_todo.creation_date()
if does_recur and due_date and not start_date:
# add negation, offset is based on due date
recurrence_pattern = p_todo.tag_value('rec')
neg_recurrence_pattern = re.sub('^\+?', '-', recurrence_pattern)
start = relative_date_to_date(neg_recurrence_pattern, due_date)
result = diff_days(start, due_date)
elif due_date and not start_date and not creation_date:
result = ASSUMED_TODO_LENGTH
elif due_date and start_date and due_date < start_date:
result = ASSUMED_TODO_LENGTH
elif due_date and not start_date and creation_date and due_date < creation_date:
result = ASSUMED_TODO_LENGTH
else:
result = p_todo.length()
# a todo item is at least one day long
return max(1, result)
def get_progress():
"""
Returns a value from 0 to 1 where we are today in a date range. Returns
a value >1 when a todo item is overdue.
"""
if p_todo.is_overdue():
return 1.1
elif p_todo.due_date():
days_till_due = p_todo.days_till_due()
length = get_length()
return max((length - days_till_due), 0) / length
else:
return 0
color_range = color256_range if config().colors() == 256 else color16_range
progress = get_progress()
# TODO: remove linear scale to exponential scale
if progress > 1:
# overdue, return the last color
return Color(color_range[-1])
else:
# not overdue, calculate position over color range excl. due date
# color
pos = round(progress * (len(color_range) - 2))
return Color(color_range[pos])
......@@ -52,7 +52,7 @@ class TodoListBase(object):
self._id_todo_map = {}
self.add_list(p_todostrings)
self.dirty = False
self._dirty = False
def __iter__(self):
"""
......@@ -227,11 +227,13 @@ class TodoListBase(object):
"""
return View(p_sorter, p_filters, self)
def is_dirty(self):
return self.dirty
@property
def dirty(self):
return self._dirty
def set_dirty(self):
self.dirty = True
@dirty.setter
def dirty(self, p_flag):
self._dirty = p_flag
def todos(self):
return self._todos
......@@ -277,4 +279,4 @@ class TodoListBase(object):
this list.
"""
printer = PrettyPrinter()
return printer.print_list(self._todos)
return "\n".join([str(s) for s in printer.print_list(self._todos)])
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2016 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" This module provides TopydoString to embed colors in a string. """
import collections
class TopydoString(collections.UserString):
"""
Represents a string that also contains color information. A combination of
(position, color) is maintained, where the position is the start position
where a certain color should start.
"""
def __init__(self, p_content, p_metadata=None):
if isinstance(p_content, TopydoString):
# don't nest topydostrings
self.colors = p_content.colors
self.metadata = p_content.metadata
super().__init__(p_content.data)
else:
self.colors = {}
super().__init__(p_content)
# allows clients to pass arbitrary data with this string (e.g. a Todo
# object)
self.metadata = p_metadata
def append(self, p_string, p_color):
"""
Append a string with the given color (normal Color or an
AbstractColor).
"""
self.colors[len(self.data)] = p_color
self.data += p_string
def set_color(self, p_pos, p_color):
""" Start using a color at the given position. """
self.colors[p_pos] = p_color
......@@ -23,12 +23,6 @@ import re
from collections import namedtuple
from datetime import date
# shutil.get_terminal_size was added to the standard library in Python 3.3
try:
from shutil import get_terminal_size as _get_terminal_size # pylint: disable=no-name-in-module
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size # pylint: disable=import-error
def date_string_to_date(p_date):
"""
......@@ -61,12 +55,31 @@ def escape_ansi(p_string):
escape_ansi.pattern = re.compile(r'\x1b[^m]*m')
def get_terminal_size():
def get_terminal_size(p_getter=None):
"""
Try to determine terminal size at run time. If that is not possible,
returns the default size of 80x24.
By default, the size is determined with provided get_terminal_size by
shutil. Sometimes an UI may want to specify the desired width, then it can
provide a getter that returns a named tuple (columns, lines) with the size.
"""
try:
return get_terminal_size.getter()
except AttributeError:
if p_getter:
get_terminal_size.getter = p_getter
else:
def inner():
try:
# shutil.get_terminal_size was added to the standard
# library in Python 3.3
try:
from shutil import get_terminal_size as _get_terminal_size # pylint: disable=no-name-in-module
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size # pylint: disable=import-error
sz = _get_terminal_size()
except ValueError:
"""
......@@ -77,3 +90,22 @@ def get_terminal_size():
sz = terminal_size(80, 24)
return sz
get_terminal_size.getter = inner
return get_terminal_size.getter()
def translate_key_to_config(p_key):
"""
Translates urwid key event to form understandable by topydo config parser.
"""
if len(p_key) > 1:
key = p_key.capitalize()
if key.startswith('Ctrl') or key.startswith('Meta'):
key = key[0] + '-' + key[5:]
key = '<' + key + '>'
else:
key = p_key
return key
......@@ -25,14 +25,14 @@ class View(object):
"""
def __init__(self, p_sorter, p_filters, p_todolist):
self._todolist = p_todolist
self.todolist = p_todolist
self._sorter = p_sorter
self._filters = p_filters
@property
def todos(self):
""" Returns a sorted and filtered list of todos in this view. """
result = self._sorter.sort(self._todolist.todos())
result = self._sorter.sort(self.todolist.todos())
for _filter in self._filters:
result = _filter.filter(result)
......
......@@ -18,9 +18,10 @@
import re
from topydo.lib.Colors import NEUTRAL_COLOR, Colors
from topydo.lib.Color import AbstractColor
from topydo.lib.Config import config
from topydo.lib.PrettyPrinterFilter import PrettyPrinterFilter
from topydo.lib.TopydoString import TopydoString
class PrettyPrinterColorFilter(PrettyPrinterFilter):
......@@ -33,41 +34,26 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter):
def filter(self, p_todo_str, p_todo):
""" Applies the colors. """
if config().colors():
colorscheme = Colors()
priority_colors = colorscheme.get_priority_colors()
project_color = colorscheme.get_project_color()
context_color = colorscheme.get_context_color()
metadata_color = colorscheme.get_metadata_color()
link_color = colorscheme.get_link_color()
p_todo_str = TopydoString(p_todo_str, p_todo)
priority_color = NEUTRAL_COLOR
try:
priority_color = priority_colors[p_todo.priority()]
except KeyError:
pass
priority_color = config().priority_color(p_todo.priority())
# color projects / contexts
p_todo_str = re.sub(
r'\B(\+|@)(\S*\w)',
lambda m: (
context_color if m.group(0)[0] == "@"
else project_color) + m.group(0) + priority_color,
p_todo_str)
colors = [
(r'\B@(\S*\w)', AbstractColor.CONTEXT),
(r'\B\+(\S*\w)', AbstractColor.PROJECT),
(r'\b\S+:[^/\s]\S*\b', AbstractColor.META),
(r'(^|\s)(\w+:){1}(//\S+)', AbstractColor.LINK),
]
# tags
p_todo_str = re.sub(r'\b\S+:[^/\s]\S*\b',
metadata_color + r'\g<0>' + priority_color,
p_todo_str)
for pattern, color in colors:
for match in re.finditer(pattern, p_todo_str.data):
p_todo_str.set_color(match.start(), color)
p_todo_str.set_color(match.end(), priority_color)
# add link_color to any valid URL specified outside of the tag.
p_todo_str = re.sub(r'(^|\s)(\w+:){1}(//\S+)',
r'\1' + link_color + r'\2\3' + priority_color,
p_todo_str)
p_todo_str += NEUTRAL_COLOR
p_todo_str.append('', AbstractColor.NEUTRAL)
# color by priority
p_todo_str = priority_color + p_todo_str
p_todo_str.set_color(0, priority_color)
return p_todo_str
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser
from topydo.lib.Config import home_config_path, config
def columns():
"""
Returns list with complete column configuration dicts.
"""
def _get_column_dict(p_cp, p_column):
column_dict = dict()
column_dict['title'] = p_cp.get(p_column, 'title')
column_dict['filterexpr'] = p_cp.get(p_column, 'filterexpr')
column_dict['sortexpr'] = p_cp.get(p_column, 'sortexpr')
column_dict['show_all'] = p_cp.getboolean(p_column, 'show_all')
return column_dict
defaults = {
'title': 'Yet another column',
'filterexpr': '',
'sortexpr': config().sort_string(),
'show_all': '0',
}
cp = configparser.RawConfigParser(defaults)
files = [
"topydo_columns.ini",
"topydo_columns.conf",
".topydo_columns",
home_config_path('.topydo_columns'),
home_config_path('.config/topydo/columns'),
"/etc/topydo_columns.conf",
]
for filename in files:
if cp.read(filename):
break
column_list = []
for column in cp.sections():
column_list.append(_get_column_dict(cp, column))
return column_list
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urwid
class CommandLineWidget(urwid.Edit):
def __init__(self, *args, **kwargs):
self.history = []
self.history_pos = None
# temporary history storage for edits before cmd execution
self.history_tmp = []
super().__init__(*args, **kwargs)
urwid.register_signal(CommandLineWidget, ['blur', 'execute_command'])
def clear(self):
self.set_edit_text("")
def _blur(self):
self.clear()
urwid.emit_signal(self, 'blur')
def _emit_command(self):
if len(self.edit_text) > 0:
urwid.emit_signal(self, 'execute_command', self.edit_text)
self._save_to_history()
self.clear()
def _save_to_history(self):
if len(self.edit_text) > 0:
self.history.append(self.edit_text)
self.history_pos = len(self.history)
self.history_tmp = self.history[:] # sync temporary storage with real history
self.history_tmp.append('')
def _history_move(self, p_step):
"""
Changes current value of the command-line to the value obtained from
history_tmp list with index calculated by addition of p_step to the
current position in the command history (history_pos attribute).
Also saves value of the command-line (before changing it) to history_tmp
for potential later access.
"""
if len(self.history) > 0:
# don't pollute real history - use temporary storage
self.history_tmp[self.history_pos] = self.edit_text
self.history_pos = self.history_pos + p_step
self.set_edit_text(self.history_tmp[self.history_pos])
def _history_next(self):
if self.history_pos != len(self.history):
self._history_move(1)
def _history_back(self):
if self.history_pos != 0:
self._history_move(-1)
def keypress(self, p_size, p_key):
dispatch = {
'enter': self._emit_command,
'esc': self._blur,
'up': self._history_back,
'down': self._history_next
}
try:
dispatch[p_key]()
except KeyError:
super().keypress(p_size, p_key)
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urwid
from topydo.lib.Color import AbstractColor
from topydo.lib.Todo import Todo
from topydo.lib.TopydoString import TopydoString
from topydo.ui.Utils import PaletteItem
PALETTE_LOOKUP = {
# omitting AbstractColor.NEUTRAL on purpose, so a text without any
# attribute will be added to the markup
AbstractColor.PROJECT: PaletteItem.PROJECT,
AbstractColor.CONTEXT: PaletteItem.CONTEXT,
AbstractColor.META: PaletteItem.METADATA,
AbstractColor.LINK: PaletteItem.LINK,
}
def topydostringToMarkup(p_string):
markup = []
color_positions = sorted(p_string.colors.items())
# in case no color positions are available, at least set something at the
# start position
if not color_positions:
color_positions = [(0, None)]
for i, (start_pos, color) in enumerate(color_positions):
# color starts at indicated position
start = start_pos
# color ends at next color indication. if missing, run until the end of
# the string
try:
end = color_positions[i+1][0]
except IndexError:
end = len(str(p_string))
text = str(p_string)[start:end]
if color in PALETTE_LOOKUP:
markup.append((PALETTE_LOOKUP[color], text))
else:
# a plain text without any attribute set (including
# AbstractColor.NEUTRAL)
markup.append(text)
color_at_start = color_positions and color_positions[0][0] == 0
# priority color should appear at the start if present, build a nesting
# markup
if color_at_start and isinstance(p_string.metadata, Todo):
priority = p_string.metadata.priority()
markup = ('pri_' + priority, markup)
return markup
class ConsoleWidget(urwid.LineBox):
def __init__(self, p_text=""):
urwid.register_signal(ConsoleWidget, ['close'])
self.width = 0
self.pile = urwid.Pile([])
super().__init__(self.pile)
def keypress(self, p_size, p_key):
if p_key == 'enter' or p_key == 'q' or p_key == 'esc':
urwid.emit_signal(self, 'close')
# don't return the key, 'enter', 'escape', 'q' or ':' are your only
# escape. ':' will reenter to the cmdline.
elif p_key == ':':
urwid.emit_signal(self, 'close', True)
def render(self, p_size, focus):
"""
This override intercepts the width of the widget such that it can be
stored. The width is used for rendering `ls` output.
"""
self.width = p_size[0]
return super().render(p_size, focus)
def selectable(self):
return True
def print_text(self, p_text):
if isinstance(p_text, list):
for text in p_text:
self.print_text(text)
return
elif isinstance(p_text, TopydoString):
text = urwid.Text(topydostringToMarkup(p_text))
else:
text = urwid.Text(p_text)
self.pile.contents.append((text, ('pack', None)))
def clear(self):
self.pile.contents = []
def console_width(self):
# return the last known width (last render)
return self.width
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urwid
class KeystateWidget(urwid.Text):
def __init__(self):
super().__init__('', align='right')
def selectable(self):
return False
This diff is collapsed.
This diff is collapsed.
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import urwid
from topydo.lib.Config import config
from topydo.lib.ListFormat import ListFormatParser
from topydo.lib.ProgressColor import progress_color
from topydo.ui.Utils import PaletteItem, to_urwid_color
# pass a None todo list, since we won't use %i or %I here
PRIO_FORMATTER = ListFormatParser(None, "%{(}p{)}")
TEXT_FORMATTER = ListFormatParser(None, "%s %k\n%h")
PRJ_CON_PATTERN = r'\B(?:\+|@)(?:\S*\w)'
TAG_PATTERN = r'\b\S+:[^/\s]\S*\b'
URL_PATTERN = r'(?:^|\s)(?:\w+:){1}(?://\S+)'
def _markup(p_todo, p_focus):
"""
Returns an attribute spec for the colors that correspond to the given todo
item.
"""
pri = p_todo.priority()
pri = 'pri_' + pri if pri else PaletteItem.DEFAULT
if not p_focus:
attr_dict = {None: pri}
else:
# use '_focus' palette entries instead of standard ones
attr_dict = {None: pri + '_focus'}
attr_dict[PaletteItem.PROJECT] = PaletteItem.PROJECT_FOCUS
attr_dict[PaletteItem.CONTEXT] = PaletteItem.CONTEXT_FOCUS
attr_dict[PaletteItem.METADATA] = PaletteItem.METADATA_FOCUS
attr_dict[PaletteItem.LINK] = PaletteItem.LINK_FOCUS
return attr_dict
class TodoWidget(urwid.WidgetWrap):
def __init__(self, p_todo, p_number):
# clients use this to associate this widget with the given todo item
self.todo = p_todo
todo_text = TEXT_FORMATTER.parse(p_todo)
priority_text = PRIO_FORMATTER.parse(p_todo)
# split todo_text at each occurrence of tag/project/context/url
txt_pattern = r'|'.join([PRJ_CON_PATTERN, TAG_PATTERN, URL_PATTERN])
txt_pattern = r'(' + txt_pattern + r')'
txt_splitted = re.split(txt_pattern, todo_text)
txt_markup = []
# Examine each substring and apply relevant palette entry if needed
for substring in txt_splitted:
# re.split can generate empty strings when capturing group is used
if not substring:
continue
if re.match(TAG_PATTERN, substring):
txt_markup.append((PaletteItem.METADATA, substring))
elif re.match(URL_PATTERN, substring):
txt_markup.append((PaletteItem.LINK, substring))
elif re.match(PRJ_CON_PATTERN, substring):
if substring.startswith('+'):
txt_markup.append((PaletteItem.PROJECT, substring))
else:
txt_markup.append((PaletteItem.CONTEXT, substring))
else:
txt_markup.append(substring)
id_widget = urwid.Text(str(p_number), align='right')
priority_widget = urwid.Text(priority_text)
self.text_widget = urwid.Text(txt_markup)
progress = to_urwid_color(progress_color(p_todo)) if config().colors() else PaletteItem.DEFAULT
progress_bar = urwid.AttrMap(
urwid.SolidFill(' '),
urwid.AttrSpec(PaletteItem.DEFAULT, progress, 256),
urwid.AttrSpec(PaletteItem.DEFAULT, progress, 256),
)
self.columns = urwid.Columns(
[
(1, progress_bar),
(4, id_widget),
(3, priority_widget),
('weight', 1, self.text_widget),
],
dividechars=1,
box_columns=[0] # the progress bar adapts its height to the rest
)
self.widget = urwid.AttrMap(
self.columns,
_markup(p_todo, p_focus=False),
_markup(p_todo, p_focus=True)
)
super().__init__(self.widget)
def keypress(self, p_size, p_key):
"""
Override keypress to prevent the wrapped Columns widget to
receive any key.
"""
return p_key
def selectable(self):
# make sure that ListBox will highlight this widget
return True
def mark(self):
attr_map = {
None: PaletteItem.MARKED,
PaletteItem.LINK: PaletteItem.MARKED,
PaletteItem.CONTEXT: PaletteItem.MARKED,
PaletteItem.PROJECT: PaletteItem.MARKED,
PaletteItem.METADATA: PaletteItem.MARKED,
}
self.widget.set_attr_map(attr_map)
def unmark(self):
self.widget.set_attr_map(_markup(self.todo, False))
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2016 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class PaletteItem:
PROJECT = 'project'
PROJECT_FOCUS = 'project_focus'
CONTEXT = 'context'
CONTEXT_FOCUS = 'context_focus'
METADATA = 'metadata'
METADATA_FOCUS = 'metadata_focus'
LINK = 'link'
LINK_FOCUS = 'link_focus'
DEFAULT = 'default'
DEFAULT_FOCUS = 'default_focus'
MARKED = 'marked'
def to_urwid_color(p_color):
"""
Given a Color object, transform it to a color that urwid understands.
"""
if not p_color.is_valid():
return 'black'
elif p_color.is_neutral():
return 'default'
else:
return 'h{}'.format(p_color.color)
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2015 Bram Schoenmakers <me@bramschoenmakers.nl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urwid
from topydo.lib.Config import config
class ViewWidget(urwid.LineBox):
def __init__(self, p_todolist):
self._todolist = p_todolist
self.titleedit = urwid.Edit("Title: ", "")
self.sortedit = urwid.Edit("Sort expression: ", "")
self.filteredit = urwid.Edit("Filter expression: ", "")
group = []
self.relevantradio = urwid.RadioButton(group, "Only show relevant todo items", True)
self.allradio = urwid.RadioButton(group, "Show all todo items")
self.pile = urwid.Pile([
self.filteredit,
self.titleedit,
self.sortedit,
self.relevantradio,
self.allradio,
urwid.Button("Save", lambda _: urwid.emit_signal(self, 'save')),
urwid.Button("Cancel", lambda _: self.close()),
])
self.reset()
super().__init__(self.pile)
urwid.register_signal(ViewWidget, ['save', 'close'])
@property
def data(self):
return {
'title': self.titleedit.edit_text or self.filteredit.edit_text,
'sortexpr': self.sortedit.edit_text or config().sort_string(),
'filterexpr': self.filteredit.edit_text,
'show_all': self.allradio.state,
}
@data.setter
def data(self, p_data):
self.titleedit.edit_text = p_data['title']
self.sortedit.edit_text = p_data['sortexpr']
self.filteredit.edit_text = p_data['filterexpr']
self.relevantradio.set_state(not p_data['show_all'])
self.allradio.set_state(p_data['show_all'])
def reset(self):
""" Resets the form. """
self.titleedit.set_edit_text("")
self.sortedit.set_edit_text("")
self.filteredit.set_edit_text("")
self.relevantradio.set_state(True)
self.pile.focus_item = 0
def close(self):
urwid.emit_signal(self, 'close')
def keypress(self, p_size, p_key):
if p_key == 'esc':
self.close()
else:
return super().keypress(p_size, p_key) # pylint: disable=E1102
[all]
title = All tasks
filterexpr =
[today]
title = Due today
filterexpr = due:tod
[overdue]
title = Overdue tasks
filterexpr = due:<tod
sortexpr = desc:due
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment