Skip to content

Commit

Permalink
tabs to spaces, Console.markup applies to Table
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Mar 1, 2020
1 parent 9529453 commit 2a53a15
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 130 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.0] - Unreleased

### Added

- Added tab_size to Console and Text
- Added protocol.is_renderable for runtime check

### Changed

- Console.markup attribute now effects Table
- SeparatedConsoleRenderable and RichCast types

### Fixed

- Fixed tabs breaking rendering by converting to spaces

## [0.5.0] - 2020-02-23

### Changed
Expand Down
Binary file removed imgs/traceback.png
Binary file not shown.
Binary file removed imgs/traceback_windows.png
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rich"
homepage = "https://github.com/willmcgugan/rich"
documentation = "https://rich.readthedocs.io/en/latest/"
version = "0.5.0"
version = "0.6.0"
description = "Render rich text, tables, syntax highlighting, markdown and more to the terminal"
authors = ["Will McGugan <willmcgugan@gmail.com>"]
license = "MIT"
Expand Down
49 changes: 32 additions & 17 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,29 @@ def update(
return options


@runtime_checkable
class RichCast(Protocol):
"""An object that may be 'cast' to a console renderable."""

def __rich__(self) -> Union["ConsoleRenderable", str]: # pragma: no cover
...


@runtime_checkable
class ConsoleRenderable(Protocol):
"""An object that supports the console protocol."""

def __console__(
self, console: "Console", options: "ConsoleOptions"
) -> Iterable[Union["ConsoleRenderable", Segment, str]]: # pragma: no cover
) -> "RenderResult": # pragma: no cover
...


RenderableType = Union[ConsoleRenderable, Segment, str]
RenderResult = Iterable[RenderableType]
"""A type that may be rendered by Console."""
RenderableType = Union[ConsoleRenderable, RichCast, str]

"""The result of calling a __console__ method."""
RenderResult = Iterable[Union[RenderableType, Segment]]


_null_highlighter = NullHighlighter()
Expand Down Expand Up @@ -246,6 +257,7 @@ def __init__(
file: IO = None,
width: int = None,
height: int = None,
tab_size: int = 8,
record: bool = False,
markup: bool = True,
log_time: bool = True,
Expand All @@ -259,6 +271,7 @@ def __init__(
self.file = file or sys.stdout
self._width = width
self._height = height
self.tab_size = tab_size
self.record = record
self._markup = markup

Expand Down Expand Up @@ -410,7 +423,9 @@ def line(self, count: int = 1) -> None:
self._check_buffer()

def _render(
self, renderable: RenderableType, options: Optional[ConsoleOptions]
self,
renderable: Union[RenderableType, Segment],
options: Optional[ConsoleOptions],
) -> Iterable[Segment]:
"""Render an object in to an iterable of `Segment` instances.
Expand All @@ -425,17 +440,15 @@ def _render(
Returns:
Iterable[Segment]: An iterable of segments that may be rendered.
"""
render_iterable: Iterable[RenderableType]
render_options = options or self.options
render_iterable: RenderResult
if isinstance(renderable, Segment):
yield renderable
return
elif isinstance(renderable, ConsoleRenderable):
render_options = options or self.options
if isinstance(renderable, ConsoleRenderable):
render_iterable = renderable.__console__(self, render_options)
elif isinstance(renderable, str):
from .text import Text

yield from self._render(Text(renderable), render_options)
yield from self.render(self.render_str(renderable), render_options)
return
else:
raise errors.NotRenderableError(
Expand Down Expand Up @@ -522,20 +535,22 @@ def render_lines(
)
return lines

def render_str(self, text: str) -> "Text":
def render_str(self, text: str, style: Union[str, Style] = "") -> "Text":
"""Convert a string to a Text instance.
Args:
text (str): Text to render.
style (Union[str, Style], optional): Style to apply to rendered text.
Returns:
ConsoleRenderable: Renderable object.
"""
if self._markup:
return markup.render(text)
return markup.render(text, style=style)

from .text import Text

return markup.render_text(text)
return Text(text, style=style)

def _get_style(self, name: str) -> Optional[Style]:
"""Get a named style, or `None` if it doesn't exist.
Expand Down Expand Up @@ -681,10 +696,10 @@ def check_text() -> None:
check_text()
append(renderable)
elif isinstance(renderable, str):
render_str = renderable
renderable_str = renderable
if emoji:
render_str = _emoji_replace(render_str)
render_text = self.render_str(render_str)
renderable_str = _emoji_replace(renderable_str)
render_text = self.render_str(renderable_str)
append_text(_highlighter(render_text))
elif isinstance(renderable, Text):
append_text(renderable)
Expand Down
13 changes: 7 additions & 6 deletions rich/constrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@


class Constrain:
"""Constrain the width of a renderable to a given number of characters.
Args:
renderable (ConsoleRenderable): A renderable object.
width (int, optional): The maximum width (in characters) to render. Defaults to 80.
"""

def __init__(
self, renderable: ConsoleRenderable, width: Optional[int] = 80
) -> None:
"""Constrain the width of a renderable to a given number of characters.
Args:
renderable (ConsoleRenderable): A renderable object.
width (int, optional): The maximum width (in characters) to render. Defaults to 80.
"""
self.renderable = renderable
self.width = width

Expand Down
5 changes: 0 additions & 5 deletions rich/markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,6 @@ def repl_code(match: Match[str]) -> str:
yield markup[position:], None


def render_text(markup: str, style: Union[str, Style] = "") -> Text:
"""Convert markup to Text instance."""
return Text(markup, style=style)


def render(markup: str, style: Union[str, Style] = "") -> Text:
"""Render console markup in to a Text instance.
Expand Down
4 changes: 0 additions & 4 deletions rich/padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ def unpack(pad: "PaddingDimensions") -> Tuple[int, int, int, int]:
if len(pad) == 2:
pad_top, pad_right = cast(Tuple[int, int], pad)
return (pad_top, pad_right, pad_top, pad_right)
if len(pad) == 3:
raise ValueError(
f"1, 2 or 4 integers required for padding; {len(pad)} given"
)
if len(pad) == 4:
top, right, bottom, left = cast(Tuple[int, int, int, int], pad)
return (top, right, bottom, left)
Expand Down
6 changes: 4 additions & 2 deletions rich/render_width.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from operator import itemgetter
from typing import Iterable, NamedTuple, TYPE_CHECKING
from typing import Iterable, NamedTuple, TYPE_CHECKING, Union

from . import errors
from .segment import Segment
Expand Down Expand Up @@ -37,7 +37,9 @@ def with_maximum(self, width: int) -> "RenderWidth":
return RenderWidth(min(minimum, width), min(maximum, width))

@classmethod
def get(cls, renderable: "RenderableType", max_width: int) -> "RenderWidth":
def get(
cls, renderable: Union["RenderableType", "Segment"], max_width: int
) -> "RenderWidth":
"""Get desired width for a renderable."""
if hasattr(renderable, "__console__"):
get_console_width = getattr(renderable, "__console_width__", None)
Expand Down
46 changes: 17 additions & 29 deletions rich/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from ._tools import iter_first

WINDOWS = platform.system() == "Windows"
DEFAULT_THEME = "dark"
DEFAULT_THEME = "monokai"


class Syntax:
Expand All @@ -31,6 +31,8 @@ class Syntax:
start_line (int, optional): Starting number for line numbers. Defaults to 1.
line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
highlight_lines (Set[int]): A set of line numbers to highlight.
code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
tab_size (int, optional): Size of tabs. Defaults to 4.
"""

def __init__(
Expand All @@ -45,6 +47,7 @@ def __init__(
line_range: Tuple[int, int] = None,
highlight_lines: Set[int] = None,
code_width: Optional[int] = None,
tab_size: int = 4,
) -> None:
self.code = code
self.lexer_name = lexer_name
Expand All @@ -54,6 +57,7 @@ def __init__(
self.line_range = line_range
self.highlight_lines = highlight_lines or set()
self.code_width = code_width
self.tab_size = tab_size

self._style_cache: Dict[Any, Style] = {}
if not isinstance(theme, str) and issubclass(theme, PygmentsStyle):
Expand All @@ -76,6 +80,7 @@ def from_path(
start_line: int = 1,
highlight_lines: Set[int] = None,
code_width: Optional[int] = None,
tab_size: int = 4,
) -> "Syntax":
"""Construct a Syntax object from a file.
Expand All @@ -88,6 +93,8 @@ def from_path(
start_line (int, optional): Starting number for line numbers. Defaults to 1.
line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
highlight_lines (Set[int]): A set of line numbers to highlight.
code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
tab_size (int, optional): Size of tabs. Defaults to 4.
Returns:
[Syntax]: A Syntax object that may be printed to the console
Expand Down Expand Up @@ -139,8 +146,10 @@ def _highlight(self, lexer_name: str) -> Text:
try:
lexer = get_lexer_by_name(lexer_name)
except ClassNotFound:
return Text(self.code, style=default_style)
text = Text(style=default_style)
return Text(
self.code, justify="left", style=default_style, tab_size=self.tab_size
)
text = Text(justify="left", style=default_style, tab_size=self.tab_size)
append = text.append
_get_theme_style = self._get_theme_style
for token_type, token in lexer.get_tokens(self.code):
Expand Down Expand Up @@ -257,32 +266,11 @@ def __console__(self, console: Console, options: ConsoleOptions) -> RenderResult


if __name__ == "__main__": # pragma: no cover
CODE = r"""
def __init__(self):
self.b = self.

"""
# syntax = Syntax(CODE, "python", dedent=True, line_numbers=True, start_line=990)

from time import time

syntax = Syntax(CODE, "python", line_numbers=False, theme="monokai")
syntax = Syntax.from_path(
"rich/segment.py",
theme="fruity",
line_numbers=True,
# line_range=(190, 300),
highlight_lines={194},
)
console = Console(record=True)
start = time()
console.print(syntax)
elapsed = int((time() - start) * 1000)
print(f"{elapsed}ms")
import sys
from rich.console import Console

# print(Color.downgrade.cache_info())
# print(Color.parse.cache_info())
# print(Color.get_ansi_codes.cache_info())

# print(Style.parse.cache_info())
console = Console()

syntax = Syntax.from_path(sys.argv[1])
console.print(syntax)

0 comments on commit 2a53a15

Please sign in to comment.