reformatting

This commit is contained in:
evilchili 2023-07-04 10:59:51 -07:00
parent 44fc415100
commit 6ad0ab68da
9 changed files with 254 additions and 314 deletions

View File

@ -40,6 +40,29 @@ dmsh = "site_tools.cli:dmsh"
[tool.poetry.dev-dependencies]
black = "^23.3.0"
isort = "^5.12.0"
pyproject-autoflake = "^1.0.2"
[build-system]
requires = ['poetry-core~=1.0']
build-backend = 'poetry.core.masonry.api'
[tool.black]
line-length = 120
target-version = ['py310']
[tool.isort]
multi_line_output = 3
line_length = 120
include_trailing_comma = true
[tool.autoflake]
check = false # return error code if changes are needed
in-place = true # make changes to files instead of printing diffs
recursive = true # drill down directories recursively
remove-all-unused-imports = true # remove all unused imports (not just those from the standard library)
ignore-init-module-imports = true # exclude __init__.py when removing unused imports
remove-duplicate-keys = true # remove all duplicate keys in objects
remove-unused-variables = true # remove unused variables

View File

@ -2,8 +2,8 @@ from pelican.settings import DEFAULT_CONFIG, get_settings_from_file
OPEN_BROWSER_ON_SERVE = True
DEV_SETTINGS_FILE_BASE = 'pelicanconf.py'
PUB_SETTINGS_FILE_BASE = 'publishconf.py'
DEV_SETTINGS_FILE_BASE = "pelicanconf.py"
PUB_SETTINGS_FILE_BASE = "publishconf.py"
SETTINGS = {}

View File

@ -1,102 +1,95 @@
import asyncio
import click
import os
import shlex
import shutil
import subprocess
import shlex
import sys
import typer
import webbrowser
import termios
import site_tools as st
import webbrowser
from collections import defaultdict
from enum import Enum
from pathlib import Path
from time import sleep
import click
import typer
from livereload import Server
from livereload.watcher import INotifyWatcher
from pathlib import Path
from pelican import main as pelican_main
from time import sleep
from rolltable.tables import RollTable
from typing_extensions import Annotated
from collections import defaultdict
import site_tools as st
from site_tools.content_manager import create
from site_tools.shell.interactive_shell import DMShell
from rolltable.tables import RollTable
CONFIG = defaultdict(dict)
CONFIG.update({
'settings_base': st.DEV_SETTINGS_FILE_BASE,
'settings_publish': st.PUB_SETTINGS_FILE_BASE,
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
'deploy_path': st.SETTINGS['OUTPUT_PATH'],
# Remote server configuration
'ssh_user': 'greg',
'ssh_host': 'froghat.club',
'ssh_port': '22',
'ssh_path': '/usr/local/deploy/deadsands/',
# Host and port for `serve`
'host': 'localhost',
'port': 8000,
# content manager config
'templates_path': 'markdown-templates',
# directory to watch for new assets
'import_path': 'imports',
# where new asseets will be made available
'production_host': 'deadsands.froghat.club',
# where to find roll table sources
'table_sources_path': 'sources',
})
CONFIG.update(
{
"settings_base": st.DEV_SETTINGS_FILE_BASE,
"settings_publish": st.PUB_SETTINGS_FILE_BASE,
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
"deploy_path": st.SETTINGS["OUTPUT_PATH"],
# Remote server configuration
"ssh_user": "greg",
"ssh_host": "froghat.club",
"ssh_port": "22",
"ssh_path": "/usr/local/deploy/deadsands/",
# Host and port for `serve`
"host": "localhost",
"port": 8000,
# content manager config
"templates_path": "markdown-templates",
# directory to watch for new assets
"import_path": "imports",
# where new asseets will be made available
"production_host": "deadsands.froghat.club",
# where to find roll table sources
"table_sources_path": "sources",
}
)
app = typer.Typer()
class ContentType(str, Enum):
post = 'post'
lore = 'lore'
monster = 'monster'
region = 'region'
location = 'location'
page = 'page'
post = "post"
lore = "lore"
monster = "monster"
region = "region"
location = "location"
page = "page"
class Die(str, Enum):
d100 = '100'
d20 = '20'
d12 = '12'
d10 = '10'
d6 = '6'
d4 = '4'
d100 = "100"
d20 = "20"
d12 = "12"
d10 = "10"
d6 = "6"
d4 = "4"
def pelican_run(cmd: list = [], publish=False) -> None:
settings = CONFIG['settings_publish' if publish else 'settings_base']
pelican_main(['-s', settings] + cmd)
settings = CONFIG["settings_publish" if publish else "settings_base"]
pelican_main(["-s", settings] + cmd)
@app.command()
def clean() -> None:
if os.path.isdir(CONFIG['deploy_path']):
shutil.rmtree(CONFIG['deploy_path'])
os.makedirs(CONFIG['deploy_path'])
if os.path.isdir(CONFIG["deploy_path"]):
shutil.rmtree(CONFIG["deploy_path"])
os.makedirs(CONFIG["deploy_path"])
@app.command()
def build() -> None:
subprocess.run(shlex.split('git submodule update --remote --merge'))
subprocess.run(shlex.split("git submodule update --remote --merge"))
pelican_run()
@app.command()
def watch() -> None:
import_path = Path(CONFIG['import_path'])
content_path = Path(st.SETTINGS['PATH'])
import_path = Path(CONFIG["import_path"])
content_path = Path(st.SETTINGS["PATH"])
def do_import():
assets = []
for src in import_path.rglob('*'):
for src in import_path.rglob("*"):
relpath = src.relative_to(import_path)
target = content_path / relpath
if src.is_dir():
@ -107,15 +100,14 @@ def watch() -> None:
continue
print(f"{target}: importing...")
src.link_to(target)
subprocess.run(shlex.split(f'git add {target}'))
uri = target.relative_to('content')
subprocess.run(shlex.split(f"git add {target}"))
uri = target.relative_to("content")
assets.append(f"https://{CONFIG['production_host']}/{uri}")
src.unlink()
if assets:
publish()
print('\n\t'.join(["\nImported Asset URLS:"] + assets))
print("\n\t".join(["\nImported Asset URLS:"] + assets))
print("\n")
watcher = INotifyWatcher()
watcher.watch(import_path, do_import)
watcher.start(do_import)
@ -124,33 +116,29 @@ def watch() -> None:
watcher.examine()
sleep(5)
@app.command()
def serve() -> None:
url = 'http://{host}:{port}/'.format(**CONFIG)
url = "http://{host}:{port}/".format(**CONFIG)
def cached_build():
pelican_run(['-ve', 'CACHE_CONTENT=true', 'LOAD_CONTENT_CACHE=true',
'SHOW_DRAFTS=true', f'SITEURL="{url}"'])
pelican_run(["-ve", "CACHE_CONTENT=true", "LOAD_CONTENT_CACHE=true", "SHOW_DRAFTS=true", f'SITEURL="{url}"'])
clean()
cached_build()
server = Server()
theme_path = st.SETTINGS['THEME']
theme_path = st.SETTINGS["THEME"]
watched_globs = [
CONFIG['settings_base'],
'{}/templates/**/*.html'.format(theme_path),
CONFIG["settings_base"],
"{}/templates/**/*.html".format(theme_path),
]
content_file_extensions = ['.md', '.rst']
content_file_extensions = [".md", ".rst"]
for extension in content_file_extensions:
content_glob = '{0}/**/*{1}'.format(st.SETTINGS['PATH'], extension)
content_glob = "{0}/**/*{1}".format(st.SETTINGS["PATH"], extension)
watched_globs.append(content_glob)
static_file_extensions = ['.css', '.js']
static_file_extensions = [".css", ".js"]
for extension in static_file_extensions:
static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension)
static_file_glob = "{0}/static/**/*{1}".format(theme_path, extension)
watched_globs.append(static_file_glob)
for glob in watched_globs:
@ -159,69 +147,46 @@ def serve() -> None:
if st.OPEN_BROWSER_ON_SERVE:
webbrowser.open(url)
server.serve(host=CONFIG['host'], port=CONFIG['port'],
root=CONFIG['deploy_path'])
server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"])
@app.command()
def publish() -> None:
clean()
pelican_run(publish=True)
subprocess.run(shlex.split(
'rsync --delete --exclude ".DS_Store" -pthrvz -c '
'-e "ssh -p {ssh_port}" '
'{} {ssh_user}@{ssh_host}:{ssh_path}'.format(
CONFIG['deploy_path'].rstrip('/') + '/',
**CONFIG
subprocess.run(
shlex.split(
'rsync --delete --exclude ".DS_Store" -pthrvz -c '
'-e "ssh -p {ssh_port}" '
"{} {ssh_user}@{ssh_host}:{ssh_path}".format(CONFIG["deploy_path"].rstrip("/") + "/", **CONFIG)
)
))
)
@app.command()
def restock(source: str = typer.Argument(
...,
help='The source file for the store.'
),
frequency: str = Annotated[
str,
typer.Option(
'default',
help='use the specified frequency from the source file'
)
],
die: Die = typer.Option(
20,
help='The size of the die for which to create a table'
),
def restock(
source: str = typer.Argument(..., help="The source file for the store."),
frequency: str = Annotated[str, typer.Option("default", help="use the specified frequency from the source file")],
die: Die = typer.Option(20, help="The size of the die for which to create a table"),
template_dir: str = Annotated[
str,
typer.Argument(
CONFIG['templates_path'],
CONFIG["templates_path"],
help="Override the default location for markdown templates.",
)
),
],
) -> None:
rt = RollTable([Path(source).read_text()], frequency=frequency, die=die, hide_rolls=True)
store = rt.datasources[0].metadata["store"]
rt = RollTable(
[Path(source).read_text()],
frequency=frequency,
die=die,
hide_rolls=True
)
store = rt.datasources[0].metadata['store']
click.edit(filename=create(
content_type='post',
title=store['title'],
template_dir=template_dir,
category='stores',
template='store',
extra_context=dict(
inventory=rt.as_markdown,
**store
click.edit(
filename=create(
content_type="post",
title=store["title"],
template_dir=template_dir,
category="stores",
template="store",
extra_context=dict(inventory=rt.as_markdown, **store),
)
))
)
@app.command()
def new(
@ -242,28 +207,26 @@ def new(
help="Override the default template for the content_type.",
),
template_dir: str = typer.Argument(
CONFIG['templates_path'],
CONFIG["templates_path"],
help="Override the default location for markdown templates.",
)
),
) -> None:
if not category:
match content_type:
case 'post':
case "post":
print("You must specify a category for 'post' content.")
sys.exit()
case 'monster':
category = 'bestiary'
case 'region':
category = 'locations'
case 'location':
category = 'locations'
case 'page':
category = 'pages'
case "monster":
category = "bestiary"
case "region":
category = "locations"
case "location":
category = "locations"
case "page":
category = "pages"
case _:
category = content_type.value
click.edit(filename=create(content_type.value, title, template_dir,
category, template or content_type.value))
click.edit(filename=create(content_type.value, title, template_dir, category, template or content_type.value))
def dmsh():
old_attrs = termios.tcgetattr(sys.stdin)
@ -272,6 +235,5 @@ def dmsh():
finally:
termios.tcsetattr(sys.stdin, termios.TCSANOW, old_attrs)
if __name__ == '__main__':
if __name__ == "__main__":
app()

View File

@ -1,38 +1,33 @@
import os
from configparser import ConfigParser
from pathlib import Path
from textwrap import dedent
from typing import Union, List
from typing import List, Union
import rich.repr
from prompt_toolkit import PromptSession
from prompt_toolkit.formatted_text import ANSI
from prompt_toolkit.output import ColorDepth
from prompt_toolkit.patch_stdout import patch_stdout
from prompt_toolkit.styles import Style
from rich.console import Console as _Console
from rich.markdown import Markdown
from rich.table import Column, Table
from rich.theme import Theme
from rich.table import Table, Column
from prompt_toolkit import PromptSession
from prompt_toolkit.patch_stdout import patch_stdout
from prompt_toolkit.formatted_text import ANSI
from prompt_toolkit.styles import Style
from prompt_toolkit.output import ColorDepth
BASE_STYLE = {
'help': 'cyan',
'bright': 'white',
'repr.str': 'dim',
'repr.brace': 'dim',
'repr.url': 'blue',
'table.header': 'white',
'toolbar.fg': '#888888',
'toolbar.bg': '#111111',
'toolbar.bold': '#FFFFFF',
'error': 'red',
"help": "cyan",
"bright": "white",
"repr.str": "dim",
"repr.brace": "dim",
"repr.url": "blue",
"table.header": "white",
"toolbar.fg": "#888888",
"toolbar.bg": "#111111",
"toolbar.bold": "#FFFFFF",
"error": "red",
}
def console_theme(theme_name: Union[str, None] = None) -> dict:
"""
Return a console theme as a dictionary.
@ -41,15 +36,12 @@ def console_theme(theme_name: Union[str, None] = None) -> dict:
theme_name (str):
"""
cfg = ConfigParser()
cfg.read_dict({'styles': BASE_STYLE})
cfg.read_dict({"styles": BASE_STYLE})
if theme_name:
theme_path = theme_name if theme_name else os.environ.get('DEFAULT_THEME', 'blue_train')
cfg.read(Theme(
Path(theme_path) / Path('console.cfg')
))
return cfg['styles']
theme_path = theme_name if theme_name else os.environ.get("DEFAULT_THEME", "blue_train")
cfg.read(Theme(Path(theme_path) / Path("console.cfg")))
return cfg["styles"]
@rich.repr.auto
class Console(_Console):
@ -79,17 +71,15 @@ class Console(_Console):
"""
def __init__(self, *args, **kwargs):
self._console_theme = console_theme(kwargs.get('theme', None))
self._overflow = 'ellipsis'
kwargs['theme'] = Theme(self._console_theme, inherit=False)
self._console_theme = console_theme(kwargs.get("theme", None))
self._overflow = "ellipsis"
kwargs["theme"] = Theme(self._console_theme, inherit=False)
super().__init__(*args, **kwargs)
self._session = PromptSession()
@property
def theme(self) -> Theme:
return self._console_theme
def prompt(self, lines: List, **kwargs) -> str:
"""
Print a list of lines, using the final line as a prompt.
@ -102,52 +92,45 @@ class Console(_Console):
"""
prompt_style = Style.from_dict({
# 'bottom-toolbar': f"{self.theme['toolbar.fg']} bg:{self.theme['toolbar.bg']}",
# 'toolbar-bold': f"{self.theme['toolbar.bold']}"
})
prompt_style = Style.from_dict(
{
# 'bottom-toolbar': f"{self.theme['toolbar.fg']} bg:{self.theme['toolbar.bg']}",
# 'toolbar-bold': f"{self.theme['toolbar.bold']}"
}
)
for line in lines[:-1]:
super().print(line)
with self.capture() as capture:
super().print(f"[prompt bold]{lines[-1]}>[/] ", end='')
super().print(f"[prompt bold]{lines[-1]}>[/] ", end="")
text = ANSI(capture.get())
# This is required to intercept key bindings and not mess up the
# prompt. Both the prompt and bottom_toolbar params must be functions
# for this to correctly regenerate the prompt after the interrupt.
with patch_stdout(raw=True):
return self._session.prompt(
lambda: text,
style=prompt_style,
color_depth=ColorDepth.TRUE_COLOR,
**kwargs)
return self._session.prompt(lambda: text, style=prompt_style, color_depth=ColorDepth.TRUE_COLOR, **kwargs)
def mdprint(self, txt: str, **kwargs) -> None:
"""
Like print(), but support markdown. Text will be dedented.
"""
self.print(Markdown(dedent(txt), justify='left'), **kwargs)
self.print(Markdown(dedent(txt), justify="left"), **kwargs)
def print(self, txt: str, **kwargs) -> None:
"""
Print text to the console, possibly truncated with an ellipsis.
"""
super().print(txt, overflow=self._overflow, **kwargs)
def debug(self, txt: str, **kwargs) -> None:
"""
Print text to the console with the current theme's debug style applied, if debugging is enabled.
"""
if os.environ.get('DEBUG', None):
self.print(dedent(txt), style='debug')
if os.environ.get("DEBUG", None):
self.print(dedent(txt), style="debug")
def error(self, txt: str, **kwargs) -> None:
"""
Print text to the console with the current theme's error style applied.
"""
self.print(dedent(txt), style='error')
self.print(dedent(txt), style="error")
def table(self, *cols: List[Column], **params) -> None:
"""
Print a rich table to the console with theme elements and styles applied.
@ -162,10 +145,10 @@ class Console(_Console):
caption_style=background_style,
style=background_style,
)
params['min_width'] = 80
width = os.environ.get('CONSOLE_WIDTH', 'auto')
if width == 'expand':
params['expand'] = True
elif width != 'auto':
params['width'] = int(width)
params["min_width"] = 80
width = os.environ.get("CONSOLE_WIDTH", "auto")
if width == "expand":
params["expand"] = True
elif width != "auto":
params["width"] = int(width)
return Table(*cols, **params)

View File

@ -1,48 +1,59 @@
import datetime
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from pelican.utils import sanitised_join, slugify
from pelican.writers import Writer
from pelican.utils import slugify, sanitised_join
from site_tools import SETTINGS
def create(content_type: str, title: str, template_dir: str,
category: str = None, source: str = None, template: str = None,
extra_context: dict = {}) -> str:
def create(
content_type: str,
title: str,
template_dir: str,
category: str = None,
source: str = None,
template: str = None,
extra_context: dict = {},
) -> str:
"""
Return the path to a new source file.
"""
base_path = Path.cwd()
def _slugify(s):
return slugify(s, regex_subs=SETTINGS['SLUG_REGEX_SUBSTITUTIONS'])
return slugify(s, regex_subs=SETTINGS["SLUG_REGEX_SUBSTITUTIONS"])
template_path = Path(template_dir)
template_name = f"{template or content_type}.md"
if not (template_path / template_name).exists():
print(f"Expected template {template_name} not found. Using default markdown template.")
template_name = 'default.md'
template_name = "default.md"
env = Environment(
loader=FileSystemLoader(template_path),
trim_blocks=True,
)
env.add_extension('site_tools.extensions.RollTable')
env.add_extension("site_tools.extensions.RollTable")
template_source = env.get_template(template_name)
target_filename = _slugify(title) + '.md'
target_filename = _slugify(title) + ".md"
relpath = Path(slugify(category)) if category else ''
relpath = Path(slugify(category)) if category else ""
target_path = base_path / SETTINGS['PATH'] / relpath
target_path = base_path / SETTINGS["PATH"] / relpath
dest = sanitised_join(str(target_path / target_filename))
SETTINGS['WRITE_SELECTED'].append(dest)
SETTINGS["WRITE_SELECTED"].append(dest)
writer = Writer(target_path, settings=SETTINGS)
writer.write_file(name=target_filename, template=template_source, context={
'title': title,
'tags': content_type,
'date': datetime.datetime.now(),
'filename': str(relpath / target_filename),
**extra_context
})
writer.write_file(
name=target_filename,
template=template_source,
context={
"title": title,
"tags": content_type,
"date": datetime.datetime.now(),
"filename": str(relpath / target_filename),
**extra_context,
},
)
return dest

View File

@ -1,13 +1,13 @@
import textwrap
from rolltable.tables import RollTable as _RT
from jinja2_simple_tags import StandaloneTag
from pathlib import Path
from jinja2_simple_tags import StandaloneTag
from rolltable.tables import RollTable as _RT
class RollTable(StandaloneTag):
tags = {"rolltable"}
def render(self, sources, frequency='default', die=8, indent=0, **kwargs):
rt = _RT([Path(f'sources/{s}.yaml').read_text() for s in sources],
frequency=frequency, die=die)
return textwrap.indent(rt.as_yaml(), ' ' * indent)
def render(self, sources, frequency="default", die=8, indent=0, **kwargs):
rt = _RT([Path(f"sources/{s}.yaml").read_text() for s in sources], frequency=frequency, die=die)
return textwrap.indent(rt.as_yaml(), " " * indent)

View File

@ -1,14 +1,14 @@
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import NestedCompleter
from prompt_toolkit.completion import FuzzyWordCompleter
from site_tools.cli import app
from enum import EnumMeta
from inspect import signature
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import FuzzyWordCompleter, NestedCompleter
from rich import print
from site_tools.cli import app
def dmsh():
session = PromptSession()
def cmd2dict(cmd):
@ -16,17 +16,14 @@ def dmsh():
if not sig.parameters:
return None
cmds = {}
for (k, v) in list(sig.parameters.items()):
for k, v in list(sig.parameters.items()):
print(v, dir(v))
if v.annotation.__class__ == EnumMeta:
cmds[k] = FuzzyWordCompleter([e.value for e in v.annotation])
else:
cmds[k] = None
return cmds
commands = dict(
site=dict((c.callback.__name__, cmd2dict(c.callback)) for c in app.registered_commands)
)
commands = dict(site=dict((c.callback.__name__, cmd2dict(c.callback)) for c in app.registered_commands))
print(commands)
completer = NestedCompleter.from_nested_dict(commands)
text = session.prompt("DM> ", completer=completer, complete_while_typing=True, enable_history_search=False)

View File

@ -1,18 +1,17 @@
import functools
from collections import namedtuple, defaultdict
from collections import defaultdict, namedtuple
from textwrap import dedent
from prompt_toolkit.completion import NestedCompleter
from site_tools.console import Console
from textwrap import dedent
COMMANDS = defaultdict(dict)
Command = namedtuple('Commmand', 'prompt,handler,usage,completer')
Command = namedtuple("Commmand", "prompt,handler,usage,completer")
def register_command(handler, usage, completer=None):
prompt = handler.__qualname__.split('.', -1)[0]
prompt = handler.__qualname__.split(".", -1)[0]
cmd = handler.__name__
if cmd not in COMMANDS[prompt]:
COMMANDS[prompt][cmd] = Command(
@ -22,7 +21,6 @@ def register_command(handler, usage, completer=None):
completer=completer,
)
def command(usage, completer=None, binding=None):
def decorator(func):
register_command(func, usage, completer)
@ -33,49 +31,38 @@ def command(usage, completer=None, binding=None):
return wrapper
return decorator
class BasePrompt(NestedCompleter):
def __init__(self, cache={}):
super(BasePrompt, self).__init__(self._nested_completer_map())
self._prompt = ''
self._prompt = ""
self._console = None
self._theme = None
self._toolbar = None
self._key_bindings = None
self._subshells = {}
self._cache = cache
self._name = 'Interactive Shell'
self._name = "Interactive Shell"
def _register_subshells(self):
for subclass in BasePrompt.__subclasses__():
if subclass.__name__ == self.__class__.__name__:
continue
self._subshells[subclass.__name__] = subclass(parent=self)
def _nested_completer_map(self):
return dict(
(cmd_name, cmd.completer) for (cmd_name, cmd) in COMMANDS[self.__class__.__name__].items()
)
return dict((cmd_name, cmd.completer) for (cmd_name, cmd) in COMMANDS[self.__class__.__name__].items())
def _get_help(self, cmd=None):
try:
return dedent(COMMANDS[self.__class__.__name__][cmd].usage)
except KeyError:
return self.usage
@property
def name(self):
return self._name
@property
def cache(self):
return self._cache
@property
def key_bindings(self):
return self._key_bindings
@property
def usage(self):
text = dedent(f"""
@ -86,56 +73,45 @@ class BasePrompt(NestedCompleter):
[title]COMMANDS[/title]
""")
for (name, cmd) in sorted(self.commands.items()):
for name, cmd in sorted(self.commands.items()):
text += f" [b]{name:10s}[/b] {cmd.handler.__doc__.strip()}\n"
return text
@property
def commands(self):
return COMMANDS[self.__class__.__name__]
@property
def console(self):
if not self._console:
self._console = Console(color_system='truecolor')
self._console = Console(color_system="truecolor")
return self._console
@property
def prompt(self):
return self._prompt
@property
def autocomplete_values(self):
return list(self.commands.keys())
@property
def toolbar(self):
return self._toolbar
@property
def key_bindings(self):
return self._key_bindings
def help(self, parts):
attr = None
if parts:
attr = parts[0]
self.console.print(self._get_help(attr))
return True
def process(self, cmd, *parts):
if cmd in self.commands:
return self.commands[cmd].handler(self, parts)
self.console.error(f"Command {cmd} not understood; try 'help' for help.")
def start(self, cmd=None):
while True:
if not cmd:
cmd = self.console.prompt(
self.prompt,
completer=self,
bottom_toolbar=self.toolbar,
key_bindings=self.key_bindings)
self.prompt, completer=self, bottom_toolbar=self.toolbar, key_bindings=self.key_bindings
)
if cmd:
cmd, *parts = cmd.split()
self.process(cmd, *parts)

View File

@ -1,47 +1,43 @@
from site_tools.shell.base import BasePrompt, command
from rolltable.tables import RollTable
from rich.table import Table
from pathlib import Path
from prompt_toolkit.application import get_app
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.application import get_app
from rich.table import Table
from rolltable.tables import RollTable
from site_tools.shell.base import BasePrompt, command
BINDINGS = KeyBindings()
class DMShell(BasePrompt):
def __init__(self, cache={}):
super().__init__(cache)
self._name = "DM Shell"
self._prompt = ['dm']
self._toolbar = [('class:bold', ' DMSH ')]
self._prompt = ["dm"]
self._toolbar = [("class:bold", " DMSH ")]
self._key_bindings = BINDINGS
self._register_subshells()
self._register_keybindings()
def _register_keybindings(self):
self._toolbar.extend(
[
("", " [H]elp "),
("", " [W]ild Magic Table "),
("", " [Q]uit "),
]
)
self._toolbar.extend([
('', " [H]elp "),
('', " [W]ild Magic Table "),
('', " [Q]uit "),
])
@self.key_bindings.add('c-q')
@self.key_bindings.add('c-d')
@self.key_bindings.add("c-q")
@self.key_bindings.add("c-d")
def quit(event):
self.quit()
@self.key_bindings.add('c-h')
@self.key_bindings.add("c-h")
def help(event):
self.help()
@self.key_bindings.add('c-w')
@self.key_bindings.add("c-w")
def wmt(event):
self.wmt()
@command(usage="""
[title]QUIT[/title]
@ -59,7 +55,6 @@ class DMShell(BasePrompt):
get_app().exit()
finally:
raise SystemExit("")
@command(usage="""
[title]HELP FOR THE HELP LORD[/title]
@ -76,7 +71,6 @@ class DMShell(BasePrompt):
"""
super().help(parts)
return True
@command(usage="""
[title]INCREMENT DATE[/title]
@ -91,8 +85,8 @@ class DMShell(BasePrompt):
Increment the date by one day.
"""
raise NotImplementedError()
@command(usage="""
@command(
usage="""
[title]LOCATION[/title]
[b]loc[/b] sets the party's location to the specified region of the Sahwat Desert.
@ -101,20 +95,15 @@ class DMShell(BasePrompt):
[link]loc LOCATION[/link]
""",
completer=WordCompleter([
"The Blooming Wastes",
"Dust River Canyon",
"Gopher Gulch",
"Calamity Ridge"
]))
completer=WordCompleter(["The Blooming Wastes", "Dust River Canyon", "Gopher Gulch", "Calamity Ridge"]),
)
def loc(self, parts=[]):
"""
Move the party to a new region of the Sahwat Desert.
"""
if parts:
self.cache['location'] = (' '.join(parts))
self.cache["location"] = " ".join(parts)
self.console.print(f"The party is in {self.cache['location']}.")
@command(usage="""
[title]OVERLAND TRAVEL[/title]
@ -129,7 +118,6 @@ class DMShell(BasePrompt):
Increment the date by one day and record
"""
raise NotImplementedError()
@command(usage="""
[title]WILD MAGIC TABLE[/title]
@ -145,18 +133,18 @@ class DMShell(BasePrompt):
sources/sahwat_magic_table.yaml \\
--frequency default --die 20[/link]
""")
def wmt(self, *parts, source='sahwat_magic_table.yaml'):
def wmt(self, *parts, source="sahwat_magic_table.yaml"):
"""
Generate a Wild Magic Table for resolving spell effects.
"""
if 'wmt' not in self.cache:
if "wmt" not in self.cache:
rt = RollTable(
[Path(f"{self.cache['table_sources_path']}/{source}").read_text()],
frequency='default',
frequency="default",
die=20,
)
table = Table(*rt.expanded_rows[0])
for row in rt.expanded_rows[1:]:
table.add_row(*row)
self.cache['wmt'] = table
self.console.print(self.cache['wmt'])
self.cache["wmt"] = table
self.console.print(self.cache["wmt"])