Coding style

- Moved path_colored into lscolors.
  - Made lscolors a package.
  - Moved LS_COLORS.sh and LSCOLORS into lscolors.
- Moved fill3.join into termstr so that lscolors
  can depend on termstr only.
This commit is contained in:
Andrew Hamilton 2022-05-11 11:11:37 +10:00
parent fe42fd36d4
commit 388c59aefd
13 changed files with 85 additions and 70 deletions

View file

@ -35,6 +35,7 @@ import time
import docopt import docopt
import fill3 import fill3
import fill3.terminal as terminal import fill3.terminal as terminal
import lscolors
import pygments.styles import pygments.styles
import pyinotify import pyinotify
import termstr import termstr
@ -122,7 +123,7 @@ class Entry:
if self.highlighted is not None: if self.highlighted is not None:
self.results[self.highlighted].is_highlighted = True self.results[self.highlighted].is_highlighted = True
row_appearance = self.widget.appearance() row_appearance = self.widget.appearance()
path = tools.path_colored(self.path) path = lscolors.path_colored(self.path)
padding = " " * (self.last_width - len(self.results) + 1) padding = " " * (self.last_width - len(self.results) + 1)
self.appearance_cache = [row_appearance[0] + padding + path] self.appearance_cache = [row_appearance[0] + padding + path]
if self.highlighted is not None: if self.highlighted is not None:
@ -136,7 +137,7 @@ class Entry:
result_html, result_styles = result.as_html() result_html, result_styles = result.as_html()
html_parts.append(result_html) html_parts.append(result_html)
styles.update(result_styles) styles.update(result_styles)
path = tools.path_colored(self.path) path = lscolors.path_colored(self.path)
padding = " " * (Entry.MAX_WIDTH - len(self.widget.widgets) + 1) padding = " " * (Entry.MAX_WIDTH - len(self.widget.widgets) + 1)
path_html, path_styles = termstr.TermStr(padding + path).as_html() path_html, path_styles = termstr.TermStr(padding + path).as_html()
return "".join(html_parts) + path_html, styles.union(path_styles) return "".join(html_parts) + path_html, styles.union(path_styles)
@ -560,7 +561,7 @@ class Log:
def log_message(self, message, timestamp=None, char_style=None): def log_message(self, message, timestamp=None, char_style=None):
if isinstance(message, list): if isinstance(message, list):
message = [part[1] if isinstance(part, tuple) else part for part in message] message = [part[1] if isinstance(part, tuple) else part for part in message]
message = fill3.join("", message) message = termstr.join("", message)
if char_style is not None: if char_style is not None:
message = termstr.TermStr(message, char_style) message = termstr.TermStr(message, char_style)
timestamp = time.strftime("%H:%M:%S", time.localtime()) if timestamp is None else timestamp timestamp = time.strftime("%H:%M:%S", time.localtime()) if timestamp is None else timestamp
@ -587,11 +588,11 @@ def highlight_chars(str_, style, marker="*"):
parts = str_.split(marker) parts = str_.split(marker)
highlighted_parts = [termstr.TermStr(part[0], style) + part[1:] highlighted_parts = [termstr.TermStr(part[0], style) + part[1:]
for part in parts[1:] if part != ""] for part in parts[1:] if part != ""]
return fill3.join("", [parts[0]] + highlighted_parts) return termstr.join("", [parts[0]] + highlighted_parts)
def get_status_help(): def get_status_help():
return fill3.join("\n", ["Statuses:"] + [" " + tools.STATUS_TO_TERMSTR[status] + " " + meaning return termstr.join("\n", ["Statuses:"] + [" " + tools.STATUS_TO_TERMSTR[status] + " " + meaning
for status, meaning in tools.STATUS_MEANINGS]) for status, meaning in tools.STATUS_MEANINGS])
@ -600,7 +601,7 @@ class Help:
def __init__(self, summary, screen): def __init__(self, summary, screen):
self.summary = summary self.summary = summary
self.screen = screen self.screen = screen
help_text = fill3.join("\n", [__doc__, KEYS_DOC, get_status_help()]) help_text = termstr.join("\n", [__doc__, KEYS_DOC, get_status_help()])
self.view = fill3.View.from_widget(fill3.Text(help_text)) self.view = fill3.View.from_widget(fill3.Text(help_text))
self.widget = fill3.Border(self.view, title="Help") self.widget = fill3.Border(self.view, title="Help")
portal = self.view.portal portal = self.view.portal
@ -777,7 +778,7 @@ class Screen:
"See option -e.") "See option -e.")
else: else:
path = self._summary.get_selection().path path = self._summary.get_selection().path
path_colored = tools.path_colored(path) path_colored = lscolors.path_colored(path)
line_num = self._summary.get_selection().entry[0].scroll_position[1] + 1 line_num = self._summary.get_selection().entry[0].scroll_position[1] + 1
self._log.log_message([in_green("Editing "), path_colored, self._log.log_message([in_green("Editing "), path_colored,
in_green(f" at line {line_num}")]) in_green(f" at line {line_num}")])
@ -801,7 +802,7 @@ class Screen:
def refresh(self): def refresh(self):
selection = self._summary.get_selection() selection = self._summary.get_selection()
tool_name = tools.tool_name_colored(selection.tool, selection.path) tool_name = tools.tool_name_colored(selection.tool, selection.path)
path_colored = tools.path_colored(selection.path) path_colored = lscolors.path_colored(selection.path)
self._log.log_message([in_green("Refreshing "), tool_name, in_green(" result of "), self._log.log_message([in_green("Refreshing "), tool_name, in_green(" result of "),
path_colored, in_green("")]) path_colored, in_green("")])
self._summary.refresh_result(selection) self._summary.refresh_result(selection)
@ -829,7 +830,7 @@ class Screen:
def xdg_open(self): def xdg_open(self):
path = self._summary.get_selection().path path = self._summary.get_selection().path
path_colored = tools.path_colored(path) path_colored = lscolors.path_colored(path)
self._log.log_message([in_green("Opening "), path_colored, in_green("")]) self._log.log_message([in_green("Opening "), path_colored, in_green("")])
subprocess.Popen(["xdg-open", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.Popen(["xdg-open", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -908,7 +909,7 @@ class Screen:
view.widget = widget.result view.widget = widget.result
tool_name = tools.tool_name_colored(widget.tool, widget.path) tool_name = tools.tool_name_colored(widget.tool, widget.path)
divider = " " + self._listing.top * 2 + " " divider = " " + self._listing.top * 2 + " "
self._listing.title = (tools.path_colored(widget.path) + divider + tool_name + " " + self._listing.title = (lscolors.path_colored(widget.path) + divider + tool_name + " " +
tools.STATUS_TO_TERMSTR[widget.status] + divider + "line " + str(y+1)) tools.STATUS_TO_TERMSTR[widget.status] + divider + "line " + str(y+1))
_STATUS_BAR = highlight_chars(" *help *quit *t*a*b:focus *turn *log *edit *next *sort" _STATUS_BAR = highlight_chars(" *help *quit *t*a*b:focus *turn *log *edit *next *sort"

View file

@ -67,13 +67,6 @@ STATUS_TO_TERMSTR = {status: termstr.TermStr(" ", termstr.CharStyle(bg_color=col
STATUS_TO_TERMSTR[Status.pending] = termstr.TermStr(".").fg_color(termstr.Color.grey_100) STATUS_TO_TERMSTR[Status.pending] = termstr.TermStr(".").fg_color(termstr.Color.grey_100)
def get_ls_color_codes():
with importlib.resources.open_text(eris, "LS_COLORS.sh") as lscolors_file:
codes = lscolors_file.readline().strip()[len("LS_COLORS='"):-len("';")]
return lscolors._parse_ls_colors(codes)
_LS_COLOR_CODES = get_ls_color_codes()
TIMEOUT = 60 TIMEOUT = 60
@ -134,11 +127,11 @@ def _syntax_highlight(text, lexer, style):
token_style["underline"]) token_style["underline"])
default_bg_color = _parse_rgb(style.background_color) default_bg_color = _parse_rgb(style.background_color)
default_style = termstr.CharStyle(bg_color=default_bg_color) default_style = termstr.CharStyle(bg_color=default_bg_color)
text = fill3.join("", [termstr.TermStr(text, _char_style_for_token_type( text = termstr.join("", [termstr.TermStr(text, _char_style_for_token_type(
token_type, default_bg_color, default_style)) token_type, default_bg_color, default_style))
for token_type, text in pygments.lex(text, lexer)]) for token_type, text in pygments.lex(text, lexer)])
text_widget = fill3.Text(text, pad_char=termstr.TermStr(" ").bg_color(default_bg_color)) text_widget = fill3.Text(text, pad_char=termstr.TermStr(" ").bg_color(default_bg_color))
return fill3.join("\n", text_widget.text) return termstr.join("\n", text_widget.text)
def _syntax_highlight_using_path(text, path): def _syntax_highlight_using_path(text, path):
@ -205,8 +198,8 @@ def metadata(path):
else: else:
name, value = line name, value = line
name = termstr.TermStr(name + ":").fg_color(termstr.Color.blue).ljust(16) name = termstr.TermStr(name + ":").fg_color(termstr.Color.blue).ljust(16)
text.append(name + fill3.join("", value) + "\n") text.append(name + termstr.join("", value) + "\n")
return Status.ok, fill3.join("", text) return Status.ok, termstr.join("", text)
@deps(deps={"python3-pygments"}, url="http://pygments.org/") @deps(deps={"python3-pygments"}, url="http://pygments.org/")
@ -275,7 +268,7 @@ def mypy(path):
def _colorize_coverage_report(lines): def _colorize_coverage_report(lines):
line_color = {"> ": termstr.Color.green, "! ": termstr.Color.grey_150, " ": None} line_color = {"> ": termstr.Color.green, "! ": termstr.Color.grey_150, " ": None}
return fill3.join("", [termstr.TermStr(line).fg_color(line_color[line[:2]]) for line in lines]) return termstr.join("", [termstr.TermStr(line).fg_color(line_color[line[:2]]) for line in lines])
@deps(deps={"python3-coverage"}, url="https://coverage.readthedocs.io/") @deps(deps={"python3-coverage"}, url="https://coverage.readthedocs.io/")
@ -335,7 +328,7 @@ def _get_mccabe_line_score(line):
def _colorize_mccabe(text): def _colorize_mccabe(text):
return fill3.join("", [ return termstr.join("", [
termstr.TermStr(line).fg_color(termstr.Color.yellow) termstr.TermStr(line).fg_color(termstr.Color.yellow)
if _get_mccabe_line_score(line) > 10 else line if _get_mccabe_line_score(line) > 10 else line
for line in text.splitlines(keepends=True)]) for line in text.splitlines(keepends=True)])
@ -398,7 +391,7 @@ def _image_to_text(image):
rows = [data[row_index*width:(row_index+1)*width] for row_index in range(image.height)] rows = [data[row_index*width:(row_index+1)*width] for row_index in range(image.height)]
if image.height % 2 == 1: if image.height % 2 == 1:
rows.append([None] * image.width) rows.append([None] * image.width)
return fill3.join("\n", [ return termstr.join("\n", [
termstr.TermStr(text, tuple(termstr.CharStyle(fg_color=top_pixel, bg_color=bottom_pixel) termstr.TermStr(text, tuple(termstr.CharStyle(fg_color=top_pixel, bg_color=bottom_pixel)
for top_pixel, bottom_pixel in zip(rows[index], rows[index+1]))) for top_pixel, bottom_pixel in zip(rows[index], rows[index+1])))
for index in range(0, image.height, 2)]) for index in range(0, image.height, 2)])
@ -568,7 +561,7 @@ class Result:
async def run(self, log, runner): async def run(self, log, runner):
tool_name = tool_name_colored(self.tool, self.path) tool_name = tool_name_colored(self.tool, self.path)
path = path_colored(self.path) path = lscolors.path_colored(self.path)
log.log_message(["Running ", tool_name, " on ", path, ""]) log.log_message(["Running ", tool_name, " on ", path, ""])
self.set_status(Status.running) self.set_status(Status.running)
fill3.APPEARANCE_CHANGED_EVENT.set() fill3.APPEARANCE_CHANGED_EVENT.set()
@ -695,35 +688,10 @@ def run_tool_no_error(path, tool):
pygments.styles.get_style_by_name(os.environ["PYGMENT_STYLE"])) pygments.styles.get_style_by_name(os.environ["PYGMENT_STYLE"]))
def _charstyle_of_path(path):
color_code = lscolors.color_code_for_path(path, _LS_COLOR_CODES)
if color_code is None:
return termstr.CharStyle()
term_text = termstr.ESC + "[" + color_code + "m-"
return termstr.TermStr.from_term(term_text).style[0]
@functools.lru_cache(maxsize=100)
def path_colored(path):
char_style = _charstyle_of_path(path)
if path.startswith("./"):
path = path[2:]
dirname, basename = os.path.split(path)
if dirname == "":
return termstr.TermStr(basename, char_style)
else:
dirname = dirname + os.path.sep
dir_style = _charstyle_of_path(os.path.sep)
parts = [termstr.TermStr(part, dir_style) for part in dirname.split(os.path.sep)]
path_sep = termstr.TermStr(os.path.sep).fg_color(termstr.Color.grey_100)
dir_name = fill3.join(path_sep, parts)
return dir_name + termstr.TermStr(basename, char_style)
@functools.lru_cache(maxsize=100) @functools.lru_cache(maxsize=100)
def tool_name_colored(tool, path): def tool_name_colored(tool, path):
char_style = (termstr.CharStyle(is_bold=True) if tool in generic_tools() char_style = (termstr.CharStyle(is_bold=True) if tool in generic_tools()
else _charstyle_of_path(path)) else lscolors._charstyle_of_path(path))
return termstr.TermStr(tool.__name__, char_style) return termstr.TermStr(tool.__name__, char_style)

View file

@ -47,7 +47,7 @@ def make_listing_page(url_path):
result = index[(path, tool_name)] result = index[(path, tool_name)]
tool = getattr(tools, tool_name) tool = getattr(tools, tool_name)
tool_name_colored = tools.tool_name_colored(tool, path) tool_name_colored = tools.tool_name_colored(tool, path)
header = fill3.appearance_as_html([tools.path_colored(path) + " - " + tool_name_colored, header = fill3.appearance_as_html([lscolors.path_colored(path) + " - " + tool_name_colored,
termstr.TermStr(" ").underline() * 100]) termstr.TermStr(" ").underline() * 100])
body = fill3.appearance_as_html(result.appearance()) body = fill3.appearance_as_html(result.appearance())
return make_page(header + body, f"{path} - {tool_name}") return make_page(header + body, f"{path} - {tool_name}")

View file

@ -18,8 +18,8 @@ setup(name="eris",
license="Artistic 2.0", license="Artistic 2.0",
python_requires=">=3.10.0", python_requires=">=3.10.0",
packages=["eris"], packages=["eris"],
py_modules=["lscolors", "sorted_collection"], py_modules=["sorted_collection"],
package_data={"eris": ["LS_COLORS.sh", "tools.toml"]}, package_data={"eris": ["tools.toml"]},
entry_points={"console_scripts": entry_points={"console_scripts":
["eris=eris.__main__:entry_point", "eris-worker=eris.worker:main", ["eris=eris.__main__:entry_point", "eris-worker=eris.worker:main",
"eris-webserver=eris.webserver:main", "pydoc_color=eris.pydoc_color:main"]}, "eris-webserver=eris.webserver:main", "pydoc_color=eris.pydoc_color:main"]},

View file

@ -10,6 +10,7 @@ import tempfile
import unittest import unittest
import fill3 import fill3
import termstr
import golden import golden
import eris.__main__ as __main__ import eris.__main__ as __main__
@ -24,7 +25,7 @@ _DIMENSIONS = (100, 60)
def _widget_to_string(widget, dimensions=_DIMENSIONS): def _widget_to_string(widget, dimensions=_DIMENSIONS):
appearance = (widget.appearance() if dimensions is None appearance = (widget.appearance() if dimensions is None
else widget.appearance_for(dimensions)) else widget.appearance_for(dimensions))
return str(fill3.join("\n", appearance)) return str(termstr.join("\n", appearance))
def _touch(path): def _touch(path):

View file

@ -7,7 +7,7 @@ import shutil
import unittest import unittest
import unittest.mock import unittest.mock
import fill3 import termstr
import golden import golden
import eris.tools as tools import eris.tools as tools
@ -31,7 +31,7 @@ class ExecutablesTestCase(unittest.TestCase):
def widget_to_string(widget): def widget_to_string(widget):
return str(fill3.join("\n", widget.appearance())) return str(termstr.join("\n", widget.appearance()))
@contextlib.contextmanager @contextlib.contextmanager

View file

@ -40,19 +40,10 @@ def appearance_dimensions(appearance):
return 0, 0 return 0, 0
def join(seperator, parts):
if parts == []:
return ""
try:
return seperator.join(parts)
except TypeError:
return termstr.TermStr(seperator).join(parts)
def join_horizontal(appearances): def join_horizontal(appearances):
heights = set(len(appearance) for appearance in appearances) heights = set(len(appearance) for appearance in appearances)
assert len(heights) == 1, heights assert len(heights) == 1, heights
return [join("", parts) for parts in zip(*appearances)] return [termstr.join("", parts) for parts in zip(*appearances)]
def even_widths(column_widgets, width): def even_widths(column_widgets, width):

View file

@ -6,6 +6,7 @@ import unittest
import fill3 import fill3
import fill3.terminal as terminal import fill3.terminal as terminal
import termstr
class WidgetTests(unittest.TestCase): class WidgetTests(unittest.TestCase):
@ -14,7 +15,7 @@ class WidgetTests(unittest.TestCase):
TEXT_B = fill3.Text("B") TEXT_B = fill3.Text("B")
def assert_string(self, appearance, expected_string): def assert_string(self, appearance, expected_string):
self.assertEqual(str(fill3.join("\n", appearance)), expected_string) self.assertEqual(str(termstr.join("\n", appearance)), expected_string)
def test_rows_widget(self): def test_rows_widget(self):
rows = fill3.Row([self.TEXT_A, self.TEXT_B]) rows = fill3.Row([self.TEXT_A, self.TEXT_B])

View file

@ -3,11 +3,17 @@
"""Give coloring for file types as in the ls command.""" """Give coloring for file types as in the ls command."""
import functools
import importlib
import importlib.resources
import os import os
import os.path import os.path
import stat import stat
import syslog import syslog
import lscolors
import termstr
__version__ = "v2022.04.30" __version__ = "v2022.04.30"
@ -127,3 +133,37 @@ def color_code_for_path(path, color_codes):
if extension is not None: if extension is not None:
color_key = extension color_key = extension
return color_codes.get(color_key, None) return color_codes.get(color_key, None)
def get_ls_color_codes():
with importlib.resources.open_text(lscolors, "LS_COLORS.sh") as lscolors_file:
codes = lscolors_file.readline().strip()[len("LS_COLORS='"):-len("';")]
return _parse_ls_colors(codes)
_LS_COLOR_CODES = get_ls_color_codes()
def _charstyle_of_path(path):
color_code = color_code_for_path(path, _LS_COLOR_CODES)
if color_code is None:
return termstr.CharStyle()
term_text = termstr.ESC + "[" + color_code + "m-"
return termstr.TermStr.from_term(term_text).style[0]
@functools.lru_cache(maxsize=100)
def path_colored(path):
char_style = _charstyle_of_path(path)
if path.startswith("./"):
path = path[2:]
dirname, basename = os.path.split(path)
if dirname == "":
return termstr.TermStr(basename, char_style)
else:
dirname = dirname + os.path.sep
dir_style = _charstyle_of_path(os.path.sep)
parts = [termstr.TermStr(part, dir_style) for part in dirname.split(os.path.sep)]
path_sep = termstr.TermStr(os.path.sep).fg_color(termstr.Color.grey_100)
dir_name = termstr.join(path_sep, parts)
return dir_name + termstr.TermStr(basename, char_style)

View file

@ -1,12 +1,14 @@
#!/usr/bin/env python3.10 #!/usr/bin/env python3.10
import os
try: try:
from setuptools import setup from setuptools import setup
except ImportError: except ImportError:
from distutils.core import setup from distutils.core import setup
REPO_PATH = os.path.dirname(os.getcwd())
setup(name="lscolors", setup(name="lscolors",
version="v2022.04.30", version="v2022.04.30",
description=("Give coloring for file types as in the ls command."), description=("Give coloring for file types as in the ls command."),
@ -14,4 +16,6 @@ setup(name="lscolors",
author="Andrew Hamilton", author="Andrew Hamilton",
author_email="and_hamilton@yahoo.com", author_email="and_hamilton@yahoo.com",
license="Artistic 2.0", license="Artistic 2.0",
py_modules=["lscolors"]) packages=["lscolors"],
package_data={"lscolors": ["LS_COLORS.sh"]},
install_requires=[f"termstr @ file://{REPO_PATH}/termstr"])

View file

@ -177,6 +177,15 @@ def _pad_wide_chars(str_):
return str_ if len(padded_str) == len(str_) else padded_str return str_ if len(padded_str) == len(str_) else padded_str
def join(seperator, parts):
if parts == []:
return ""
try:
return seperator.join(parts)
except TypeError:
return TermStr(seperator).join(parts)
class TermStr(collections.UserString): class TermStr(collections.UserString):
def __init__(self, data, style=CharStyle()): def __init__(self, data, style=CharStyle()):