mirror of
https://github.com/daylinmorgan/task.mk.git
synced 2024-12-22 01:50:44 -06:00
feat: add support for raw text and dividers
This commit is contained in:
parent
f62def68fc
commit
1350de0047
6 changed files with 178 additions and 63 deletions
35
Makefile
35
Makefile
|
@ -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})
|
||||
|
||||
### task.mk development | args: --divider --align center
|
||||
|
||||
## bootstrap | generate local dev environment
|
||||
.PHONY: bootstrap
|
||||
bootstrap:
|
||||
|
@ -12,9 +14,10 @@ bootstrap:
|
|||
@mamba run -p ./env pip install yartsu
|
||||
@git config core.hooksPath .githooks
|
||||
|
||||
## lint | lint the python
|
||||
.PHONY: lint
|
||||
lint:
|
||||
## l, lint | lint the python
|
||||
.PHONY: l lint
|
||||
l lint:
|
||||
$(call msg,Linting)
|
||||
@black generate.py
|
||||
@black src/*.py --fast
|
||||
|
||||
|
@ -23,16 +26,6 @@ lint:
|
|||
assets:
|
||||
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
|
||||
.PHONY: release
|
||||
release: version-check
|
||||
|
@ -47,6 +40,19 @@ release: version-check
|
|||
.PHONY: clean
|
||||
c clean:
|
||||
@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
|
||||
version-check:
|
||||
|
@ -70,7 +76,6 @@ endef
|
|||
test-bash:
|
||||
$(call tbash,bash_script,test bash multiline)
|
||||
|
||||
|
||||
define mlmsg
|
||||
{a.b_yellow}
|
||||
It can even be multiline!{a.end}
|
||||
|
@ -87,6 +92,8 @@ info:
|
|||
$(call tprint,$(mlmsg))
|
||||
$(call tprint,{a.custom(fg=(148, 255, 15),bg=(103, 2, 15))}Custom Colors TOO!{a.end})
|
||||
|
||||
### | args: --divider
|
||||
|
||||
task.mk: $(TEMPLATES) generate.py
|
||||
./generate.py $(VERSION) > task.mk
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ class Ansi:
|
|||
|
||||
def __init__(self):
|
||||
self.setcode("end", "\033[0m")
|
||||
self.setcode("default", "\033[38m")
|
||||
self.setcode("bg_default", "\033[48m")
|
||||
for name, byte in color2byte.items():
|
||||
self.setcode(name, f"\033[{fg(byte)}m")
|
||||
self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m")
|
||||
|
|
|
@ -4,8 +4,8 @@ PARAMS_COLOR ?= b_magenta
|
|||
ACCENT_COLOR ?= b_yellow
|
||||
GOAL_COLOR ?= $(ACCENT_COLOR)
|
||||
MSG_COLOR ?= faint
|
||||
DIVIDER_COLOR ?= default
|
||||
HELP_SEP ?= |
|
||||
HELP_SORT ?= # sort goals alphabetically
|
||||
|
||||
# python f-string literals
|
||||
EPILOG ?=
|
||||
|
|
89
src/help.py
89
src/help.py
|
@ -1,40 +1,95 @@
|
|||
#% extends "py-script.mk" %#
|
||||
#% block name %#help#% endblock %#
|
||||
#% block script %#
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import re
|
||||
|
||||
##- '$(ansi_py)' -##
|
||||
|
||||
pattern = re.compile(r"^## (.*) \| (.*)")
|
||||
MaxLens = namedtuple("MaxLens", "goal msg")
|
||||
|
||||
makefile = ""
|
||||
for file in os.getenv("MAKEFILE_LIST").split():
|
||||
# double dollar signs to prevent make escaping them
|
||||
pattern = re.compile(
|
||||
r"^## (?P<goal>.*) \| (?P<msg>.*)|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<args>.*?))?$$"
|
||||
)
|
||||
|
||||
|
||||
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 get_help(file):
|
||||
def parse_make(file):
|
||||
for line in file.splitlines():
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"):
|
||||
continue
|
||||
if not os.getenv("SHOW_HIDDEN") and str(
|
||||
match.groupdict().get("goal")
|
||||
).startswith("_"):
|
||||
pass
|
||||
else:
|
||||
yield match.groups()
|
||||
yield {k: v for k, v in match.groupdict().items() if v is not None}
|
||||
|
||||
|
||||
print(f"""$(USAGE)""")
|
||||
|
||||
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:
|
||||
def print_goal(goal, msg, max_goal_len):
|
||||
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 %#
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
#% block script %#
|
||||
##- '$(ansi_py)' -##
|
||||
|
||||
codes_names = {
|
||||
getattr(ansi, attr): attr
|
||||
for attr in ansi.__dict__
|
||||
}
|
||||
codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
|
||||
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
|
||||
print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end))
|
||||
|
||||
|
|
100
task.mk
100
task.mk
|
@ -1,7 +1,7 @@
|
|||
# }> [github.com/daylinmorgan/task.mk] <{ #
|
||||
# Copyright (c) 2022 Daylin Morgan
|
||||
# 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.
|
||||
# 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
|
||||
GOAL_COLOR ?= $(ACCENT_COLOR)
|
||||
MSG_COLOR ?= faint
|
||||
DIVIDER_COLOR ?= default
|
||||
HELP_SEP ?= |
|
||||
HELP_SORT ?= # sort goals alphabetically
|
||||
|
||||
# python f-string literals
|
||||
EPILOG ?=
|
||||
|
@ -95,42 +95,97 @@ pysh = printf "$(call create_string,$($(1)))" | python3
|
|||
|
||||
define help_py
|
||||
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import re
|
||||
|
||||
$(ansi_py)
|
||||
|
||||
pattern = re.compile(r"^## (.*) \| (.*)")
|
||||
MaxLens = namedtuple("MaxLens", "goal msg")
|
||||
|
||||
makefile = ""
|
||||
for file in os.getenv("MAKEFILE_LIST").split():
|
||||
# double dollar signs to prevent make escaping them
|
||||
pattern = re.compile(
|
||||
r"^## (?P<goal>.*) \| (?P<msg>.*)|^### (?P<rawmsg>.*?)?(?:\s?\| args: (?P<args>.*?))?$$"
|
||||
)
|
||||
|
||||
|
||||
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 get_help(file):
|
||||
def parse_make(file):
|
||||
for line in file.splitlines():
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"):
|
||||
continue
|
||||
if not os.getenv("SHOW_HIDDEN") and str(
|
||||
match.groupdict().get("goal")
|
||||
).startswith("_"):
|
||||
pass
|
||||
else:
|
||||
yield match.groups()
|
||||
yield {k: v for k, v in match.groupdict().items() if v is not None}
|
||||
|
||||
|
||||
print(f"""$(USAGE)""")
|
||||
|
||||
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:
|
||||
def print_goal(goal, msg, max_goal_len):
|
||||
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
|
||||
|
||||
|
@ -168,6 +223,8 @@ class Ansi:
|
|||
|
||||
def __init__(self):
|
||||
self.setcode("end", "\033[0m")
|
||||
self.setcode("default", "\033[38m")
|
||||
self.setcode("bg_default", "\033[48m")
|
||||
for name, byte in color2byte.items():
|
||||
self.setcode(name, f"\033[{fg(byte)}m")
|
||||
self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m")
|
||||
|
@ -230,10 +287,7 @@ define print_ansi_py
|
|||
|
||||
$(ansi_py)
|
||||
|
||||
codes_names = {
|
||||
getattr(ansi, attr): attr
|
||||
for attr in ansi.__dict__
|
||||
}
|
||||
codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__}
|
||||
for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)):
|
||||
print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end))
|
||||
|
||||
|
|
Loading…
Reference in a new issue