Commit 7e9f954c authored by Bram Schoenmakers's avatar Bram Schoenmakers

Embed colors in strings

TopydoString is a string descendent that maintains information where
colors should be applied in a string. A user interface can pass a
function that transforms a color to a color representation that is
relevant there. That way, the topydo library does not make any
assumptions anymore on where colors should be applied.
parent 58079b25
......@@ -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):
......@@ -68,6 +69,10 @@ class PrettyPrinter(Printer):
for ppf in self.filters:
todo_str = ppf.filter(todo_str, p_todo)
# transform color annotations to ANSI codes
if isinstance(todo_str, TopydoString):
return todo_str.with_colors(lambda c: c.as_ansi())
else:
return todo_str
......
# 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):
super().__init__(p_content)
self.colors = {}
def append(self, p_string, p_color):
""" Append a string with the given color. """
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
def with_colors(self, p_transform_fn):
"""
Returns a string with color information at the right positions.
p_transform_fn is a function that takes a Color object and returns a
string representing the color (e.g. "#ff0000").
"""
result = self.data
for pos, color in sorted(self.colors.items(), reverse=True):
result = result[:pos] + p_transform_fn(color) + result[pos:]
return result
......@@ -21,6 +21,7 @@ import re
from topydo.lib.Color import Color
from topydo.lib.Config import config
from topydo.lib.PrettyPrinterFilter import PrettyPrinterFilter
from topydo.lib.TopydoString import TopydoString
class PrettyPrinterColorFilter(PrettyPrinterFilter):
......@@ -33,35 +34,27 @@ class PrettyPrinterColorFilter(PrettyPrinterFilter):
def filter(self, p_todo_str, p_todo):
""" Applies the colors. """
if config().colors():
p_todo_str = TopydoString(p_todo_str)
priority_color = config().priority_color(p_todo.priority())
project_color = config().project_color()
context_color = config().context_color()
metadata_color = config().metadata_color()
link_color = config().link_color()
neutral_color = Color('NEUTRAL')
# color projects / contexts
p_todo_str = re.sub(
r'\B(\+|@)(\S*\w)',
lambda m: (
context_color.as_ansi() if m.group(0)[0] == "@"
else project_color.as_ansi()) + m.group(0) + priority_color.as_ansi(),
p_todo_str)
# tags
p_todo_str = re.sub(r'\b\S+:[^/\s]\S*\b',
metadata_color.as_ansi() + r'\g<0>' + priority_color.as_ansi(),
p_todo_str)
colors = [
(r'\B@(\S*\w)', config().context_color()),
(r'\B\+(\S*\w)', config().project_color()),
(r'\b\S+:[^/\s]\S*\b', config().metadata_color()),
(r'(^|\s)(\w+:){1}(//\S+)', config().link_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.as_ansi() + r'\2\3' + priority_color.as_ansi(),
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)
p_todo_str += neutral_color.as_ansi()
p_todo_str.append('', neutral_color)
# color by priority
p_todo_str = priority_color.as_ansi() + p_todo_str
p_todo_str.set_color(0, priority_color)
return p_todo_str
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