mirror of
https://github.com/daylinmorgan/task.mk.git
synced 2024-12-22 10:00:43 -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})
|
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
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 ?=
|
||||||
|
|
89
src/help.py
89
src/help.py
|
@ -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(
|
||||||
|
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:
|
with open(file, "r") as f:
|
||||||
makefile += f.read() + "\n\n"
|
makefile += f.read() + "\n\n"
|
||||||
|
return makefile
|
||||||
|
|
||||||
|
|
||||||
def get_help(file):
|
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 %#
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
100
task.mk
100
task.mk
|
@ -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(
|
||||||
|
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:
|
with open(file, "r") as f:
|
||||||
makefile += f.read() + "\n\n"
|
makefile += f.read() + "\n\n"
|
||||||
|
return makefile
|
||||||
|
|
||||||
|
|
||||||
def get_help(file):
|
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))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue