示例#1
0
 def merge(self, other: "ReplaceStep"):
     if not (isinstance(other, ReplaceStep)
             or other.structure != self.structure):
         return None
     if (self.from_ + self.slice.size == other.from_
             and not self.slice.open_end and not other.slice.open_start):
         if self.slice.size + other.slice.size == 0:
             slice = Slice.empty
         else:
             slice = Slice(
                 self.slice.content.append(other.slice.content),
                 self.slice.open_start,
                 other.slice.open_end,
             )
         return ReplaceStep(self.from_, self.to + (other.to - other.from_),
                            slice, self.structure)
     elif (other.to == self.from_ and not self.slice.open_start
           and not other.slice.open_end):
         if self.slice.size + other.slice.size == 0:
             slice = Slice.empty
         else:
             slice = Slice(
                 other.slice.content.append(self.slice.content),
                 other.slice.open_start,
                 self.slice.open_end,
             )
         return ReplaceStep(other.from_, self.to, slice, self.structure)
     return None
示例#2
0
def test_replace(doc, from_, to, content, open_start, open_end, result):
    if content:
        slice = Slice(content.content, open_start, open_end)
    else:
        slice = Slice.empty
    tr = Transform(doc).replace(from_, to, slice)
    assert tr.doc.eq(result)
示例#3
0
 def replace_range_with(self, from_, to, node):
     if (not node.is_inline and from_ == to
             and self.doc.resolve(from_).parent.content.size):
         point = insert_point(self.doc, from_, node.type)
         if point is not None:
             from_ = to = point
     return self.replace_range(from_, to, Slice(Fragment.from_(node), 0, 0))
示例#4
0
def _make_step(from_, to, val):
    if val == "+em":
        return AddMarkStep(from_, to, schema.marks["em"].create)
    elif val == "-em":
        return RemoveMarkStep(from_, to, schema.marks["em"].create)
    return ReplaceStep(
        from_,
        to,
        Slice.empty if val is None else Slice(
            Fragment.from_(schema.text(val), 0, 0)),
    )
示例#5
0
    def apply(self, doc):
        old_slice = doc.slice(self.from_, self.to)

        def iteratee(node, *args):
            return node.mark(self.mark.remove_from_set(node.marks))

        slice = Slice(
            map_fragment(old_slice.content, iteratee),
            old_slice.open_start,
            old_slice.open_end,
        )
        return StepResult.from_replace(doc, self.from_, self.to, slice)
示例#6
0
 def wrap(self, range_, wrappers):
     content = Fragment.empty
     i = len(wrappers) - 1
     while i >= 0:
         content = Fragment.from_(wrappers[i]["type"].create(
             wrappers[i].get("attrs"), content))
         i -= 1
     start = range_.start
     end = range_.end
     return self.step(
         ReplaceAroundStep(start, end, start, end, Slice(content, 0, 0),
                           len(wrappers), True))
示例#7
0
    def from_json(schema, json_data):
        if isinstance(json_data, str):
            import json

            json_data = json.loads(json_data)
        if not isinstance(json_data["from"], int) or not isinstance(
                json_data["to"], int):
            raise ValueError("Invlid input for ReplaceStep.from_json")
        return ReplaceStep(
            json_data["from"],
            json_data["to"],
            Slice.from_json(schema, json_data.get("slice")),
            bool(json_data.get("structure")),
        )
示例#8
0
    def apply(self, doc):
        old_slice = doc.slice(self.from_, self.to)
        from__ = doc.resolve(self.from_)
        parent = from__.node(from__.shared_depth(self.to))

        def iteratee(node, parent, *args):
            if not parent.type.allows_mark_type(self.mark.type):
                return node
            return node.mark(self.mark.add_to_set(node.marks))

        slice = Slice(
            map_fragment(old_slice.content, iteratee, parent),
            old_slice.open_start,
            old_slice.open_end,
        )
        return StepResult.from_replace(doc, self.from_, self.to, slice)
示例#9
0
    def lift(self, range_, target):
        from__ = range_.from_
        to_ = range_.to
        depth = range_.depth

        gap_start = from__.before(depth + 1)
        gap_end = to_.after(depth + 1)
        start = gap_start
        end = gap_end

        before = Fragment.empty
        open_start = 0
        d = depth
        splitting = False
        while d > target:
            if splitting or from__.index(d) > 0:
                splitting = True
                before = Fragment.from_(from__.node(d).copy(before))
                open_start += 1
            else:
                start -= 1
            d -= 1
        after = Fragment.empty
        open_end = 0
        d = depth
        splitting = False
        while d > target:
            if splitting or to_.after(d + 1) < to_.end(d):
                splitting = True
                after = Fragment.from_(to_.node(d).copy(after))
                open_end += 1
            else:
                end += 1
            d -= 1
        return self.step(
            ReplaceAroundStep(
                start,
                end,
                gap_start,
                gap_end,
                Slice(before.append(after), open_start, open_end),
                before.size - open_start,
                True,
            ))
示例#10
0
 def set_node_markup(self, pos, type, attrs, marks=None):
     node = self.doc.node_at(pos)
     if not node:
         raise ValueError("No node at given position")
     if not type:
         type = node.type
     new_node = type.create(attrs, None, marks or node.marks)
     if node.is_leaf:
         return self.replace_with(pos, pos + node.node_size, new_node)
     if not type.valid_content(node.content):
         raise ValueError(f"Invalid content for node type {type.name}")
     return self.step(
         ReplaceAroundStep(
             pos,
             pos + node.node_size,
             pos + 1,
             pos + node.node_size - 1,
             Slice(Fragment.from_(new_node), 0, 0),
             1,
             True,
         ))
示例#11
0
 def split(self, pos, depth=None, types_after=None):
     if depth is None:
         depth = 1
     pos_ = self.doc.resolve(pos)
     before = Fragment.empty
     after = Fragment.empty
     d = pos_.depth
     e = pos_.depth - depth
     i = depth - 1
     while d > e:
         before = Fragment.from_(pos_.node(d).copy(before))
         type_after = None
         if types_after and len(types_after) > i:
             type_after = types_after[i]
         after = Fragment.from_(
             type_after["type"].create(type_after.get("attrs"), after)
             if type_after else pos_.node(d).copy(after))
         d -= 1
         i -= 1
     return self.step(
         ReplaceStep(pos, pos, Slice(before.append(after), depth, depth),
                     True))
示例#12
0
 def iteratee(node: "Node", pos, *args):
     if (node.is_text_block
             and not node.has_markup(type, attrs) and can_change_type(
                 self.doc,
                 self.mapping.slice(map_from).map(pos), type)):
         self.clear_incompatible(
             self.mapping.slice(map_from).map(pos, 1), type)
         mapping = self.mapping.slice(map_from)
         start_m = mapping.map(pos, 1)
         end_m = mapping.map(pos + node.node_size, 1)
         self.step(
             ReplaceAroundStep(
                 start_m,
                 end_m,
                 start_m + 1,
                 end_m - 1,
                 Slice(
                     Fragment.from_(type.create(attrs, None,
                                                node.marks)), 0, 0),
                 1,
                 True,
             ))
         return False
示例#13
0
 def clear_incompatible(self, pos, parent_type, match=None):
     if match is None:
         match = parent_type.content_match
     node = self.doc.node_at(pos)
     del_steps = []
     cur = pos + 1
     for i in range(node.child_count):
         child = node.child(i)
         end = cur + child.node_size
         allowed = match.match_type(child.type, child.attrs)
         if not allowed:
             del_steps.append(ReplaceStep(cur, end, Slice.empty))
         else:
             match = allowed
             for j in range(len(child.marks)):
                 if not parent_type.allows_mark_type(child.marks[j].type):
                     self.step(RemoveMarkStep(cur, end, child.marks[j]))
         cur = end
     if not match.valid_end:
         fill = match.fill_before(Fragment.empty, True)
         self.replace(cur, cur, Slice(fill, 0, 0))
     for item in reversed(del_steps):
         self.step(item)
     return self
示例#14
0
    def replace_range(self, from_, to, slice):
        if not slice.size:
            return self.delete_range(from_, to)
        from__ = self.doc.resolve(from_)
        to_ = self.doc.resolve(to)
        if fits_trivially(from__, to_, slice):
            return self.step(ReplaceStep(from_, to, slice))
        target_depths = covered_depths(from__, self.doc.resolve(to))
        if target_depths and target_depths[-1] == 0:
            target_depths.pop()
        preferred_target = -(from__.depth + 1)
        target_depths.insert(0, preferred_target)
        d = from__.depth
        pos = from__.pos - 1
        while d > 0:
            spec = from__.node(d).type.spec
            if spec.get("defining") or spec.get("isolating"):
                break
            if d in target_depths:
                preferred_target = d
            elif from__.before(d) == pos:
                target_depths.insert(1, -d)
            d -= 1
            pos -= 1
        preferred_target_index = target_depths.index(preferred_target)
        left_nodes = []
        preferred_depth = slice.open_start
        content = slice.content
        i = 0
        while True:
            node = content.first_child
            left_nodes.append(node)
            if i == slice.open_start:
                break
            content = node.content
            i += 1
        if (preferred_depth > 0
                and left_nodes[preferred_depth - 1].type.spec.get("defining")
                and from__.node(preferred_target_index).type.name !=
                left_nodes[preferred_depth - 1].type.name):
            preferred_depth -= 1
        elif (preferred_depth >= 2
              and left_nodes[preferred_depth - 1].is_text_block
              and left_nodes[preferred_depth - 2].type.spec.get("defining")
              and from__.node(preferred_target_index).type.name !=
              left_nodes[preferred_depth - 2].type.name):
            preferred_depth -= 2

        for j in range(slice.open_start, -1, -1):
            open_depth = (j + preferred_depth + 1) % (slice.open_start + 1)
            if len(left_nodes) > open_depth:
                insert = left_nodes[open_depth]
            else:
                continue
            for i in range(len(target_depths)):
                target_depth = target_depths[(i + preferred_target_index) %
                                             len(target_depths)]
                expand = True
                if target_depth < 0:
                    expand = False
                    target_depth = -target_depth
                parent = from__.node(target_depth - 1)
                index = from__.index(target_depth - 1)
                if parent.can_replace_with(index, index, insert.type,
                                           insert.marks):
                    return self.replace(
                        from__.before(target_depth),
                        to_.after(target_depth) if expand else to,
                        Slice(
                            close_fragment(slice.content, 0, slice.open_start,
                                           open_depth, None),
                            open_depth,
                            slice.open_end,
                        ),
                    )
        return self.replace(from_, to, slice)
示例#15
0
 def replace_with(self, from_, to, content):
     return self.replace(from_, to, Slice(Fragment.from_(content), 0, 0))
示例#16
0
 def place_content(self, fragment, open_start, open_end, pass_, parent=None):
     i = 0
     while i < fragment.child_count:
         child = fragment.child(i)
         placed = False
         last = i == (fragment.child_count - 1)
         d = len(self.open) - 1
         while d >= 0:
             open = self.open[d]
             wrap = None
             if pass_ > 1:
                 wrap = open["match"].find_wrapping(child.type)
                 if wrap and not (parent and len(wrap) and wrap[-1] == parent.type):
                     while len(self.open) - 1 > d:
                         self.close_node()
                     w = 0
                     while w < len(wrap):
                         open["match"] = open["match"].match_type(wrap[w])
                         d += 1
                         open = {
                             "parent": wrap[w].create(),
                             "match": wrap[w].content_match,
                             "content": Fragment.empty,
                             "wrapper": True,
                             "open_end": 0,
                             "depth": d + w,
                         }
                         self.open.append(open)
                         w += 1
             match = open["match"].match_type(child.type)
             if not match:
                 fill = open["match"].fill_before(Fragment.from_(child))
                 if fill:
                     for j in range(fill.child_count):
                         ch = fill.child(j)
                         self.add_node(open, ch, 0)
                         match = open["match"].match_fragment(ch)
                 elif parent and open["match"].match_type(parent.type):
                     break
                 else:
                     d -= 1
                     continue
             while len(self.open) - 1 > d:
                 self.close_node()
             child = child.mark(open["parent"].type.allowed_marks(child.marks))
             if open_start:
                 child = close_node_start(child, open_start, open_end if last else 0)
                 open_start = 0
             self.add_node(open, child, open_end if last else 0)
             open["match"] = match
             if last:
                 open_end = 0
             placed = True
             break
         if not placed:
             break
         i += 1
     if len(self.open) > 1 and (
         i > 0
         and i == fragment.child_count
         or parent
         and self.open[-1]["parent"].type == parent.type
     ):
         self.close_node()
     return Slice(fragment.cut_by_index(i), open_start, open_end)
示例#17
0
def fit_left(from__, placed):
    inner_res = fit_left_innter(from__, 0, placed, False)
    content = inner_res["content"]
    open_end = inner_res["open_end"]
    return Slice(content, from__.depth, open_end or 0)
示例#18
0
     doc(p("<a>hi<b>")),
     doc(blockquote(p("hix"))),
 ),
 (
     doc(p("x<a>hi"), blockquote(p("yy"), "<b>"), p("c")),
     doc(p("<a>hi<b>")),
     doc(p("xhi"), blockquote(p()), p("c")),
 ),
 (doc(p("<a>x")), doc(blockquote(p("hi"), "<a>"),
                      p("b<b>")), doc(p(), p("bx"))),
 (
     doc(p("<a>x")),
     doc(p("b<a>"), blockquote("<b>", p("hi"))),
     doc(p(), blockquote(p()), p("x")),
 ),
 (p("<a>x"), Slice(Fragment.from_([blockquote(), hr()]), 0, 0), p("x")),
 (
     doc(p("foo"), "<a>", p("bar<b>")),
     ol(li(p("<a>a")), li(p("b<b>"))),
     doc(p("foo"), p("a"), ol(li(p("b")))),
 ),
 (
     doc(ul(li(p("ab<a>cd")), li(p("ef<b>gh")))),
     doc(ul(li(p("ABCD")), li(p("EFGH")))).slice(5, 13, True),
     doc(ul(li(p("abCD")), li(p("EFgh")))),
 ),
 (
     doc(ul(li(p("foo")), "<a>", li(p("bar")))),
     ul(li(p("a<a>bc")), li(p("de<b>f"))),
     doc(ul(li(p("foo")), li(p("bc")), li(p("de")), li(p("bar")))),
 ),
示例#19
0
def normalize_slice(content, open_start, open_end):
    while open_start > 0 and open_end > 0 and content.child_count == 1:
        content = content.first_child.content
        open_start -= 1
        open_end -= 1
    return Slice(content, open_start, open_end)