From e69d216d9780d19571bd722059e003ee4eaffa65 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 9 Mar 2020 17:28:49 +0000 Subject: [PATCH] fix for line wrapping --- CHANGELOG.md | 4 ++++ rich/_wrap.py | 26 +++++++++++++++----------- rich/containers.py | 3 +++ rich/progress.py | 21 +++++++++++---------- rich/table.py | 31 +++++++++---------------------- rich/text.py | 13 ------------- 6 files changed, 42 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a3e0b2..11f22036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Console.show_cursor method +### Fixed + +- Fixed wrapping when a single word was too large to fit in a line + ## [0.6.0] - 2020-03-03 ### Added diff --git a/rich/_wrap.py b/rich/_wrap.py index 992348ef..f36edc26 100644 --- a/rich/_wrap.py +++ b/rich/_wrap.py @@ -4,24 +4,28 @@ from typing import Iterable, List, Tuple re_word = re.compile(r"\s*\S+\s*") -def words(text: str) -> Iterable[Tuple[int, str]]: +def words(text: str) -> Iterable[Tuple[int, int, str]]: position = 0 word_match = re_word.match(text, position) while word_match is not None: - start, position = word_match.span() + start, end = word_match.span() word = word_match.group(0) - yield start, word - word_match = re_word.match(text, position) + yield start, end, word + word_match = re_word.match(text, end) def divide_line(text: str, width: int) -> List[int]: divides: List[int] = [] append = divides.append - line_size = 0 - for position, word in words(text): - if line_size + len(word.rstrip()) > width: - if position: - append(position) - line_size = 0 - line_size += len(word) + line_position = 0 + for start, end, word in words(text): + if line_position + len(word.rstrip()) > width: + if line_position and start: + append(start) + line_position = len(word) + else: + divides.extend(range(start or width, end + 1, width)) + line_position = len(word) % width + else: + line_position += len(word) return divides diff --git a/rich/containers.py b/rich/containers.py index c92163f6..a8602cfc 100644 --- a/rich/containers.py +++ b/rich/containers.py @@ -57,6 +57,9 @@ class Lines: def __init__(self, lines: Iterable["Text"] = ()) -> None: self._lines: List["Text"] = list(lines) + def __repr__(self) -> str: + return f"Lines({self._lines!r})" + def __iter__(self) -> Iterator["Text"]: return iter(self._lines) diff --git a/rich/progress.py b/rich/progress.py index d4034754..99c14600 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -3,7 +3,7 @@ from dataclasses import dataclass, field import sys from time import monotonic from threading import Event, RLock, Thread -from typing import Callable, Dict, List, Optional, NewType, Union +from typing import Any, Callable, Dict, List, Optional, NewType, Union from .bar import Bar from .console import Console, RenderableType @@ -29,7 +29,7 @@ class Task: total: float completed: float visible: bool = True - fields: Dict[str, str] = field(default_factory=dict) + fields: Dict[str, Any] = field(default_factory=dict) @property def finished(self) -> bool: @@ -45,7 +45,7 @@ class Task: class RefreshThread(Thread): - """A thread that calls refresh on the Process object at regular intervals.""" + """A thread that calls refresh() on the Process object at regular intervals.""" def __init__(self, progress: "Progress", refresh_per_second: int = 10) -> None: self.progress = progress @@ -59,10 +59,7 @@ class RefreshThread(Thread): def run(self) -> None: while not self.done.wait(1.0 / self.refresh_per_second): - try: - self.progress.refresh() - except Exception as error: - raise + self.progress.refresh() def bar_widget(task: Task) -> Bar: @@ -95,11 +92,13 @@ class Progress: @property def tasks(self) -> List[TaskID]: + """Get a list of task IDs.""" with self._lock: return list(self._tasks.keys()) @property def finished(self) -> bool: + """Check if all tasks have been completed.""" with self._lock: if not self._tasks: return True @@ -125,11 +124,12 @@ class Progress: def update( self, task_id: TaskID, + *, total: float = None, completed: float = None, advance: float = None, visible: bool = None, - **fields: RenderableType + **fields: Any ) -> None: with self._lock: task = self._tasks[task_id] @@ -143,6 +143,7 @@ class Progress: task.visible = True def refresh(self) -> None: + """Refresh (render) the progress information.""" with self._lock: self._live_render.set_renderable(self._table) self.console.print(self._live_render) @@ -192,14 +193,14 @@ class Progress: if __name__ == "__main__": - import random + import time with Progress() as progress: task1 = progress.add_task("[red]Downloading") task2 = progress.add_task("[green]Processing") - task3 = progress.add_task("[cyan]Cooking...") + task3 = progress.add_task("[cyan]Cooking") while not progress.finished: progress.update(task1, advance=1.0) diff --git a/rich/table.py b/rich/table.py index cd128307..39cc85e6 100644 --- a/rich/table.py +++ b/rich/table.py @@ -303,6 +303,7 @@ class Table: flex_widths = [_range.span for _range in width_ranges] if not any(flex_widths): flex_widths = [1] * len(flex_widths) + flex_widths = [0 if column.no_wrap else 1 for column in columns] excess_width = table_width - max_width widths = [ width - excess_width @@ -457,29 +458,15 @@ class Table: if __name__ == "__main__": # pragma: no cover - from .console import Console - from . import box - c = Console(markup=True) - table = Table( - Column( - "Foo", footer=Text("Total", justify="right"), footer_style="bold", ratio=1 - ), - Column("Bar", style="red", footer="123", ratio=1), - expand=True, - show_footer=True, - show_edge=True, - style="on blue", + c = Console(width=80) + table = Table() + table.add_column(no_wrap=True) + table.add_column() + table.add_row( + "Magnet", + "pneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosispneumonoultramicroscopicsilicovolcanoconiosis", ) - # table.columns[0].width = 50 - # table.columns[1].ratio = 1 - - table.add_row("Hello, [b]World[/b]! " * 3, "cake" * 10) - from .markdown import Markdown - - table.add_row(Markdown("# This is *Markdown*!"), "More text", "Hello WOrld") - table.columns[0].justify = "center" - table.columns[1].justify = "right" - c.print(table) + diff --git a/rich/text.py b/rich/text.py index 5c8194ec..6ac2d925 100644 --- a/rich/text.py +++ b/rich/text.py @@ -644,16 +644,3 @@ class Text: append(line) return lines - -if __name__ == "__main__": # pragma: no cover - from .console import Console - - console = Console() - text = Text( - """Hello, self! hello World - -hello worhello\n""" - ) - text.highlight_regex("self", "reverse") - console.print(text) -