Commit 6fa8d09c authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'master' into ls-i

Conflicts:
	topydo/commands/ListCommand.py
parents fefb54e3 d62f5d02
[run] [run]
source = topydo source = topydo
branch = True
[report] [report]
exclude_lines = exclude_lines =
...@@ -11,5 +12,5 @@ exclude_lines = ...@@ -11,5 +12,5 @@ exclude_lines =
if __name__ == .__main__.: if __name__ == .__main__.:
omit = omit =
topydo/lib/ExitCommand.py topydo/commands/ExitCommand.py
topydo/lib/Version.py topydo/lib/Version.py
...@@ -18,18 +18,13 @@ install: ...@@ -18,18 +18,13 @@ install:
- "pip install ." - "pip install ."
- "pip install .[ical]" - "pip install .[ical]"
- "pip install .[test]" - "pip install .[test]"
- "pip install coveralls" - "pip install codecov"
script: script:
- "green -vvr" - "green -vvr"
- "python -m pylint --errors-only topydo test" - "python -m pylint --errors-only topydo test"
# Cache Dependencies # Cache Dependencies
after_script: after_script:
- if [[ $TRAVIS_PYTHON_VERSION == 3.4 ]]; then - codecov
coveralls;
fi
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then
coveralls;
fi
cache: cache:
directories: directories:
- $HOME/travis/.cache/pip - $HOME/travis/.cache/pip
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
lines in the todo.txt file. lines in the todo.txt file.
* `edit` only processes the todo items when edits were actually made in the * `edit` only processes the todo items when edits were actually made in the
editor. editor.
* When entering today's day of the week as a relative date, it will use next
week's date instead of today.
* Bugfix: not all tags were properly hidden with the `hide_tags` configuration * Bugfix: not all tags were properly hidden with the `hide_tags` configuration
option. option.
* Better PEP8 compliance (thanks to @MinchinWeb) * Better PEP8 compliance (thanks to @MinchinWeb)
......
...@@ -15,36 +15,38 @@ smoothly into topydo. ...@@ -15,36 +15,38 @@ smoothly into topydo.
possible. I won't be very picky about long lines, but please try to avoid possible. I won't be very picky about long lines, but please try to avoid
them. them.
* I strongly prefer simple and short functions, doing only one thing. I'll * I strongly prefer simple and short functions, doing only one thing. I'll
request you to refactor functions with massive indentation or don't fit ask you to refactor functions with massive indentation or don't fit
otherwise on a screen. otherwise on a screen.
### Testing ### Testing
* Run tests with: * First make sure to have the prerequisites installed to perform the tests:
green pip install .[test]
* Then, run the tests with:
green -r
Obviously, I won't accept anything that makes the tests fail. When you submit Obviously, I won't accept anything that makes the tests fail. When you submit
a Pull Request, Travis CI will automatically run all tests for various Python a Pull Request, Travis CI will automatically run all tests for various Python
versions, but it's better if you run the tests locally first. versions, but it's better if you run the tests locally first.
Make sure you have the `mock` package installed if you test on a Python
version older than 3.3.
* Add tests for your change(s): * Add tests for your change(s):
* Bugfixes: add a testcase that covers your bugfix, so the bug won't happen * Bugfixes: add a test case that covers your bugfix, so the bug won't happen
ever again. ever again.
* Features: add testcases that checks various inputs and outputs of your * Features: add test cases that checks various inputs and outputs of your
feature. Be creative in trying to break the feature you've just implemented. feature. Be creative in trying to break the feature you've just implemented.
* Check the test coverage of your contributed code, in particular if you * Check the test coverage of your contributed code, in particular if you touched
touched code in the topydo.lib or topydo.command packages: code in the topydo.lib or topydo.command packages:
pip install coverage coverage report -m
coverage run setup.py test
coverage report
Or alternatively, for a more friendly output, run: Or alternatively, for a more friendly output, run:
coverage html coverage html
Which will generate annotated files in the *htmlcov* folder. The new code which will generate annotated files in the *htmlcov* folder. The new code
should be marked green (i.e. covered). should be marked green (i.e. covered).
When you create a Pull Request, code coverage will be automatically checked
and reported by [Codecov.io](https://codecov.io/github/bram85/topydo).
topydo topydo
====== ======
[![Build Status](https://travis-ci.org/bram85/topydo.svg?branch=master)](https://travis-ci.org/bram85/topydo) [![Coverage Status](https://coveralls.io/repos/bram85/topydo/badge.svg?branch=master&service=github)](https://coveralls.io/github/bram85/topydo?branch=master) [![Join the chat at https://gitter.im/bram85/topydo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bram85/topydo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=bram85&url=https://github.com/bram85/topydo&title=topydo&language=&tags=github&category=software) [![Build Status](https://travis-ci.org/bram85/topydo.svg?branch=master)](https://travis-ci.org/bram85/topydo) [![codecov.io](https://codecov.io/github/bram85/topydo/coverage.svg?branch=master)](https://codecov.io/github/bram85/topydo?branch=master) [![Join the chat at https://gitter.im/bram85/topydo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bram85/topydo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=bram85&url=https://github.com/bram85/topydo&title=topydo&language=&tags=github&category=software)
topydo is a todo list application using the [todo.txt format][1]. It is heavily topydo is a todo list application using the [todo.txt format][1]. It is heavily
inspired by the [todo.txt CLI][2] by Gina Trapani. This tool is actually a inspired by the [todo.txt CLI][2] by Gina Trapani. This tool is actually a
......
...@@ -31,13 +31,19 @@ setup( ...@@ -31,13 +31,19 @@ setup(
url = "https://github.com/bram85/topydo", url = "https://github.com/bram85/topydo",
install_requires = [ install_requires = [
'six >= 1.9.0', 'six >= 1.9.0',
'arrow >= 0.7.0',
], ],
extras_require = { extras_require = {
':sys_platform=="win32"': ['colorama>=0.2.5'], ':sys_platform=="win32"': ['colorama>=0.2.5'],
':python_version=="2.7"': ['ushlex'], # shutil.get_terminal_size() was introduced in Python 3.3
':python_version=="2.7"': [
'backports.shutil_get_terminal_size>=1.0.0',
'ushlex',
],
':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'],
'ical': ['icalendar'], 'ical': ['icalendar'],
'prompt-toolkit': ['prompt-toolkit >= 0.53'], 'prompt-toolkit': ['prompt-toolkit >= 0.53'],
'test': ['green', 'coverage'], 'test': ['coverage', 'freezegun', 'green', ],
'test:python_version=="2.7"': ['mock'], 'test:python_version=="2.7"': ['mock'],
'test:python_version!="3.2"': ['pylint'], 'test:python_version!="3.2"': ['pylint'],
}, },
......
[topydo]
colors = junk
[add]
auto_creation_date = junk
[ls]
indent = junk
list_limit = junk
human_readable_dates = junk
[sort]
keep_sorted = junk
ignore_weekends = junk
[dep]
append_parent_projects = junk
append_parent_contexts = junk
[colorscheme]
priority_colors = junk
project_color = junk
context_color = junk
metadata_color = junk
link_color = junk
[colorscheme]
; priority_colors = junk
; project_color = junk
; context_color = junk
; metadata_color = junk
; link_color = junk
[{"completed": false, "completion_date": null, "contexts": ["Context2"], "creation_date": null, "priority": "C", "projects": ["Project1"], "source": "(C) Foo @Context2 Not@Context +Project1 Not+Project", "tags": [], "text": "Foo @Context2 Not@Context +Project1 Not+Project"}, {"completed": false, "completion_date": null, "contexts": [], "creation_date": null, "priority": "C", "projects": [], "source": "(C) Drink beer @ home", "tags": [], "text": "Drink beer @ home"}, {"completed": false, "completion_date": null, "contexts": [], "creation_date": null, "priority": "C", "projects": [], "source": "(C) 13 + 29 = 42", "tags": [], "text": "13 + 29 = 42"}, {"completed": false, "completion_date": null, "contexts": ["Context1"], "creation_date": null, "priority": "D", "projects": ["Project2"], "source": "(D) Bar @Context1 +Project2 p:1", "tags": [["p", "1"]], "text": "Bar @Context1 +Project2"}] [{"completed": false, "completion_date": null, "contexts": ["Context2"], "creation_date": "2015-11-05", "priority": "C", "projects": ["Project1"], "source": "(C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project", "tags": [], "text": "Foo @Context2 Not@Context +Project1 Not+Project"}, {"completed": false, "completion_date": null, "contexts": [], "creation_date": null, "priority": "C", "projects": [], "source": "(C) Drink beer @ home", "tags": [], "text": "Drink beer @ home"}, {"completed": false, "completion_date": null, "contexts": [], "creation_date": null, "priority": "C", "projects": [], "source": "(C) 13 + 29 = 42", "tags": [], "text": "13 + 29 = 42"}, {"completed": false, "completion_date": null, "contexts": ["Context1"], "creation_date": null, "priority": "D", "projects": ["Project2"], "source": "(D) Bar @Context1 +Project2 p:1", "tags": [["p", "1"]], "text": "Bar @Context1 +Project2"}]
(C) Foo @Context2 Not@Context +Project1 Not+Project (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project
(D) Bar @Context1 +Project2 p:1 (D) Bar @Context1 +Project2 p:1
(C) Baz @Context1 +Project1 key:value id:1 (C) Baz @Context1 +Project1 key:value id:1
(C) Drink beer @ home (C) Drink beer @ home
......
(D) 2015-08-31 Bar @Context1 +Project2 due:2015-09-30 t:2015-09-29
(Z) 2015-11-06 Lorem ipsum dolorem sit amet. Red @fox +jumped over the lazy:bar and jar due:2015-11-08 t:2015-11-07
(C) 2015-07-12 Foo @Context2 Not@Context +Project1 Not+Project
(C) Baz @Context1 +Project1 key:value
Drink beer @ home id:1 p:2 ical:foobar
x 2014-12-12 Completed but with date:2014-12-12
[ls]
list_format = |%I| %x %{(}p{)} %c %S\t%K
...@@ -15,28 +15,130 @@ ...@@ -15,28 +15,130 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest import unittest
from unittest import skip
from test.topydo_testcase import TopydoTest from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
class ConfigTest(TopydoTest): class ConfigTest(TopydoTest):
def test_config1(self): def test_config01(self):
self.assertEqual(config("test/data/config1").default_command(), 'do') self.assertEqual(config("test/data/ConfigTest1.conf").default_command(), 'do')
def test_config2(self): def test_config02(self):
self.assertNotEqual(config("").default_command(), 'do') self.assertNotEqual(config("").default_command(), 'do')
def test_config3(self): def test_config03(self):
self.assertTrue(config("test/data/config2").ignore_weekends()) self.assertTrue(config("test/data/ConfigTest2.conf").ignore_weekends())
def test_config4(self): def test_config04(self):
""" Test that value in file is overridden by parameter. """ """ Test that value in file is overridden by parameter. """
overrides = { overrides = {
('topydo', 'default_command'): 'edit' ('topydo', 'default_command'): 'edit'
} }
self.assertEqual(config("test/data/config1", p_overrides=overrides).default_command(), 'edit') self.assertEqual(config("test/data/ConfigTest1.conf",
p_overrides=overrides).default_command(),
'edit')
def test_config05(self):
""" 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"])))
def test_config06(self):
""" Bad auto creation date switch value. """
self.assertEqual(config("test/data/ConfigTest4.conf").auto_creation_date(),
bool(int(config().defaults["add"]["auto_creation_date"])))
def test_config07(self):
""" Bad indent value. """
self.assertEqual(config("test/data/ConfigTest4.conf").list_indent(),
int(config().defaults["ls"]["indent"]))
def test_config08(self):
""" Bad list limit value. """
self.assertEqual(config("test/data/ConfigTest4.conf").list_limit(),
int(config().defaults["ls"]["list_limit"]))
def test_config10(self):
""" Bad keep sorted switch value. """
self.assertEqual(config("test/data/ConfigTest4.conf").keep_sorted(),
bool(int(config().defaults["sort"]["keep_sorted"])))
def test_config11(self):
""" Bad ignore weekends switch value. """
self.assertEqual(config("test/data/ConfigTest4.conf").ignore_weekends(),
bool(int(config().defaults["sort"]["ignore_weekends"])))
def test_config12(self):
""" Bad append parent projects switch value. """
self.assertEqual(config("test/data/ConfigTest4.conf").append_parent_projects(),
bool(int(config().defaults["dep"]["append_parent_projects"])))
def test_config13(self):
""" Bad append parent project contexts switch value. """
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"])
@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"])
@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"])
@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"])
@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"])
@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"])
def test_config20(self):
""" No project color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").project_color(),
config().defaults["colorscheme"]["project_color"])
def test_config21(self):
""" No context color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").context_color(),
config().defaults["colorscheme"]["context_color"])
def test_config22(self):
""" No metadata color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").metadata_color(),
config().defaults["colorscheme"]["metadata_color"])
def test_config23(self):
""" No link color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").link_color(),
config().defaults["colorscheme"]["link_color"])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -198,5 +198,14 @@ class EditCommandTest(CommandTest): ...@@ -198,5 +198,14 @@ class EditCommandTest(CommandTest):
self.assertEqual(self.todolist.print_todos(), result) self.assertEqual(self.todolist.print_todos(), result)
mock_call.assert_called_once_with([editor, todotxt]) mock_call.assert_called_once_with([editor, todotxt])
def test_help(self):
command = EditCommand(["help"], self.todolist, self.out, self.error,
None)
command.execute()
self.assertEqual(self.output, "")
self.assertEqual(self.errors,
command.usage() + "\n\n" + command.help() + "\n")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -90,6 +90,22 @@ class GetSubcommandTest(TopydoTest): ...@@ -90,6 +90,22 @@ class GetSubcommandTest(TopydoTest):
self.assertTrue(issubclass(real_cmd, DeleteCommand)) self.assertTrue(issubclass(real_cmd, DeleteCommand))
self.assertEqual(final_args, ["-f", "test"]) self.assertEqual(final_args, ["-f", "test"])
def test_alias_default_cmd03(self):
config("test/data/aliases.conf", {('topydo', 'default_command'): 'nonexisting_default'})
args = ['nonexisting']
real_cmd, final_args = get_subcommand(args)
self.assertFalse(real_cmd)
self.assertEqual(final_args, ['nonexisting'])
def test_alias_default_cmd04(self):
config("test/data/aliases.conf", {('topydo', 'default_command'): 'nonexisting_default'})
args = []
real_cmd, final_args = get_subcommand(args)
self.assertFalse(real_cmd)
self.assertEqual(final_args, [])
def test_wrong_alias(self): def test_wrong_alias(self):
config("test/data/aliases.conf") config("test/data/aliases.conf")
...@@ -97,5 +113,10 @@ class GetSubcommandTest(TopydoTest): ...@@ -97,5 +113,10 @@ class GetSubcommandTest(TopydoTest):
real_cmd, final_args = get_subcommand(args) real_cmd, final_args = get_subcommand(args)
self.assertEqual(real_cmd, None) self.assertEqual(real_cmd, None)
def test_help(self):
real_cmd, final_args = get_subcommand(['help', 'nonexisting'])
self.assertFalse(real_cmd)
self.assertEqual(final_args, ['help', 'nonexisting'])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest import unittest
from datetime import date from datetime import date, timedelta
from freezegun import freeze_time
from test.topydo_testcase import TopydoTest from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config from topydo.lib.Config import config
...@@ -23,23 +24,37 @@ from topydo.lib.Importance import importance ...@@ -23,23 +24,37 @@ from topydo.lib.Importance import importance
from topydo.lib.Todo import Todo from topydo.lib.Todo import Todo
@freeze_time("2015, 11, 06")
class ImportanceTest(TopydoTest): class ImportanceTest(TopydoTest):
def test_importance1(self): def test_importance01(self):
todo = Todo("Foo") todo = Todo("Foo")
self.assertEqual(importance(todo), 2) self.assertEqual(importance(todo), 2)
def test_importance2(self): def test_importance02(self):
todo = Todo("(A) Foo") todo = Todo("(A) Foo")
self.assertEqual(importance(todo), 5) self.assertEqual(importance(todo), 5)
def test_importance3(self): def test_importance03(self):
todo = Todo("(A) Foo " + config().tag_star() + ":1") todo = Todo("(A) Foo " + config().tag_star() + ":1")
self.assertEqual(importance(todo), 6) self.assertEqual(importance(todo), 6)
def test_importance4(self): def test_importance04(self):
today_str = date.today().isoformat() today_str = date.today().isoformat()
todo = Todo("(C) Foo " + config().tag_due() + ":" + today_str) todo = Todo("(C) Foo " + config().tag_due() + ":" + today_str)
self.assertEqual(importance(todo), 8) self.assertEqual(importance(todo), 8)
def test_importance05(self):
todo = Todo("(C) Foo " + config().tag_due() + ":" + "2015-11-14")
self.assertEqual(importance(todo), 4)
def test_importance06(self):
todo = Todo("(C) Foo " + config().tag_due() + ":" + "2015-11-10")
self.assertEqual(importance(todo), 5)
def test_importance07(self):
config(p_overrides={('sort', 'ignore_weekends'): '1'})
todo = Todo("(C) Foo " + config().tag_due() + ":" + "2015-11-09")
self.assertEqual(importance(todo), 6)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -36,7 +36,7 @@ class ListCommandTest(CommandTest): ...@@ -36,7 +36,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list03(self): def test_list03(self):
...@@ -62,7 +62,7 @@ class ListCommandTest(CommandTest): ...@@ -62,7 +62,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list06(self): def test_list06(self):
...@@ -80,7 +80,7 @@ class ListCommandTest(CommandTest): ...@@ -80,7 +80,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 3| (C) Baz @Context1 +Project1 key:value\n| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n") self.assertEqual(self.output, "| 3| (C) Baz @Context1 +Project1 key:value\n| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list08(self): def test_list08(self):
...@@ -117,7 +117,7 @@ class ListCommandTest(CommandTest): ...@@ -117,7 +117,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list12(self): def test_list12(self):
...@@ -128,7 +128,7 @@ class ListCommandTest(CommandTest): ...@@ -128,7 +128,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list13(self): def test_list13(self):
...@@ -137,7 +137,7 @@ class ListCommandTest(CommandTest): ...@@ -137,7 +137,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list14(self): def test_list14(self):
...@@ -147,7 +147,7 @@ class ListCommandTest(CommandTest): ...@@ -147,7 +147,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, " | 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n | 4| (C) Drink beer @ home\n | 5| (C) 13 + 29 = 42\n | 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, " | 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n | 4| (C) Drink beer @ home\n | 5| (C) 13 + 29 = 42\n | 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list15(self): def test_list15(self):
...@@ -165,7 +165,7 @@ class ListCommandTest(CommandTest): ...@@ -165,7 +165,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "|t5c| (C) Foo @Context2 Not@Context +Project1 Not+Project\n|wa5| (C) Drink beer @ home\n|z63| (C) 13 + 29 = 42\n|mfg| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "|t5c| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n|wa5| (C) Drink beer @ home\n|z63| (C) 13 + 29 = 42\n|mfg| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list17(self): def test_list17(self):
...@@ -195,7 +195,7 @@ class ListCommandTest(CommandTest): ...@@ -195,7 +195,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 3| (C) Baz @Context1 +Project1 key:value id:1\n| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n") self.assertEqual(self.output, "| 3| (C) Baz @Context1 +Project1 id:1 key:value\n| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list20(self): def test_list20(self):
...@@ -203,7 +203,7 @@ class ListCommandTest(CommandTest): ...@@ -203,7 +203,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list21(self): def test_list21(self):
...@@ -212,7 +212,7 @@ class ListCommandTest(CommandTest): ...@@ -212,7 +212,7 @@ class ListCommandTest(CommandTest):
command.execute() command.execute()
self.assertFalse(self.todolist.is_dirty()) self.assertFalse(self.todolist.is_dirty())
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list22(self): def test_list22(self):
...@@ -239,7 +239,7 @@ class ListCommandTest(CommandTest): ...@@ -239,7 +239,7 @@ class ListCommandTest(CommandTest):
command = ListCommand(["-n", "1"], self.todolist, self.out, self.error) command = ListCommand(["-n", "1"], self.todolist, self.out, self.error)
command.execute() command.execute()
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list33(self): def test_list33(self):
...@@ -247,7 +247,7 @@ class ListCommandTest(CommandTest): ...@@ -247,7 +247,7 @@ class ListCommandTest(CommandTest):
command = ListCommand(["-n", "-1"], self.todolist, self.out, self.error) command = ListCommand(["-n", "-1"], self.todolist, self.out, self.error)
command.execute() command.execute()
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list34(self): def test_list34(self):
...@@ -257,7 +257,7 @@ class ListCommandTest(CommandTest): ...@@ -257,7 +257,7 @@ class ListCommandTest(CommandTest):
command = ListCommand(["-n", "foo"], self.todolist, self.out, self.error) command = ListCommand(["-n", "foo"], self.todolist, self.out, self.error)
command.execute() command.execute()
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_list35(self): def test_list35(self):
...@@ -265,7 +265,7 @@ class ListCommandTest(CommandTest): ...@@ -265,7 +265,7 @@ class ListCommandTest(CommandTest):
command = ListCommand(["-x", "-n", "foo"], self.todolist, self.out, self.error) command = ListCommand(["-x", "-n", "foo"], self.todolist, self.out, self.error)
command.execute() command.execute()
self.assertEqual(self.output, "| 1| (C) Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n") self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 3| (C) Baz @Context1 +Project1 key:value\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n| 6| x 2014-12-12 Completed but with date:2014-12-12\n")
self.assertEqual(self.errors, "") self.assertEqual(self.errors, "")
def test_help(self): def test_help(self):
......
This diff is collapsed.
...@@ -16,20 +16,20 @@ ...@@ -16,20 +16,20 @@
import unittest import unittest
from datetime import date, timedelta from datetime import date, timedelta
from freezegun import freeze_time
from test.topydo_testcase import TopydoTest from test.topydo_testcase import TopydoTest
from topydo.lib.RelativeDate import relative_date_to_date from topydo.lib.RelativeDate import relative_date_to_date
@freeze_time('2015, 11, 06')
class RelativeDateTester(TopydoTest): class RelativeDateTester(TopydoTest):
def setUp(self): def setUp(self):
super(RelativeDateTester, self).setUp() super(RelativeDateTester, self).setUp()
self.today = date.today() self.today = date(2015, 11, 6)
self.tomorrow = self.today + timedelta(1) self.tomorrow = date(2015, 11, 7)
self.monday = date(2015, 11, 9)
self.monday = self.today self.friday = date(2015, 11, 13)
if self.monday.weekday() != 0:
self.monday += timedelta(7 - self.today.weekday() % 7)
def test_zero_days(self): def test_zero_days(self):
result = relative_date_to_date('0d') result = relative_date_to_date('0d')
...@@ -41,7 +41,7 @@ class RelativeDateTester(TopydoTest): ...@@ -41,7 +41,7 @@ class RelativeDateTester(TopydoTest):
def test_one_week(self): def test_one_week(self):
result = relative_date_to_date('1w') result = relative_date_to_date('1w')
self.assertEqual(result, date.today() + timedelta(weeks=1)) self.assertEqual(result, date(2015, 11, 13))
def test_one_month(self): def test_one_month(self):
test_date = date(2015, 1, 10) test_date = date(2015, 1, 10)
...@@ -104,7 +104,7 @@ class RelativeDateTester(TopydoTest): ...@@ -104,7 +104,7 @@ class RelativeDateTester(TopydoTest):
self.assertEqual(result, self.today) self.assertEqual(result, self.today)
def test_today3(self): def test_today3(self):
result = relative_date_to_date('today', date.today() + timedelta(1)) result = relative_date_to_date('today', self.tomorrow)
self.assertEqual(result, self.today) self.assertEqual(result, self.today)
def test_tomorrow1(self): def test_tomorrow1(self):
...@@ -133,15 +133,23 @@ class RelativeDateTester(TopydoTest): ...@@ -133,15 +133,23 @@ class RelativeDateTester(TopydoTest):
def test_offset1(self): def test_offset1(self):
result = relative_date_to_date('1d', self.tomorrow) result = relative_date_to_date('1d', self.tomorrow)
self.assertEqual(result, date.today() + timedelta(2)) self.assertEqual(result, date(2015, 11, 8))
def test_negative_period1(self): def test_negative_period1(self):
result = relative_date_to_date('-1d') result = relative_date_to_date('-1d')
self.assertEqual(result, date.today() - timedelta(1)) self.assertEqual(result, date(2015, 11, 5))
def test_negative_period2(self): def test_negative_period2(self):
result = relative_date_to_date('-0d') result = relative_date_to_date('-0d')
self.assertTrue(result, self.today) self.assertTrue(result, self.today)
def test_weekday_next_week(self):
"""
When entering "Friday" on a Friday, return next week Friday instead of
today.
"""
result = relative_date_to_date("fri")
self.assertTrue(result, self.friday)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import tempfile
import unittest import unittest
from datetime import date from datetime import date
...@@ -47,8 +48,8 @@ class RevertCommandTest(CommandTest): ...@@ -47,8 +48,8 @@ class RevertCommandTest(CommandTest):
self.tmp_name = str(uuid4().hex.upper()[0:6]) self.tmp_name = str(uuid4().hex.upper()[0:6])
archive_filename = '/tmp/' + self.tmp_name + '_archive' archive_filename = tempfile.gettempdir() + os.sep + self.tmp_name + '_archive'
todo_filename = '/tmp/' + self.tmp_name + '_todo' todo_filename = tempfile.gettempdir() + os.sep + self.tmp_name + '_todo'
config(p_overrides={('topydo', 'archive_filename'): archive_filename, config(p_overrides={('topydo', 'archive_filename'): archive_filename,
('topydo', 'filename'): todo_filename, ('topydo', 'backup_count'): '5'}) ('topydo', 'filename'): todo_filename, ('topydo', 'backup_count'): '5'})
......
...@@ -5,7 +5,8 @@ default_command = ls ...@@ -5,7 +5,8 @@ default_command = ls
; filename = todo.txt ; filename = todo.txt
; archive_filename = done.txt ; archive_filename = done.txt
colors = 1 colors = 1
identifiers = linenumber ; or: text ; identifiers can be 'linenumber' or 'text'
identifiers = linenumber
backup_count = 5 backup_count = 5
[add] [add]
...@@ -15,6 +16,7 @@ auto_creation_date = 1 ...@@ -15,6 +16,7 @@ auto_creation_date = 1
hide_tags = id,p,ical hide_tags = id,p,ical
indent = 0 indent = 0
list_limit = -1 list_limit = -1
list_format = |%I| %x %{(}p{)} %c %s %k %{due:}d %{t:}t
[tags] [tags]
tag_start = t tag_start = t
...@@ -50,6 +52,7 @@ append_parent_contexts = 0 ...@@ -50,6 +52,7 @@ append_parent_contexts = 0
[aliases] [aliases]
;showall = ls -x ;showall = ls -x
;next = ls -n 1 ;next = ls -n 1
;top = ls -F '|%I| %x %p %S %k %{(}H{)}'
;lsproj = lsprj ;lsproj = lsprj
;listprj = lsprj ;listprj = lsprj
;listproj = lsprj ;listproj = lsprj
......
...@@ -17,15 +17,20 @@ ...@@ -17,15 +17,20 @@
""" Entry file for the topydo Prompt interface (CLI). """ """ Entry file for the topydo Prompt interface (CLI). """
import os.path import os.path
import shlex
import sys import sys
from topydo.cli.CLIApplicationBase import CLIApplicationBase, error, usage from topydo.cli.CLIApplicationBase import CLIApplicationBase, error, usage
from topydo.cli.TopydoCompleter import TopydoCompleter from topydo.cli.TopydoCompleter import TopydoCompleter
from prompt_toolkit.shortcuts import prompt from prompt_toolkit.shortcuts import prompt
from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.history import InMemoryHistory
from six import PY2
from topydo.lib.Config import config, ConfigError from topydo.lib.Config import config, ConfigError
if PY2:
import ushlex as shlex
# First thing is to poke the configuration and check whether it's sane # First thing is to poke the configuration and check whether it's sane
# The modules below may already read in configuration upon import, so # The modules below may already read in configuration upon import, so
# make sure to bail out if the configuration is invalid. # make sure to bail out if the configuration is invalid.
...@@ -94,7 +99,8 @@ class PromptApplication(CLIApplicationBase): ...@@ -94,7 +99,8 @@ class PromptApplication(CLIApplicationBase):
try: try:
user_input = prompt(u'topydo> ', history=history, user_input = prompt(u'topydo> ', history=history,
completer=self.completer, completer=self.completer,
complete_while_typing=False).split() complete_while_typing=False)
user_input = shlex.split(user_input)
except (EOFError, KeyboardInterrupt): except (EOFError, KeyboardInterrupt):
sys.exit(0) sys.exit(0)
......
...@@ -18,8 +18,9 @@ from topydo.lib.Config import config ...@@ -18,8 +18,9 @@ from topydo.lib.Config import config
from topydo.lib.ExpressionCommand import ExpressionCommand from topydo.lib.ExpressionCommand import ExpressionCommand
from topydo.lib.Filter import InstanceFilter from topydo.lib.Filter import InstanceFilter
from topydo.lib.PrettyPrinter import pretty_printer_factory from topydo.lib.PrettyPrinter import pretty_printer_factory
from topydo.lib.PrettyPrinterFilter import (PrettyPrinterHideTagFilter, from topydo.lib.ListFormat import ListFormatParser
PrettyPrinterIndentFilter) from topydo.lib.PrettyPrinter import pretty_printer_factory
from topydo.lib.PrettyPrinterFilter import PrettyPrinterFormatFilter
from topydo.lib.TodoListBase import InvalidTodoException from topydo.lib.TodoListBase import InvalidTodoException
...@@ -35,6 +36,7 @@ class ListCommand(ExpressionCommand): ...@@ -35,6 +36,7 @@ class ListCommand(ExpressionCommand):
self.sort_expression = config().sort_string() self.sort_expression = config().sort_string()
self.show_all = False self.show_all = False
self.ids = None self.ids = None
self.format = config().list_format()
def _poke_icalendar(self): def _poke_icalendar(self):
""" """
...@@ -50,7 +52,7 @@ class ListCommand(ExpressionCommand): ...@@ -50,7 +52,7 @@ class ListCommand(ExpressionCommand):
return True return True
def _process_flags(self): def _process_flags(self):
opts, args = self.getopt('f:i:n:s:x') opts, args = self.getopt('f:F:i:n:s:x')
for opt, value in opts: for opt, value in opts:
if opt == '-x': if opt == '-x':
...@@ -67,6 +69,8 @@ class ListCommand(ExpressionCommand): ...@@ -67,6 +69,8 @@ class ListCommand(ExpressionCommand):
self.printer = IcalPrinter(self.todolist) self.printer = IcalPrinter(self.todolist)
else: else:
self.printer = None self.printer = None
elif opt == '-F':
self.format = value
elif opt == '-n': elif opt == '-n':
try: try:
self.limit = int(value) self.limit = int(value)
...@@ -111,11 +115,11 @@ class ListCommand(ExpressionCommand): ...@@ -111,11 +115,11 @@ class ListCommand(ExpressionCommand):
if self.printer is None: if self.printer is None:
# create a standard printer with some filters # create a standard printer with some filters
indent = config().list_indent() indent = config().list_indent()
final_format = ' ' * indent + self.format
hidden_tags = config().hidden_tags() hidden_tags = config().hidden_tags()
filters = [] filters = []
filters.append(PrettyPrinterIndentFilter(indent)) filters.append(PrettyPrinterFormatFilter(self.todolist, final_format))
filters.append(PrettyPrinterHideTagFilter(hidden_tags))
self.printer = pretty_printer_factory(self.todolist, filters) self.printer = pretty_printer_factory(self.todolist, filters)
...@@ -136,7 +140,8 @@ class ListCommand(ExpressionCommand): ...@@ -136,7 +140,8 @@ class ListCommand(ExpressionCommand):
return True return True
def usage(self): def usage(self):
return """ Synopsis: ls [-x] [-s <sort_expression>] [-f <format>] [expression]""" return """Synopsis: ls [-x] [-s <sort_expression>] [-f <output format>]
[-F <format string>] [expression]"""
def help(self): def help(self):
return """\ return """\
...@@ -156,6 +161,36 @@ When an expression is given, only the todos matching that expression are shown. ...@@ -156,6 +161,36 @@ When an expression is given, only the todos matching that expression are shown.
an 'ical' tag with a unique ID. Completed todo items may be an 'ical' tag with a unique ID. Completed todo items may be
archived. archived.
* 'json' - Javascript Object Notation (JSON) * 'json' - Javascript Object Notation (JSON)
-F : Specify the format of the text ('text' format), which may contain
placeholders that may be expanded if the todo has such attribute. If such
attribute does not exist, then it expands to an empty string.
%c: Absolute creation date.
%C: Relative creation date.
%d: Absolute due date.
%D: Relative due date.
%h: Relative due and start date (due in 3 days, started 3 days ago)
%H: Like %h with creation date.
%i: Todo number.
%I: Todo number padded with spaces (always 3 characters wide).
%k: List of tags separated by spaces (excluding hidden tags).
%K: List of all tags separated by spaces.
%p: Priority.
%s: Todo text.
%S: Todo text, truncated such that an item fits on one line.
%t: Absolute creation date.
%T: Relative creation date.
%x: 'x' followed by absolute completion date.
%X: 'x' followed by relative completion date.
\%: Literal percent sign.
Conditional characters can be added with blocks surrounded by curly
braces, they will only appear when a placeholder expanded to a value.
E.g. %{(}p{)} will print (C) when the todo item has priority C, or ''
(empty string) when an item has no priority set.
A tab character serves as a marker to start right alignment.
-i : Comma separated list of todo IDs to print. -i : Comma separated list of todo IDs to print.
-s : Sort the list according to a sort expression. Defaults to the expression -s : Sort the list according to a sort expression. Defaults to the expression
in the configuration. in the configuration.
......
...@@ -74,6 +74,7 @@ class _Config: ...@@ -74,6 +74,7 @@ class _Config:
'hide_tags': 'id,p,ical', 'hide_tags': 'id,p,ical',
'indent': '0', 'indent': '0',
'list_limit': '-1', 'list_limit': '-1',
'list_format': '|%I| %x %{(}p{)} %c %s %k %{due:}d %{t:}t',
}, },
'tags': { 'tags': {
...@@ -115,7 +116,7 @@ class _Config: ...@@ -115,7 +116,7 @@ class _Config:
self.config = {} self.config = {}
self.cp = configparser.ConfigParser() self.cp = configparser.RawConfigParser()
for section in self.defaults: for section in self.defaults:
self.cp.add_section(section) self.cp.add_section(section)
...@@ -320,6 +321,11 @@ class _Config: ...@@ -320,6 +321,11 @@ class _Config:
return alias_dict return alias_dict
def list_format(self):
""" Returns the list format used by `ls` """
return self.cp.get('ls', 'list_format')
def config(p_path=None, p_overrides=None): def config(p_path=None, p_overrides=None):
""" """
Retrieve the config instance. Retrieve the config instance.
......
This diff is collapsed.
...@@ -18,10 +18,13 @@ ...@@ -18,10 +18,13 @@
import re import re
from collections import OrderedDict
from six import u from six import u
from topydo.lib.Colors import NEUTRAL_COLOR, Colors from topydo.lib.Colors import NEUTRAL_COLOR, Colors
from topydo.lib.Config import config from topydo.lib.Config import config
from topydo.lib.ListFormat import ListFormatParser
from topydo.lib.Utils import get_terminal_size
class PrettyPrinterFilter(object): class PrettyPrinterFilter(object):
...@@ -61,9 +64,6 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter): ...@@ -61,9 +64,6 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter):
except KeyError: except KeyError:
pass pass
# color by priority
p_todo_str = color + p_todo_str
# color projects / contexts # color projects / contexts
p_todo_str = re.sub( p_todo_str = re.sub(
r'\B(\+|@)(\S*\w)', r'\B(\+|@)(\S*\w)',
...@@ -79,24 +79,15 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter): ...@@ -79,24 +79,15 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter):
# add link_color to any valid URL specified outside of the tag. # add link_color to any valid URL specified outside of the tag.
p_todo_str = re.sub(r'(^|\s)(\w+:){1}(//\S+)', p_todo_str = re.sub(r'(^|\s)(\w+:){1}(//\S+)',
' ' + link_color + r'\2\3' + color, r'\1' + link_color + r'\2\3' + color,
p_todo_str) p_todo_str)
p_todo_str += NEUTRAL_COLOR p_todo_str += NEUTRAL_COLOR
return p_todo_str # color by priority
p_todo_str = color + p_todo_str
class PrettyPrinterIndentFilter(PrettyPrinterFilter):
""" Adds indentation to the todo item. """
def __init__(self, p_indent=0):
super(PrettyPrinterIndentFilter, self).__init__()
self.indent = p_indent
def filter(self, p_todo_str, _): return p_todo_str
""" Applies the indentation. """
return ' ' * self.indent + p_todo_str
class PrettyPrinterNumbers(PrettyPrinterFilter): class PrettyPrinterNumbers(PrettyPrinterFilter):
...@@ -111,17 +102,12 @@ class PrettyPrinterNumbers(PrettyPrinterFilter): ...@@ -111,17 +102,12 @@ class PrettyPrinterNumbers(PrettyPrinterFilter):
return u("|{:>3}| {}").format(self.todolist.number(p_todo), p_todo_str) return u("|{:>3}| {}").format(self.todolist.number(p_todo), p_todo_str)
class PrettyPrinterHideTagFilter(PrettyPrinterFilter): class PrettyPrinterFormatFilter(PrettyPrinterFilter):
""" Removes all occurrences of the given tags from the text. """ def __init__(self, p_todolist, p_format=None):
super(PrettyPrinterFormatFilter, self).__init__()
def __init__(self, p_hidden_tags): self.parser = ListFormatParser(p_todolist, p_format)
super(PrettyPrinterHideTagFilter, self).__init__()
self.hidden_tags = p_hidden_tags
def filter(self, p_todo_str, _): def filter(self, p_todo_str, p_todo):
for hidden_tag in self.hidden_tags: p_todo_str = self.parser.parse(p_todo)
# inspired from remove_tag in TodoBase
p_todo_str = re.sub(r'\s?\b' + hidden_tag + r':\S+\b', '',
p_todo_str)
return p_todo_str return p_todo_str
...@@ -63,8 +63,7 @@ def _convert_weekday_pattern(p_weekday): ...@@ -63,8 +63,7 @@ def _convert_weekday_pattern(p_weekday):
""" """
Converts a weekday name to an absolute date. Converts a weekday name to an absolute date.
When today's day of the week is entered, it will return today and not next When today's day of the week is entered, it will return next week's date.
week's.
""" """
day_value = { day_value = {
'mo': 0, 'mo': 0,
...@@ -81,7 +80,7 @@ def _convert_weekday_pattern(p_weekday): ...@@ -81,7 +80,7 @@ def _convert_weekday_pattern(p_weekday):
day = date.today().weekday() day = date.today().weekday()
shift = (target_day - day) % 7 shift = 7 - (day - target_day) % 7
return date.today() + timedelta(shift) return date.today() + timedelta(shift)
......
...@@ -19,6 +19,8 @@ Various utility functions. ...@@ -19,6 +19,8 @@ Various utility functions.
""" """
import re import re
from collections import namedtuple
from datetime import date from datetime import date
...@@ -51,3 +53,25 @@ def escape_ansi(p_string): ...@@ -51,3 +53,25 @@ def escape_ansi(p_string):
return escape_ansi.pattern.sub('', p_string) return escape_ansi.pattern.sub('', p_string)
escape_ansi.pattern = re.compile(r'\x1b[^m]*m') escape_ansi.pattern = re.compile(r'\x1b[^m]*m')
def get_terminal_size():
"""
Try to determine terminal size at run time. If that is not possible,
returns the default size of 80x24.
"""
try:
from shutil import get_terminal_size # pylint: disable=no-name-in-module
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size
try:
sz = get_terminal_size()
except ValueError:
"""
This can result from the 'underlying buffer being detached', which
occurs during running the unittest on Windows (but not on Linux?)
"""
terminal_size = namedtuple('Terminal_Size', 'columns lines')
sz = terminal_size((80, 24))
return sz
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