Wire edit command to open combat TOML files

This commit is contained in:
Jared Miller 2026-02-08 12:44:56 -05:00
parent 1b63f87da7
commit d0c33911f3
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 208 additions and 7 deletions

View file

@ -12,6 +12,7 @@ from mudlib.player import Player, players
# Combat moves will be injected after loading
combat_moves: dict[str, CombatMove] = {}
combat_content_dir: Path | None = None
async def do_attack(player: Player, target_args: str, move: CombatMove) -> None:
@ -170,7 +171,10 @@ def register_combat_commands(content_dir: Path) -> None:
Args:
content_dir: Path to directory containing combat move TOML files
"""
global combat_moves
global combat_moves, combat_content_dir
# Save content directory for use by edit command
combat_content_dir = content_dir
# Load all moves from content directory
combat_moves = load_moves(content_dir)

View file

@ -1,5 +1,7 @@
"""Edit command for entering the text editor."""
from pathlib import Path
from mudlib.commands import CommandDefinition, register
from mudlib.editor import Editor
from mudlib.player import Player
@ -9,21 +11,80 @@ async def cmd_edit(player: Player, args: str) -> None:
"""Enter the text editor.
Args:
player: The player executing the command
args: Command arguments (unused for now)
player: The player entering the editor
args: Optional argument - combat move name to edit
"""
args = args.strip()
async def save_callback(content: str) -> None:
await player.send("Content saved.\r\n")
# Default: blank editor
initial_content = ""
content_type = "text"
save_callback_fn = _make_default_save_callback(player)
toml_path: Path | None = None
# If args provided, try to load combat move TOML
if args:
from mudlib.combat.commands import combat_content_dir, combat_moves
if combat_content_dir is None:
await player.send("Combat content not loaded.\r\n")
return
# Look up the move - could be by name or alias
move = combat_moves.get(args)
# If not found, try to find by command name (for variant bases)
if move is None:
for m in combat_moves.values():
if m.command == args:
move = m
break
if move is None:
await player.send(f"Unknown content: {args}\r\n")
return
# Get the base command name to find the TOML file
toml_filename = f"{move.command}.toml"
toml_path = combat_content_dir / toml_filename
if not toml_path.exists():
await player.send(f"TOML file not found: {toml_filename}\r\n")
return
# Read the file content
initial_content = toml_path.read_text()
content_type = "toml"
save_callback_fn = _make_toml_save_callback(player, toml_path)
player.editor = Editor(
save_callback=save_callback,
content_type="text",
save_callback=save_callback_fn,
content_type=content_type,
color_depth=player.color_depth,
initial_content=initial_content,
)
player.mode_stack.append("editor")
await player.send("Entering editor. Type :h for help.\r\n")
def _make_default_save_callback(player: Player):
"""Create default save callback for blank editor."""
async def save_callback(content: str) -> None:
await player.send("Content saved.\r\n")
return save_callback
def _make_toml_save_callback(player: Player, toml_path: Path):
"""Create save callback for TOML file editing."""
async def save_callback(content: str) -> None:
toml_path.write_text(content)
await player.send(f"Saved {toml_path.name}\r\n")
return save_callback
# Register the edit command
register(CommandDefinition("edit", cmd_edit, mode="normal"))

View file

@ -165,3 +165,139 @@ async def test_editor_prompt_uses_cursor(player):
# This test verifies the cursor field exists and can be used for prompts
assert player.editor.cursor == 1
# Shell loop prompt: f" {player.editor.cursor + 1}> " = " 2> "
@pytest.mark.asyncio
async def test_edit_no_args_opens_blank_editor(player):
"""Test that edit with no args opens a blank editor."""
await cmd_edit(player, "")
assert player.editor is not None
assert player.editor.buffer == []
assert player.mode == "editor"
@pytest.mark.asyncio
async def test_edit_combat_move_opens_toml(player, tmp_path):
"""Test that edit roundhouse opens the TOML file for editing."""
from mudlib.combat import commands as combat_commands
# Create a test TOML file
toml_content = """name = "roundhouse"
aliases = ["rh"]
move_type = "attack"
stamina_cost = 8.0
timing_window_ms = 2000
"""
toml_file = tmp_path / "roundhouse.toml"
toml_file.write_text(toml_content)
# Set up combat moves
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
combat_commands.combat_content_dir = tmp_path
await cmd_edit(player, "roundhouse")
assert player.editor is not None
assert player.mode == "editor"
assert toml_content in "\n".join(player.editor.buffer)
@pytest.mark.asyncio
async def test_edit_combat_move_saves_to_disk(player, tmp_path, mock_writer):
"""Test that saving in editor writes back to the TOML file."""
from mudlib.combat import commands as combat_commands
# Create a test TOML file
original_content = """name = "roundhouse"
aliases = ["rh"]
move_type = "attack"
"""
toml_file = tmp_path / "roundhouse.toml"
toml_file.write_text(original_content)
# Set up combat moves
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
combat_commands.combat_content_dir = tmp_path
await cmd_edit(player, "roundhouse")
mock_writer.reset_mock()
# Modify the buffer
player.editor.buffer = [
'name = "roundhouse"',
'aliases = ["rh"]',
'move_type = "attack"',
"stamina_cost = 9.0",
]
# Save
await player.editor.handle_input(":w")
# Check that file was written
saved_content = toml_file.read_text()
assert "stamina_cost = 9.0" in saved_content
assert mock_writer.write.called
@pytest.mark.asyncio
async def test_edit_variant_base_opens_toml(player, tmp_path):
"""Test that edit punch opens punch.toml (variant base name)."""
from mudlib.combat import commands as combat_commands
# Create punch.toml with variants
toml_content = """name = "punch"
move_type = "attack"
[variants.left]
aliases = ["pl"]
"""
toml_file = tmp_path / "punch.toml"
toml_file.write_text(toml_content)
# Set up combat moves with variant
combat_commands.combat_moves = {
"punch left": MagicMock(command="punch", variant="left")
}
combat_commands.combat_content_dir = tmp_path
await cmd_edit(player, "punch")
assert player.editor is not None
assert player.mode == "editor"
assert "[variants.left]" in "\n".join(player.editor.buffer)
@pytest.mark.asyncio
async def test_edit_unknown_content_shows_error(player, mock_writer, tmp_path):
"""Test that edit nonexistent shows an error."""
from mudlib.combat import commands as combat_commands
combat_commands.combat_moves = {}
combat_commands.combat_content_dir = tmp_path
await cmd_edit(player, "nonexistent")
assert player.editor is None
assert player.mode == "normal"
assert mock_writer.write.called
output = "".join([call[0][0] for call in mock_writer.write.call_args_list])
assert "unknown" in output.lower()
assert "nonexistent" in output.lower()
@pytest.mark.asyncio
async def test_edit_combat_move_uses_toml_content_type(player, tmp_path):
"""Test that editor for combat moves uses toml content type."""
from mudlib.combat import commands as combat_commands
toml_file = tmp_path / "roundhouse.toml"
toml_file.write_text("name = 'roundhouse'\n")
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
combat_commands.combat_content_dir = tmp_path
await cmd_edit(player, "roundhouse")
assert player.editor is not None
assert player.editor.content_type == "toml"