diff --git a/pyproject.toml b/pyproject.toml index 813942c..081409f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rp" -version = "0.3.0" +version = "0.3.1" description = "Rich print files — markdown, JSON, code, and more — beautifully in your terminal" readme = "README.md" license = "MIT" diff --git a/src/rp/__init__.py b/src/rp/__init__.py index 0343ed4..c37360e 100644 --- a/src/rp/__init__.py +++ b/src/rp/__init__.py @@ -2,6 +2,6 @@ from __future__ import annotations -__version__ = "0.3.0" +__version__ = "0.3.1" __author__ = "Yunxiao Xu" __license__ = "MIT" diff --git a/src/rp/render.py b/src/rp/render.py index d74133b..9d4815c 100644 --- a/src/rp/render.py +++ b/src/rp/render.py @@ -83,12 +83,20 @@ def render( except (SyntaxError, ValueError): log_debug(options.debug, "render: invalid JSON, falling back to syntax") renderable = Syntax( - content, "json", theme=options.theme, line_numbers=line_numbers + content, + "json", + theme=options.theme, + line_numbers=line_numbers, + word_wrap=True, ) else: try: renderable = Syntax( - content, file_type, theme=options.theme, line_numbers=line_numbers + content, + file_type, + theme=options.theme, + line_numbers=line_numbers, + word_wrap=True, ) except Exception: log_debug( @@ -96,7 +104,11 @@ def render( f"render: unknown lexer {file_type!r}, falling back to plain text", ) renderable = Syntax( - content, "text", theme=options.theme, line_numbers=line_numbers + content, + "text", + theme=options.theme, + line_numbers=line_numbers, + word_wrap=True, ) padded = Padding(renderable, (1, 2)) diff --git a/tests/test_render.py b/tests/test_render.py index 4781a14..637982e 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -94,6 +94,7 @@ class TestRenderJson: "json", theme="monokai", line_numbers=False, + word_wrap=True, ) @@ -113,6 +114,7 @@ class TestRenderCode: "python", theme="monokai", line_numbers=True, + word_wrap=True, ) def test_renders_unknown_type_as_text_fallback(self, capture_console) -> None: @@ -136,11 +138,13 @@ class TestRenderCode: assert mock_syntax.call_args_list[0].kwargs == { "theme": "monokai", "line_numbers": True, + "word_wrap": True, } assert mock_syntax.call_args_list[1].args == ("some content", "text") assert mock_syntax.call_args_list[1].kwargs == { "theme": "monokai", "line_numbers": True, + "word_wrap": True, } def test_renders_rust(self, capture_console) -> None: @@ -156,9 +160,59 @@ class TestRenderCode: "rust", theme="monokai", line_numbers=True, + word_wrap=True, ) +class TestCodeWrap: + """Code wrapping (word_wrap) for syntax-highlighted output.""" + + def test_python_syntax_has_word_wrap( + self, capture_console, sample_python: str + ) -> None: + opts = RenderOptions(pager=False) + + with patch("rp.render.Syntax", wraps=Syntax) as mock_syntax: + render(sample_python, "python", capture_console, opts) + + mock_syntax.assert_called_once() + assert mock_syntax.call_args.kwargs["word_wrap"] is True + + def test_long_line_wraps_in_output(self, capture_console) -> None: + long_line = "x = " + "'a' * " * 200 + "1" + opts = RenderOptions(pager=False, line_numbers=False) + render(long_line, "python", capture_console, opts) + output = capture_console.file.getvalue() + assert len(output) > 0 + lines = output.split("\n") + non_empty = [line for line in lines if line.strip()] + assert len(non_empty) > 1, "Long line should wrap across multiple visual lines" + + def test_text_fallback_has_word_wrap(self, capture_console) -> None: + opts = RenderOptions(pager=False) + + def raise_on_bad_lexer(*args, **kwargs): + if args[1] == "unknown_xyz": + raise Exception("bad lexer") + return Syntax(*args, **kwargs) + + with patch("rp.render.Syntax", side_effect=raise_on_bad_lexer) as mock_syntax: + render("some text", "unknown_xyz", capture_console, opts) + + fallback_call = mock_syntax.call_args_list[-1] + assert fallback_call.args[1] == "text" + assert fallback_call.kwargs["word_wrap"] is True + + def test_json_fallback_has_word_wrap(self, capture_console) -> None: + opts = RenderOptions(pager=False) + + with patch("rp.render.Syntax", wraps=Syntax) as mock_syntax: + render("{invalid}", "json", capture_console, opts) + + mock_syntax.assert_called_once() + assert mock_syntax.call_args.kwargs["word_wrap"] is True + + class TestLineNumbers: """Line number auto-detection logic.""" diff --git a/uv.lock b/uv.lock index dbb7c35..cd2e69f 100644 --- a/uv.lock +++ b/uv.lock @@ -339,7 +339,7 @@ wheels = [ [[package]] name = "rp" -version = "0.3.0" +version = "0.3.1" source = { editable = "." } dependencies = [ { name = "pygments" },