Rename and add attribute setting/getting

This commit is contained in:
Joscha 2019-04-22 23:13:34 +00:00
parent 96f29a49cf
commit 2eb26c5532

View file

@ -1,7 +1,6 @@
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
__all__ = ["Attributes", "Chunk", "MarkedUp"]
__all__ = ["Attributes", "Chunk", "AttributedText"]
Attributes = Dict[str, Any] Attributes = Dict[str, Any]
@ -58,7 +57,7 @@ class Chunk:
return self._text return self._text
@property @property
def attrs(self) -> Attributes: def attributes(self) -> Attributes:
return dict(self._attributes) return dict(self._attributes)
# Private methods # Private methods
@ -66,17 +65,20 @@ class Chunk:
def _join(self, chunk: "Chunk") -> Optional["Chunk"]: def _join(self, chunk: "Chunk") -> Optional["Chunk"]:
if self._attributes == chunk._attributes: if self._attributes == chunk._attributes:
return Chunk(self.text + chunk.text, self._attributes) return Chunk(self.text + chunk.text, self._attributes)
else:
return None return None
# Public methods # Public methods
def with_attribute(self, name: str, value: Any) -> "Chunk": def get(self, name: str, default: Any = None) -> Any:
return self.attributes.get(name, default)
def set(self, name: str, value: Any) -> "Chunk":
new_attributes = dict(self._attributes) new_attributes = dict(self._attributes)
new_attributes[name] = value new_attributes[name] = value
return Chunk(self.text, new_attributes) return Chunk(self.text, new_attributes)
def without_attribute(self, name: str) -> "Chunk": def remove(self, name: str) -> "Chunk":
new_attributes = dict(self._attributes) new_attributes = dict(self._attributes)
# This removes the value with that key, if it exists, and does nothing # This removes the value with that key, if it exists, and does nothing
@ -87,16 +89,16 @@ class Chunk:
return Chunk(self.text, new_attributes) return Chunk(self.text, new_attributes)
class MarkedUp: class AttributedText:
""" """
Objects of this class are immutable and behave str-like. Supported Objects of this class are immutable and behave str-like. Supported
operations are len, + and splicing. operations are len, + and splicing.
""" """
@classmethod @classmethod
def from_chunks(cls, chunks: List[Chunk]) -> "MarkedUp": def from_chunks(cls, chunks: Iterable[Chunk]) -> "AttributedText":
new = cls() new = cls()
new._chunks = Chunk.join_chunks(chunks) new._chunks = Chunk.join_chunks(list(chunks))
return new return new
# Common special methods # Common special methods
@ -107,17 +109,17 @@ class MarkedUp:
self._chunks.append(Chunk(text)) self._chunks.append(Chunk(text))
def __str__(self) -> str: def __str__(self) -> str:
return "".join(map(str, self._chunks)) return self.text
def __repr__(self) -> str: def __repr__(self) -> str:
return f"MarkedUp.from_chunks({self._chunks!r})" return f"AttributedText.from_chunks({self._chunks!r})"
# Uncommon special methods # Uncommon special methods
def __add__(self, other: "MarkedUp") -> "MarkedUp": def __add__(self, other: "AttributedText") -> "AttributedText":
return MarkedUp.from_chunks(self._pieces + other._pieces) return AttributedText.from_chunks(self._chunks + other._chunks)
def __getitem__(self, key: Union[int, slice]) -> "MarkedUp": def __getitem__(self, key: Union[int, slice]) -> "AttributedText":
chunks: List[Chunk] chunks: List[Chunk]
if isinstance(key, slice): if isinstance(key, slice):
@ -125,13 +127,17 @@ class MarkedUp:
else: else:
chunks = [self._at(key)] chunks = [self._at(key)]
return MarkedUp.from_chunks(chunks) return AttributedText.from_chunks(chunks)
def __len__(self) -> int: def __len__(self) -> int:
return sum(map(len, self._chunks)) return sum(map(len, self._chunks))
# Properties # Properties
@property
def text(self) -> str:
return "".join(chunk.text for chunk in self._chunks)
@property @property
def chunks(self) -> List[Chunk]: def chunks(self) -> List[Chunk]:
return list(self._chunks) return list(self._chunks)
@ -150,8 +156,9 @@ class MarkedUp:
return chunk[chunk_key] return chunk[chunk_key]
pos += len(chunk) pos += len(chunk)
else:
raise KeyError # We haven't found the chunk
raise KeyError
def _slice(self, key: slice) -> List[Chunk]: def _slice(self, key: slice) -> List[Chunk]:
start, stop, step = key.start, key.stop, key.step start, stop, step = key.start, key.stop, key.step
@ -194,5 +201,85 @@ class MarkedUp:
# Public methods # Public methods
def attrs(self, key: int) -> Attributes: def at(self, pos: int) -> Attributes:
return self._at(key).attrs return self._at(pos).attributes
def get(self,
pos: int,
name: str,
default: Any = None,
) -> Any:
return self._at(pos).get(name, default)
# "find all separate blocks with this property"
def find_all(self, name: str) -> List[Tuple[Any, int, int]]:
blocks = []
pos = 0
block = None
for chunk in self._chunks:
if name in chunk.attributes:
attribute = chunk.attributes[name]
start = pos
stop = pos + len(chunk)
if block is None:
block = (attribute, start, stop)
continue
block_attr, block_start, _ = block
if block_attr == attribute:
block = (attribute, block_start, stop)
else:
blocks.append(block)
block = (attribute, start, stop)
pos += len(chunk)
if block is not None:
blocks.append(block)
return blocks
def set(self,
name: str,
value: Any,
start: Optional[int] = None,
stop: Optional[int] = None,
) -> "AttributedText":
if start is None and stop is None:
chunks = (chunk.set(name, value) for chunk in self._chunks)
return AttributedText.from_chunks(chunks)
elif start is None:
return self[:stop].set(name, value) + self[stop:]
elif stop is None:
return self[:start] + self[start:].set(name, value)
elif start > stop:
# set value everywhere BUT the specified interval
return self.set(name, value, stop=stop).set(name, value, start=start)
else:
middle = self[start:stop].set(name, value)
return self[:start] + middle + self[stop:]
def set_at(self, name: str, value: Any, pos: int) -> "AttributedText":
return self.set(name, value, pos, pos)
def remove(self,
name: str,
start: Optional[int] = None,
stop: Optional[int] = None,
) -> "AttributedText":
if start is None and stop is None:
chunks = (chunk.remove(name) for chunk in self._chunks)
return AttributedText.from_chunks(chunks)
elif start is None:
return self[:stop].remove(name) + self[stop:]
elif stop is None:
return self[:start] + self[start:].remove(name)
elif start > stop:
# remove value everywhere BUT the specified interval
return self.remove(name, stop=stop).remove(name, start=start)
else:
middle = self[start:stop].remove(name)
return self[:start] + middle + self[stop:]