Add syntax highlighting to editor buffer display
This commit is contained in:
parent
a799b6716c
commit
0574457404
3 changed files with 110 additions and 2 deletions
|
|
@ -19,6 +19,7 @@ async def cmd_edit(player: Player, args: str) -> None:
|
|||
player.editor = Editor(
|
||||
save_callback=save_callback,
|
||||
content_type="text",
|
||||
color_depth=player.color_depth,
|
||||
)
|
||||
player.mode_stack.append("editor")
|
||||
await player.send("Entering editor. Type :h for help.\r\n")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from mudlib.render.highlight import highlight
|
||||
|
||||
|
||||
@dataclass
|
||||
class EditorResponse:
|
||||
|
|
@ -26,6 +28,7 @@ class Editor:
|
|||
undo_stack: list[list[str]] = field(default_factory=list)
|
||||
save_callback: Callable[[str], Awaitable[None]] | None = None
|
||||
content_type: str = "text" # hint for syntax highlighting later
|
||||
color_depth: str = "16" # "16", "256", or "truecolor"
|
||||
dirty: bool = False
|
||||
|
||||
def __init__(
|
||||
|
|
@ -33,10 +36,12 @@ class Editor:
|
|||
initial_content: str = "",
|
||||
save_callback: Callable[[str], Awaitable[None]] | None = None,
|
||||
content_type: str = "text",
|
||||
color_depth: str = "16",
|
||||
):
|
||||
"""Initialize editor with optional content."""
|
||||
self.save_callback = save_callback
|
||||
self.content_type = content_type
|
||||
self.color_depth = color_depth
|
||||
self.cursor = 0
|
||||
self.undo_stack = []
|
||||
self.dirty = False
|
||||
|
|
@ -62,7 +67,20 @@ class Editor:
|
|||
if not self.buffer:
|
||||
return "(empty buffer)"
|
||||
|
||||
# Calculate padding for line numbers
|
||||
# Use syntax highlighting if content_type is not "text"
|
||||
if self.content_type != "text":
|
||||
content = "\n".join(self.buffer)
|
||||
highlighted = highlight(
|
||||
content,
|
||||
language=self.content_type,
|
||||
color_depth=self.color_depth,
|
||||
line_numbers=True,
|
||||
)
|
||||
# If highlight() returned the original (unknown language), fall through
|
||||
if "\033[" in highlighted:
|
||||
return highlighted
|
||||
|
||||
# Fall back to plain line-number formatting
|
||||
max_line_num = len(self.buffer)
|
||||
padding = len(str(max_line_num))
|
||||
|
||||
|
|
@ -81,7 +99,22 @@ class Editor:
|
|||
start = max(0, line_num - 1 - context)
|
||||
end = min(len(self.buffer), line_num + context)
|
||||
|
||||
# Calculate padding for line numbers
|
||||
# Use syntax highlighting if content_type is not "text"
|
||||
if self.content_type != "text":
|
||||
# Get the context window
|
||||
context_lines = self.buffer[start:end]
|
||||
content = "\n".join(context_lines)
|
||||
highlighted = highlight(
|
||||
content,
|
||||
language=self.content_type,
|
||||
color_depth=self.color_depth,
|
||||
line_numbers=True,
|
||||
)
|
||||
# If highlight() returned highlighted content, use it
|
||||
if "\033[" in highlighted:
|
||||
return highlighted
|
||||
|
||||
# Fall back to plain line-number formatting
|
||||
max_line_num = end
|
||||
padding = len(str(max_line_num))
|
||||
|
||||
|
|
|
|||
|
|
@ -344,3 +344,77 @@ async def test_line_numbers_padded():
|
|||
# Find lines with numbers
|
||||
assert any(" 1 line 1" in line for line in lines)
|
||||
assert any("11 line 11" in line for line in lines)
|
||||
|
||||
|
||||
# Syntax highlighting tests
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_view_buffer_with_python_includes_ansi():
|
||||
"""Viewing buffer with content_type=python includes ANSI codes."""
|
||||
editor = Editor(
|
||||
initial_content="def foo():\n return 42",
|
||||
content_type="python",
|
||||
color_depth="16",
|
||||
)
|
||||
response = await editor.handle_input(":")
|
||||
# Should contain ANSI escape codes from syntax highlighting
|
||||
assert "\033[" in response.output
|
||||
# Should contain the code
|
||||
assert "def" in response.output
|
||||
assert "foo" in response.output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_view_buffer_with_text_no_ansi():
|
||||
"""Viewing buffer with content_type=text does NOT include ANSI codes."""
|
||||
editor = Editor(
|
||||
initial_content="plain text line 1\nplain text line 2", content_type="text"
|
||||
)
|
||||
response = await editor.handle_input(":")
|
||||
# Should NOT contain ANSI escape codes (plain line numbers)
|
||||
assert "\033[" not in response.output
|
||||
# Should contain plain line numbers
|
||||
assert "1 plain text line 1" in response.output
|
||||
assert "2 plain text line 2" in response.output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_view_specific_line_with_python_includes_ansi():
|
||||
"""Viewing a specific line with python content includes ANSI codes."""
|
||||
content = "\n".join([f"def func{i}():\n return {i}" for i in range(10)])
|
||||
editor = Editor(
|
||||
initial_content=content,
|
||||
content_type="python",
|
||||
color_depth="16",
|
||||
)
|
||||
response = await editor.handle_input(":5")
|
||||
# Should contain ANSI escape codes from syntax highlighting
|
||||
assert "\033[" in response.output
|
||||
# Should contain code from the context window
|
||||
assert "def" in response.output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_color_depth_passed_to_highlight():
|
||||
"""Color depth is passed through to highlight function."""
|
||||
editor = Editor(
|
||||
initial_content="x = 1\ny = 2",
|
||||
content_type="python",
|
||||
color_depth="256",
|
||||
)
|
||||
response = await editor.handle_input(":")
|
||||
# Should contain ANSI codes (256-color formatter)
|
||||
assert "\033[" in response.output
|
||||
# Should contain the code
|
||||
assert "x" in response.output
|
||||
assert "y" in response.output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_buffer_with_python_content_type():
|
||||
"""Empty buffer with content_type=python still works."""
|
||||
editor = Editor(content_type="python", color_depth="16")
|
||||
response = await editor.handle_input(":")
|
||||
# Should show empty buffer message
|
||||
assert "empty" in response.output.lower()
|
||||
|
|
|
|||
Loading…
Reference in a new issue