Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

94 Zeilen
3.3 KiB

  1. from __future__ import annotations
  2. import re
  3. from typing import Iterable
  4. from ._loop import loop_last
  5. from .cells import cell_len, chop_cells
  6. re_word = re.compile(r"\s*\S+\s*")
  7. def words(text: str) -> Iterable[tuple[int, int, str]]:
  8. """Yields each word from the text as a tuple
  9. containing (start_index, end_index, word). A "word" in this context may
  10. include the actual word and any whitespace to the right.
  11. """
  12. position = 0
  13. word_match = re_word.match(text, position)
  14. while word_match is not None:
  15. start, end = word_match.span()
  16. word = word_match.group(0)
  17. yield start, end, word
  18. word_match = re_word.match(text, end)
  19. def divide_line(text: str, width: int, fold: bool = True) -> list[int]:
  20. """Given a string of text, and a width (measured in cells), return a list
  21. of cell offsets which the string should be split at in order for it to fit
  22. within the given width.
  23. Args:
  24. text: The text to examine.
  25. width: The available cell width.
  26. fold: If True, words longer than `width` will be folded onto a new line.
  27. Returns:
  28. A list of indices to break the line at.
  29. """
  30. break_positions: list[int] = [] # offsets to insert the breaks at
  31. append = break_positions.append
  32. cell_offset = 0
  33. _cell_len = cell_len
  34. for start, _end, word in words(text):
  35. word_length = _cell_len(word.rstrip())
  36. remaining_space = width - cell_offset
  37. word_fits_remaining_space = remaining_space >= word_length
  38. if word_fits_remaining_space:
  39. # Simplest case - the word fits within the remaining width for this line.
  40. cell_offset += _cell_len(word)
  41. else:
  42. # Not enough space remaining for this word on the current line.
  43. if word_length > width:
  44. # The word doesn't fit on any line, so we can't simply
  45. # place it on the next line...
  46. if fold:
  47. # Fold the word across multiple lines.
  48. folded_word = chop_cells(word, width=width)
  49. for last, line in loop_last(folded_word):
  50. if start:
  51. append(start)
  52. if last:
  53. cell_offset = _cell_len(line)
  54. else:
  55. start += len(line)
  56. else:
  57. # Folding isn't allowed, so crop the word.
  58. if start:
  59. append(start)
  60. cell_offset = _cell_len(word)
  61. elif cell_offset and start:
  62. # The word doesn't fit within the remaining space on the current
  63. # line, but it *can* fit on to the next (empty) line.
  64. append(start)
  65. cell_offset = _cell_len(word)
  66. return break_positions
  67. if __name__ == "__main__": # pragma: no cover
  68. from .console import Console
  69. console = Console(width=10)
  70. console.print("12345 abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ 12345")
  71. print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10))
  72. console = Console(width=20)
  73. console.rule()
  74. console.print("TextualはPythonの高速アプリケーション開発フレームワークです")
  75. console.rule()
  76. console.print("アプリケーションは1670万色を使用でき")