Push down types
This commit is contained in:
parent
a050ec252b
commit
47479896d5
3 changed files with 82 additions and 161 deletions
|
|
@ -1,154 +1,15 @@
|
||||||
"""Intelligent 3-way text merging with automated conflict resolution."""
|
"""Intelligent 3-way text merging with automated conflict resolution."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from reconcile_text._native import diff, reconcile, reconcile_with_history, undiff
|
||||||
|
from reconcile_text._types import (
|
||||||
from typing import Literal, TypedDict, Union
|
BuiltinTokenizer,
|
||||||
|
CursorPosition,
|
||||||
from reconcile_text._native import diff as _diff
|
History,
|
||||||
from reconcile_text._native import reconcile as _reconcile
|
SpanWithHistory,
|
||||||
from reconcile_text._native import reconcile_with_history as _reconcile_with_history
|
TextInput,
|
||||||
from reconcile_text._native import undiff as _undiff
|
TextWithCursors,
|
||||||
|
TextWithCursorsAndHistory,
|
||||||
BuiltinTokenizer = Literal["Character", "Line", "Markdown", "Word"]
|
)
|
||||||
"""Tokenization strategy for text merging."""
|
|
||||||
|
|
||||||
History = Literal[
|
|
||||||
"Unchanged", "AddedFromLeft", "AddedFromRight", "RemovedFromLeft", "RemovedFromRight"
|
|
||||||
]
|
|
||||||
"""Provenance label for each span in a merge result."""
|
|
||||||
|
|
||||||
|
|
||||||
class CursorPosition(TypedDict):
|
|
||||||
"""A cursor position within a text document."""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
"""Unique identifier for the cursor."""
|
|
||||||
position: int
|
|
||||||
"""Character position in the text (0-based)."""
|
|
||||||
|
|
||||||
|
|
||||||
class TextWithCursors(TypedDict):
|
|
||||||
"""A text document with associated cursor positions."""
|
|
||||||
|
|
||||||
text: str
|
|
||||||
"""The document content."""
|
|
||||||
cursors: list[CursorPosition]
|
|
||||||
"""Cursor positions within the text."""
|
|
||||||
|
|
||||||
|
|
||||||
class SpanWithHistory(TypedDict):
|
|
||||||
"""A text span annotated with its origin in a merge result."""
|
|
||||||
|
|
||||||
text: str
|
|
||||||
"""The text content of this span."""
|
|
||||||
history: History
|
|
||||||
"""Which source this span came from."""
|
|
||||||
|
|
||||||
|
|
||||||
class TextWithCursorsAndHistory(TypedDict):
|
|
||||||
"""A merged text document with cursor positions and change provenance."""
|
|
||||||
|
|
||||||
text: str
|
|
||||||
"""The merged document content."""
|
|
||||||
cursors: list[CursorPosition]
|
|
||||||
"""Repositioned cursor positions."""
|
|
||||||
history: list[SpanWithHistory]
|
|
||||||
"""Provenance information for each text span."""
|
|
||||||
|
|
||||||
|
|
||||||
TextInput = Union[str, TextWithCursors]
|
|
||||||
"""Input type for text arguments: either a plain string or a dict with text and cursors."""
|
|
||||||
|
|
||||||
|
|
||||||
def reconcile(
|
|
||||||
parent: str,
|
|
||||||
left: TextInput,
|
|
||||||
right: TextInput,
|
|
||||||
tokenizer: BuiltinTokenizer = "Word",
|
|
||||||
) -> TextWithCursors:
|
|
||||||
"""Merge three versions of text using conflict-free resolution.
|
|
||||||
|
|
||||||
Takes a parent text and two concurrent edits (left and right), returning
|
|
||||||
the merged result with automatically repositioned cursors.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
parent: The original text that both sides diverged from.
|
|
||||||
left: The left edit (string or dict with "text" and "cursors").
|
|
||||||
right: The right edit (string or dict with "text" and "cursors").
|
|
||||||
tokenizer: Tokenization strategy. Defaults to "Word".
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dict with "text" (merged string) and "cursors" (repositioned cursor list).
|
|
||||||
"""
|
|
||||||
return _reconcile(parent, left, right, tokenizer) # type: ignore[return-value]
|
|
||||||
|
|
||||||
|
|
||||||
def reconcile_with_history(
|
|
||||||
parent: str,
|
|
||||||
left: TextInput,
|
|
||||||
right: TextInput,
|
|
||||||
tokenizer: BuiltinTokenizer = "Word",
|
|
||||||
) -> TextWithCursorsAndHistory:
|
|
||||||
"""Merge three versions of text and return provenance history.
|
|
||||||
|
|
||||||
Like `reconcile`, but also returns which source each text span came from.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
parent: The original text that both sides diverged from.
|
|
||||||
left: The left edit (string or dict with "text" and "cursors").
|
|
||||||
right: The right edit (string or dict with "text" and "cursors").
|
|
||||||
tokenizer: Tokenization strategy. Defaults to "Word".
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dict with "text", "cursors", and "history".
|
|
||||||
"""
|
|
||||||
return _reconcile_with_history(parent, left, right, tokenizer) # type: ignore[return-value]
|
|
||||||
|
|
||||||
|
|
||||||
def diff(
|
|
||||||
parent: str,
|
|
||||||
changed: TextInput,
|
|
||||||
tokenizer: BuiltinTokenizer = "Word",
|
|
||||||
) -> list[int | str]:
|
|
||||||
"""Generate a compact diff between two texts.
|
|
||||||
|
|
||||||
Returns retain counts (positive ints), delete counts (negative ints),
|
|
||||||
and inserted strings.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
parent: The original text.
|
|
||||||
changed: The modified text (string or dict with "text" and "cursors").
|
|
||||||
tokenizer: Tokenization strategy. Defaults to "Word".
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A list of ints and strings representing the diff.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the diff computation overflows.
|
|
||||||
"""
|
|
||||||
return _diff(parent, changed, tokenizer) # type: ignore[return-value]
|
|
||||||
|
|
||||||
|
|
||||||
def undiff(
|
|
||||||
parent: str,
|
|
||||||
diff: list[int | str],
|
|
||||||
tokenizer: BuiltinTokenizer = "Word",
|
|
||||||
) -> str:
|
|
||||||
"""Apply a compact diff to reconstruct the changed text.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
parent: The original text.
|
|
||||||
diff: A list of ints and strings (as produced by `diff`).
|
|
||||||
tokenizer: Tokenization strategy. Defaults to "Word".
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The reconstructed text.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the diff format is invalid.
|
|
||||||
"""
|
|
||||||
return _undiff(parent, diff, tokenizer)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"BuiltinTokenizer",
|
"BuiltinTokenizer",
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,29 @@
|
||||||
from typing import Any
|
from reconcile_text._types import (
|
||||||
|
BuiltinTokenizer,
|
||||||
|
TextInput,
|
||||||
|
TextWithCursors,
|
||||||
|
TextWithCursorsAndHistory,
|
||||||
|
)
|
||||||
|
|
||||||
def reconcile(
|
def reconcile(
|
||||||
parent: str,
|
parent: str,
|
||||||
left: Any,
|
left: TextInput,
|
||||||
right: Any,
|
right: TextInput,
|
||||||
tokenizer: str = "Word",
|
tokenizer: BuiltinTokenizer = "Word",
|
||||||
) -> dict[str, Any]: ...
|
) -> TextWithCursors: ...
|
||||||
def reconcile_with_history(
|
def reconcile_with_history(
|
||||||
parent: str,
|
parent: str,
|
||||||
left: Any,
|
left: TextInput,
|
||||||
right: Any,
|
right: TextInput,
|
||||||
tokenizer: str = "Word",
|
tokenizer: BuiltinTokenizer = "Word",
|
||||||
) -> dict[str, Any]: ...
|
) -> TextWithCursorsAndHistory: ...
|
||||||
def diff(
|
def diff(
|
||||||
parent: str,
|
parent: str,
|
||||||
changed: Any,
|
changed: TextInput,
|
||||||
tokenizer: str = "Word",
|
tokenizer: BuiltinTokenizer = "Word",
|
||||||
) -> list[int | str]: ...
|
) -> list[int | str]: ...
|
||||||
def undiff(
|
def undiff(
|
||||||
parent: str,
|
parent: str,
|
||||||
diff: list[int | str],
|
diff: list[int | str],
|
||||||
tokenizer: str = "Word",
|
tokenizer: BuiltinTokenizer = "Word",
|
||||||
) -> str: ...
|
) -> str: ...
|
||||||
|
|
|
||||||
55
reconcile-python/python/reconcile_text/_types.py
Normal file
55
reconcile-python/python/reconcile_text/_types.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
"""Public input and result types."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Literal, TypedDict, Union
|
||||||
|
|
||||||
|
BuiltinTokenizer = Literal["Character", "Line", "Markdown", "Word"]
|
||||||
|
"""Tokenization strategy for text merging."""
|
||||||
|
|
||||||
|
History = Literal[
|
||||||
|
"Unchanged", "AddedFromLeft", "AddedFromRight", "RemovedFromLeft", "RemovedFromRight"
|
||||||
|
]
|
||||||
|
"""Provenance label for each span in a merge result."""
|
||||||
|
|
||||||
|
|
||||||
|
class CursorPosition(TypedDict):
|
||||||
|
"""A cursor position within a text document."""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
"""Unique identifier for the cursor."""
|
||||||
|
position: int
|
||||||
|
"""Character position in the text (0-based)."""
|
||||||
|
|
||||||
|
|
||||||
|
class TextWithCursors(TypedDict):
|
||||||
|
"""A text document with associated cursor positions."""
|
||||||
|
|
||||||
|
text: str
|
||||||
|
"""The document content."""
|
||||||
|
cursors: list[CursorPosition]
|
||||||
|
"""Cursor positions within the text."""
|
||||||
|
|
||||||
|
|
||||||
|
class SpanWithHistory(TypedDict):
|
||||||
|
"""A text span annotated with its origin in a merge result."""
|
||||||
|
|
||||||
|
text: str
|
||||||
|
"""The text content of this span."""
|
||||||
|
history: History
|
||||||
|
"""Which source this span came from."""
|
||||||
|
|
||||||
|
|
||||||
|
class TextWithCursorsAndHistory(TypedDict):
|
||||||
|
"""A merged text document with cursor positions and change provenance."""
|
||||||
|
|
||||||
|
text: str
|
||||||
|
"""The merged document content."""
|
||||||
|
cursors: list[CursorPosition]
|
||||||
|
"""Repositioned cursor positions."""
|
||||||
|
history: list[SpanWithHistory]
|
||||||
|
"""Provenance information for each text span."""
|
||||||
|
|
||||||
|
|
||||||
|
TextInput = Union[str, TextWithCursors]
|
||||||
|
"""Input type for text arguments: either a plain string or a dict with text and cursors."""
|
||||||
Loading…
Reference in a new issue