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 will be injected after loading
combat_moves: dict[str, CombatMove] = {} combat_moves: dict[str, CombatMove] = {}
combat_content_dir: Path | None = None
async def do_attack(player: Player, target_args: str, move: CombatMove) -> 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: Args:
content_dir: Path to directory containing combat move TOML files 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 # Load all moves from content directory
combat_moves = load_moves(content_dir) combat_moves = load_moves(content_dir)

View file

@ -1,5 +1,7 @@
"""Edit command for entering the text editor.""" """Edit command for entering the text editor."""
from pathlib import Path
from mudlib.commands import CommandDefinition, register from mudlib.commands import CommandDefinition, register
from mudlib.editor import Editor from mudlib.editor import Editor
from mudlib.player import Player from mudlib.player import Player
@ -9,21 +11,80 @@ async def cmd_edit(player: Player, args: str) -> None:
"""Enter the text editor. """Enter the text editor.
Args: Args:
player: The player executing the command player: The player entering the editor
args: Command arguments (unused for now) args: Optional argument - combat move name to edit
""" """
args = args.strip()
async def save_callback(content: str) -> None: # Default: blank editor
await player.send("Content saved.\r\n") 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( player.editor = Editor(
save_callback=save_callback, save_callback=save_callback_fn,
content_type="text", content_type=content_type,
color_depth=player.color_depth, color_depth=player.color_depth,
initial_content=initial_content,
) )
player.mode_stack.append("editor") player.mode_stack.append("editor")
await player.send("Entering editor. Type :h for help.\r\n") 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 the edit command
register(CommandDefinition("edit", cmd_edit, mode="normal")) 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 # This test verifies the cursor field exists and can be used for prompts
assert player.editor.cursor == 1 assert player.editor.cursor == 1
# Shell loop prompt: f" {player.editor.cursor + 1}> " = " 2> " # 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"