VIT provides a user directory that allows for configuring basic settings (via config.ini), as well as custom themes, formatters, and keybindings.
VIT searches for the user directory in this order of priority:
- The
VIT_DIRenvironment variable ~/.vit(the default location)- A
vitdirectory in any valid XDG base directory
By default, VIT uses the default location of the Taskwarrior configuration file to read configuration from Taskwarrior.
Use the taskrc setting in the taskwarrior section of config.ini to override this, or set the TASKRC environment variable to override both the VIT config file and the default Taskwarrior configuration file location.
To provide your own theme:
- Create a
themedirectory in the user directory - Copy over one of the core themes, and customize to your liking.
- Set the
themesetting inconfig.inito the name of your theme without the.pyextension. For example, if you created a theme attheme/mytheme.py, then you would settheme = mythemeinconfig.ini
VIT uses the urwid console user interface library, and the theme is essentially an urwid color palette. Check out their tutorial to learn more.
VIT provides all the standard formatters used in Taskwarrior. If you choose, you can override any default formatter with your own:
- Create a
formatterdirectory in the user directory - Find the formatter you want to override, e.g.
description.truncated - Create the override file name by replacing dots with underscores, and adding a
.pyextension. For the above example, that would bedescription_truncated.py - Import a base formatter, or one of the other more specialized formatters.
- Name the class by CamelCasing the file name. For the above example, that would be
class DescriptionTruncated - Provide a
format(self, value, task)method to the class.valueis the column value for the task in question,taskis the entire task object. The method should return a tuple, the first element is the total length of the formatted text, and the second element is any valid urwid text markup
An excellent place to use custom formatters is for UDA columns that you want formatted in some non-standard way. For example, let's suppose you have a notes UDA created, and you want to display that in reports, but in a truncated format. Here's an example of a custom formatter that would accomplish that:
from vit.formatter.description_truncated import DescriptionTruncated
class Notes(DescriptionTruncated):
def format(self, notes, task):
if not notes:
return self.markup_none(self.colorize())
truncated_notes = self.format_description_truncated(notes)
return (len(truncated_notes), self.markup_element(truncated_notes))
def colorize(self, notes=None):
return self.colorizer.uda_string(self.column, notes)There are tons of existing formatters to use as examples, and the base formatters include the most common helper methods.
VIT exposes actions to be mapped to keybindings however you like, and custom macros can also be triggered by keybindings.
By default, VIT uses the keybinding/vi.ini configuration to provide Vi-style bindings.
To see the list of actions that can be mapped, execute vit --list-actions.
The [keybinding] section in config.ini overrides any core keybindings or keybindings that you place in the user keybinding directory. If you just want to make some small tweaks and/or add some macros, it's probably better to take this approach.
The config.sample.ini has many examples to illustrate how to customize keybindings for actions and add macros, check it out!
VIT exposes a simple API to provide your own variable replacements in keybindings.
Variables enclosed in curly brackets that VIT doesn't know about will be passed to your custom code, where you can match against the variable, and parse the string to extract metadata in the form of arguments that you pass to your custom replacement callback.
To provide custom variable replacement:
- In your user
keybindingdirectory, createkeybinding.py - Create a
Keybindingclass in that file - Expose a
replacementsmethod in that class, that returns a list of replacement configuration objects - Replacement configuration objects have the following keys:
- match_callback: should be a function with one argument, which is the variable to test for a match against. If the variable matches your case, return a list with any arguments you want passed to your replacement callback.
- replacement_callback: should be a function, with the task object as the first argument, followed by the other arguments you returned from the match callback, and should return a string replacement for the variable.
Here's a minimal example:
# keybinding/keybinding.py
class Keybinding:
def replacements(self):
def _custom_match(variable):
if variable == 'TEST':
return ['pass']
def _custom_replace(task, arg):
return 'TEST:%s' % arg
return [
{
'match_callback': _custom_match,
'replacement_callback': _custom_replace,
},
]NOTE: This functionality is more suited to users who want to do something completely different than a Vi-style workflow -- most users will simply want to make some tweaks in the [keybinding] section of config.ini.
- Create a
keybindingdirectory in the user directory - Copy over one of the core keybindings, and customize to your liking.
- Set the
default_keybindingssetting inconfig.inito the name of the keybinding file you created, without the.iniextension. For example, if you createdkeybinding/strange.ini, you would setdefault_keybindings = strangeinconfig.ini
As jumping to tasks is central to VIT's operation, you might want to map each
digit key to an ex command containing that digit, by adding the following to
your keybindings:
1 = :1
2 = :2
3 = :3
4 = :4
5 = :5
6 = :6
7 = :7
8 = :8
9 = :9
Now, for example, to jump to a task whose ID is 42, you need to press 4, 2
and <Enter>, instead of :, 4, 2 and <Enter>.
This saves a : keypress whenever jumping to a task.
Note: Windows unfortunately does not support the SIGUSR1 signal, so this feature is not currently available in that environment.
VIT was designed to be used in a request/response manner with the underlying TaskWarrior database, and by default its interface does not refresh when there are other changes happening outside of a specific VIT instance.
However, VIT provides some basic mechanisms that, when combined, allow for an easy implementation of an auto-refreshing interface:
- Signal handling: Sending the
SIGUSR1signal to a VIT process will cause it to refresh its interface (the equivalent of the{ACTION_REFRESH}action keybinding - PID management: Configuring
pid_dirinconfig.iniwill cause VIT to manage PID files inpid_dir. Executing VIT with the--list-pidsargument will output all current PIDs inpid_dirto standard output - Instance environment variable: VIT injects the
IS_VIT_INSTANCEenvironment variable into the environment of the running process. As such, processes invoked in that environment have access to the variable
vit-external-refresh.sh provides an example leveraging signals to externally refresh all local VIT interfaces. To use, make sure:
- The script is executable, and in your
PATHenvironment variable - You've properly set
pid_dirinconfig.ini
on-exit-refresh-vit.sh provides an example TaskWarrior hook that will automatically refresh all local VIT interfaces when using Taskwarrior directly. To use, make sure:
- The script is executable, named properly, and placed in TaskWarrior's hooks directory, usually
.task/hooks - vit-external-refresh.sh is configured correctly as above
The basic tools above can be used in other more complex scenarios, such as refreshing VIT's interface after an automated script updates one or more tasks. The implementation details will vary with the use case, and are left as an exercise for the user.