feat: add support for raw text and dividers

This commit is contained in:
Daylin Morgan 2022-09-13 16:40:07 -05:00
parent f62def68fc
commit 1350de0047
6 changed files with 178 additions and 63 deletions

View file

@ -4,6 +4,8 @@ TEMPLATES := $(shell find src/ -type f)
msg = $(call tprint,{a.bold}==>{a.end} {a.magenta}$(1){a.end} {a.bold}<=={a.end}) msg = $(call tprint,{a.bold}==>{a.end} {a.magenta}$(1){a.end} {a.bold}<=={a.end})
### task.mk development | args: --divider --align center
## bootstrap | generate local dev environment ## bootstrap | generate local dev environment
.PHONY: bootstrap .PHONY: bootstrap
bootstrap: bootstrap:
@ -12,9 +14,10 @@ bootstrap:
@mamba run -p ./env pip install yartsu @mamba run -p ./env pip install yartsu
@git config core.hooksPath .githooks @git config core.hooksPath .githooks
## lint | lint the python ## l, lint | lint the python
.PHONY: lint .PHONY: l lint
lint: l lint:
$(call msg,Linting)
@black generate.py @black generate.py
@black src/*.py --fast @black src/*.py --fast
@ -23,16 +26,6 @@ lint:
assets: assets:
yartsu -o assets/help.svg -t "make help" -- make --no-print-directory help yartsu -o assets/help.svg -t "make help" -- make --no-print-directory help
define list_files_py
from pathlib import Path
print("files in $(2)")
print([f.name for f in (Path("$(2)").iterdir())])
endef
## list-% | use pathlib.Path to list files
list-%:
$(call py,list_files_py,$*)
## release | release new version of task.mk ## release | release new version of task.mk
.PHONY: release .PHONY: release
release: version-check release: version-check
@ -47,6 +40,19 @@ release: version-check
.PHONY: clean .PHONY: clean
c clean: c clean:
@rm -f task.mk .task.mk @rm -f task.mk .task.mk
### | args: --divider
###
### examples of task.mk features | args: --divider --align center
define list_files_py
from pathlib import Path
print("files in $(2)")
print([f.name for f in (Path("$(2)").iterdir())])
endef
## list-% | use pathlib.Path to list files
### name the directory in rule (make list-src) | args: --align sep
list-%:
$(call py,list_files_py,$*)
.PHONY: version-check .PHONY: version-check
version-check: version-check:
@ -70,7 +76,6 @@ endef
test-bash: test-bash:
$(call tbash,bash_script,test bash multiline) $(call tbash,bash_script,test bash multiline)
define mlmsg define mlmsg
{a.b_yellow} {a.b_yellow}
It can even be multiline!{a.end} It can even be multiline!{a.end}
@ -87,6 +92,8 @@ info:
$(call tprint,$(mlmsg)) $(call tprint,$(mlmsg))
$(call tprint,{a.custom(fg=(148, 255, 15),bg=(103, 2, 15))}Custom Colors TOO!{a.end}) $(call tprint,{a.custom(fg=(148, 255, 15),bg=(103, 2, 15))}Custom Colors TOO!{a.end})
### | args: --divider
task.mk: $(TEMPLATES) generate.py task.mk: $(TEMPLATES) generate.py
./generate.py $(VERSION) > task.mk ./generate.py $(VERSION) > task.mk

View file

@ -33,6 +33,8 @@ class Ansi:
def __init__(self): def __init__(self):
self.setcode("end", "\033[0m") self.setcode("end", "\033[0m")
self.setcode("default", "\033[38m")
self.setcode("bg_default", "\033[48m")
for name, byte in color2byte.items(): for name, byte in color2byte.items():
self.setcode(name, f"\033[{fg(byte)}m") self.setcode(name, f"\033[{fg(byte)}m")
self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m") self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m")

View file

@ -4,8 +4,8 @@ PARAMS_COLOR ?= b_magenta
ACCENT_COLOR ?= b_yellow ACCENT_COLOR ?= b_yellow
GOAL_COLOR ?= $(ACCENT_COLOR) GOAL_COLOR ?= $(ACCENT_COLOR)
MSG_COLOR ?= faint MSG_COLOR ?= faint
DIVIDER_COLOR ?= default
HELP_SEP ?= | HELP_SEP ?= |
HELP_SORT ?= # sort goals alphabetically
# python f-string literals # python f-string literals
EPILOG ?= EPILOG ?=

View file

@ -1,40 +1,95 @@
#% extends "py-script.mk" %# #% extends "py-script.mk" %#
#% block name %#help#% endblock %# #% block name %#help#% endblock %#
#% block script %# #% block script %#
import argparse
from collections import namedtuple
import os import os
import re import re
##- '$(ansi_py)' -## ##- '$(ansi_py)' -##
pattern = re.compile(r"^## (.*) \| (.*)") MaxLens = namedtuple("MaxLens", "goal msg")
makefile = "" # double dollar signs to prevent make escaping them
for file in os.getenv("MAKEFILE_LIST").split(): pattern = re.compile(
with open(file, "r") as f: r"^## (?P<goal>.*) \| (?P<msg>.*)|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<args>.*?))?$$"
makefile += f.read() + "\n\n" )
def get_help(file): def rawargs(argstring):
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--align")
parser.add_argument("-d", "--divider", action="store_true")
return parser.parse_known_args(argstring.split())
def gen_makefile():
makefile = ""
for file in os.getenv("MAKEFILE_LIST").split():
with open(file, "r") as f:
makefile += f.read() + "\n\n"
return makefile
def parse_make(file):
for line in file.splitlines(): for line in file.splitlines():
match = pattern.search(line) match = pattern.search(line)
if match: if match:
if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"): if not os.getenv("SHOW_HIDDEN") and str(
continue match.groupdict().get("goal")
).startswith("_"):
pass
else: else:
yield match.groups() yield {k: v for k, v in match.groupdict().items() if v is not None}
print(f"""$(USAGE)""") def print_goal(goal, msg, max_goal_len):
goals = list(get_help(makefile))
if os.getenv("SORT_HELP", False):
goals.sort(key=lambda i: i[0])
goal_len = max(len(goal[0]) for goal in goals)
for goal, msg in goals:
print( print(
f" {ansi.$(GOAL_COLOR)}{goal:>{goal_len}}{ansi.end} $(HELP_SEP) {ansi.$(MSG_COLOR)}{msg}{ansi.end}" f" {ansi.$(GOAL_COLOR)}{goal:>{max_goal_len}}{ansi.end}"
" $(HELP_SEP) "
f"{ansi.$(MSG_COLOR)}{msg}{ansi.end}"
) )
print(f"""$(EPILOG)""")
def print_rawmsg(msg, argstr, maxlens):
args, unknown = rawargs(argstr)
if msg:
if args.align == "sep":
print(
f"{' '*(maxlens.goal+len('$(HELP_SEP)')+4)}{ansi.$(MSG_COLOR)}{msg}{ansi.end}"
)
elif args.align == "center":
print(f" {ansi.$(MSG_COLOR)}{msg.center(sum(maxlens))}{ansi.end}")
else:
print(f" {ansi.$(MSG_COLOR)}{msg}{ansi.end}")
if args.divider:
print(
f"{ansi.$(DIVIDER_COLOR)} {''*(len('$(HELP_SEP)')+sum(maxlens)+2)}{ansi.end}"
)
def print_help():
print(f"""$(USAGE)""")
items = list(parse_make(gen_makefile()))
maxlens = MaxLens(
*(max((len(item[x]) for item in items if x in item)) for x in ["goal", "msg"])
)
for item in items:
if "goal" in item:
print_goal(item["goal"], item["msg"], maxlens.goal)
if "rawmsg" in item:
print_rawmsg(item["rawmsg"], item.get("args", ""), maxlens)
if len(item) == 1 and "args" in item:
args, unknown = rawargs(item["args"])
if args.divider:
print(
" " + "" * (len("$(HELP_SEP)") + maxlens.goal + maxlens.msg + 2)
)
print(f"""$(EPILOG)""")
print_help()
#% endblock %# #% endblock %#

View file

@ -3,10 +3,7 @@
#% block script %# #% block script %#
##- '$(ansi_py)' -## ##- '$(ansi_py)' -##
codes_names = { codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
getattr(ansi, attr): attr
for attr in ansi.__dict__
}
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)): for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end)) print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end))

104
task.mk
View file

@ -1,7 +1,7 @@
# }> [github.com/daylinmorgan/task.mk] <{ # # }> [github.com/daylinmorgan/task.mk] <{ #
# Copyright (c) 2022 Daylin Morgan # Copyright (c) 2022 Daylin Morgan
# MIT License # MIT License
# version: v22.9.12-dev # version: v22.9.12-3-gcf1233a-dev
# #
# task.mk should be included at the bottom of your Makefile. # task.mk should be included at the bottom of your Makefile.
# See below for the standard configuration options that should be set prior to including this file. # See below for the standard configuration options that should be set prior to including this file.
@ -13,8 +13,8 @@ PARAMS_COLOR ?= b_magenta
ACCENT_COLOR ?= b_yellow ACCENT_COLOR ?= b_yellow
GOAL_COLOR ?= $(ACCENT_COLOR) GOAL_COLOR ?= $(ACCENT_COLOR)
MSG_COLOR ?= faint MSG_COLOR ?= faint
DIVIDER_COLOR ?= default
HELP_SEP ?= | HELP_SEP ?= |
HELP_SORT ?= # sort goals alphabetically
# python f-string literals # python f-string literals
EPILOG ?= EPILOG ?=
@ -95,42 +95,97 @@ pysh = printf "$(call create_string,$($(1)))" | python3
define help_py define help_py
import argparse
from collections import namedtuple
import os import os
import re import re
$(ansi_py) $(ansi_py)
pattern = re.compile(r"^## (.*) \| (.*)") MaxLens = namedtuple("MaxLens", "goal msg")
makefile = "" # double dollar signs to prevent make escaping them
for file in os.getenv("MAKEFILE_LIST").split(): pattern = re.compile(
with open(file, "r") as f: r"^## (?P<goal>.*) \| (?P<msg>.*)|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<args>.*?))?$$"
makefile += f.read() + "\n\n" )
def get_help(file): def rawargs(argstring):
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--align")
parser.add_argument("-d", "--divider", action="store_true")
return parser.parse_known_args(argstring.split())
def gen_makefile():
makefile = ""
for file in os.getenv("MAKEFILE_LIST").split():
with open(file, "r") as f:
makefile += f.read() + "\n\n"
return makefile
def parse_make(file):
for line in file.splitlines(): for line in file.splitlines():
match = pattern.search(line) match = pattern.search(line)
if match: if match:
if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"): if not os.getenv("SHOW_HIDDEN") and str(
continue match.groupdict().get("goal")
).startswith("_"):
pass
else: else:
yield match.groups() yield {k: v for k, v in match.groupdict().items() if v is not None}
print(f"""$(USAGE)""") def print_goal(goal, msg, max_goal_len):
goals = list(get_help(makefile))
if os.getenv("SORT_HELP", False):
goals.sort(key=lambda i: i[0])
goal_len = max(len(goal[0]) for goal in goals)
for goal, msg in goals:
print( print(
f" {ansi.$(GOAL_COLOR)}{goal:>{goal_len}}{ansi.end} $(HELP_SEP) {ansi.$(MSG_COLOR)}{msg}{ansi.end}" f" {ansi.$(GOAL_COLOR)}{goal:>{max_goal_len}}{ansi.end}"
" $(HELP_SEP) "
f"{ansi.$(MSG_COLOR)}{msg}{ansi.end}"
) )
print(f"""$(EPILOG)""")
def print_rawmsg(msg, argstr, maxlens):
args, unknown = rawargs(argstr)
if msg:
if args.align == "sep":
print(
f"{' '*(maxlens.goal+len('$(HELP_SEP)')+4)}{ansi.$(MSG_COLOR)}{msg}{ansi.end}"
)
elif args.align == "center":
print(f" {ansi.$(MSG_COLOR)}{msg.center(sum(maxlens))}{ansi.end}")
else:
print(f" {ansi.$(MSG_COLOR)}{msg}{ansi.end}")
if args.divider:
print(
f"{ansi.$(DIVIDER_COLOR)} {'─'*(len('$(HELP_SEP)')+sum(maxlens)+2)}{ansi.end}"
)
def print_help():
print(f"""$(USAGE)""")
items = list(parse_make(gen_makefile()))
maxlens = MaxLens(
*(max((len(item[x]) for item in items if x in item)) for x in ["goal", "msg"])
)
for item in items:
if "goal" in item:
print_goal(item["goal"], item["msg"], maxlens.goal)
if "rawmsg" in item:
print_rawmsg(item["rawmsg"], item.get("args", ""), maxlens)
if len(item) == 1 and "args" in item:
args, unknown = rawargs(item["args"])
if args.divider:
print(
" " + "─" * (len("$(HELP_SEP)") + maxlens.goal + maxlens.msg + 2)
)
print(f"""$(EPILOG)""")
print_help()
endef endef
@ -168,6 +223,8 @@ class Ansi:
def __init__(self): def __init__(self):
self.setcode("end", "\033[0m") self.setcode("end", "\033[0m")
self.setcode("default", "\033[38m")
self.setcode("bg_default", "\033[48m")
for name, byte in color2byte.items(): for name, byte in color2byte.items():
self.setcode(name, f"\033[{fg(byte)}m") self.setcode(name, f"\033[{fg(byte)}m")
self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m") self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m")
@ -230,10 +287,7 @@ define print_ansi_py
$(ansi_py) $(ansi_py)
codes_names = { codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
getattr(ansi, attr): attr
for attr in ansi.__dict__
}
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)): for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end)) print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end))