
    fE                     t    d dl mZmZmZ d dlZd dlZ ej                  d      Z G d d      Z G d d      Z	y)    )divisionprint_functionunicode_literalsNTextChangesc                       e Zd ZdZddZd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd ZddZd Zd Zd Zd Zd Zd Zd Zd Zy)TextSpanzw
    Span of text

    Doctests:
    >>> span = TextSpan(3, 2, "0123456789")
    >>> span.get_span_text()
    '34'
    c                 J    || _         || _        || _        || _        d | _        y N)poslengthtexttext_poslast_modified)selfr   r   r   r   s        5/usr/lib/python3/dist-packages/Onboard/TextChanges.py__init__zTextSpan.__init__(   s&    	 !    c                 n    t        | j                  | j                  | j                  | j                        S r
   )r   r   r   r   r   r   s    r   copyzTextSpan.copy/   s#    $++tyy$--HHr   c                     | j                   S r
   )r   r   s    r   beginzTextSpan.begin2   s    xxr   c                 4    | j                   | j                  z   S r
   r   r   r   s    r   endzTextSpan.end5   s    xx$++%%r   c                     | j                   S r
   )r   r   s    r   
text_beginzTextSpan.text_begin8   s    }}r   c                      | j                   dk(  S Nr   )r   r   s    r   is_emptyzTextSpan.is_empty;   s    {{ar   c                 d    | j                   |cxk  xr | j                   | j                  z   k  S c S r
   r   r   r   s     r   containszTextSpan.contains>   s)    xx37DKK!77777r   c                 B    | j                  |      j                          S r
   )intersectionr    r   spans     r   
intersectszTextSpan.intersectsA   s    $$T*33555r   c                     t        | j                  |j                        }t        | j                  | j                  z   |j                  |j                  z         }||kD  r
t	               S t	        |||z
        S r
   )maxr   minr   r   )r   r'   p0p1s       r   r%   zTextSpan.intersectionD   s\    $((#r4;;&DKK)?@r
r'*2rBw''r   c                    t        | j                         |j                               }t        | j                         |j                               }||z
  }|dz  }| j                  d|| j
                  z
   |j                  ||j
                  z
  d z   | _        || _        || _        t        | j                  r| j                  nd|j                  r|j                  nd      | _        | S )aj  
        Join two spans, result in self.

        Doctests:
        - adjacent spans
        >>> a = TextSpan(2, 3, "0123456789")
        >>> b = TextSpan(5, 2, "0123456789")
        >>> a.union_inplace(b)                         # doctest: +ELLIPSIS
        TextSpan(2, 5, '23456', ...
        >>> a.get_text()
        '0123456789'

        - intersecting spans
        >>> a = TextSpan(2, 3, "0123456789")
        >>> b = TextSpan(4, 2, "0123456789")
        >>> a.union_inplace(b)                         # doctest: +ELLIPSIS
        TextSpan(2, 4, '2345', ...
        >>> a.get_text()
        '0123456789'
           Nr   )	r+   r   r*   r   r   r   r   r   r   )r   r'   r   r   r   middles         r   union_inplacezTextSpan.union_inplaceL   s    * DJJL$**,/DHHJ$((*-u1ii 7$--!78ii 6 789	 t7I7I!3!3q7;7I7I!3!3qRr   Nc                     ||| j                   S || j                  }|| j                         }| j                   || j                  z
  || j                  z
   S )z! Return the whole available text )r   r   r   r   )r   r   r   s      r   get_textzTextSpan.get_textm   sV    =S[99=HHE;((*Cyy.t}}1DEEr   c                 V    | j                  | j                  | j                               S )z* Return just the span's part of the text. )r3   r   r   r   s    r   get_span_textzTextSpan.get_span_texty   s    }}TXXtxxz22r   c                 V    | j                   d| j                         | j                  z
   S )z
        Return the beginning of the whole available text,
        ending with and including the span.

        Doctests:
        >>> span = TextSpan(3, 2, "0123456789")
        >>> span.get_text_until_span()
        '01234'
        Nr   r   r   r   s    r   get_text_until_spanzTextSpan.get_text_until_span}   s%     yy4$((*t}}455r   c                 N    | j                   | j                  | j                  z
  d S )z
        Return the end of the whole available text,
        starting from and including the span.

        Doctests:
        >>> span = TextSpan(3, 2, "0123456789")
        >>> span.get_text_from_span()
        '3456789'
        N)r   r   r   r   s    r   get_text_from_spanzTextSpan.get_text_from_span   s#     yyDMM1233r   c                 V    | j                   | j                         | j                  z
  d S )z
        Return the remaining available text after the span.

        Doctests:
        >>> span = TextSpan(3, 2, "0123456789")
        >>> span.get_text_after_span()
        '56789'
        Nr7   r   s    r   get_text_after_spanzTextSpan.get_text_after_span   s%     yydmm3455r   c                 X    | j                   | j                  z
  }| j                  |dz
  | S )ax  
        Character right before the span.

        Doctests:
        >>> span = TextSpan(0, 0, "0123456789", 0)
        >>> span.get_char_before_span()
        ''

        >>> span = TextSpan(9, 1, "0123456789", 0)
        >>> span.get_char_before_span()
        '8'

        >>> span = TextSpan(5, 2, "3456789", 3)
        >>> span.get_char_before_span()
        '4'
           )r   r   r   r"   s     r   get_char_before_spanzTextSpan.get_char_before_span   s+    " hh&yyq3''r   c                 `    | j                         | j                  z
  }| j                  |dz
  | S )a{  
        Character right before the span.

        Doctests:
        >>> span = TextSpan(0, 0, "0123456789", 0)
        >>> span.get_last_char_in_span()
        ''

        >>> span = TextSpan(9, 1, "0123456789", 0)
        >>> span.get_last_char_in_span()
        '9'

        >>> span = TextSpan(5, 2, "3456789", 3)
        >>> span.get_last_char_in_span()
        '6'
        r>   )r   r   r   r"   s     r   get_last_char_in_spanzTextSpan.get_last_char_in_span   s-    " hhj4==(yyq3''r   c                 &    |j                  dd      S )N
z\n)replace)r   r   s     r   _escapezTextSpan._escape   s    ||D%((r   c                     dj                  | j                  | j                  | j                  | j	                               | j                         | j                        S )NzTextSpan({}, {}, '{}', {}, {}))formatr   r   rE   r5   r   r   r   s    r   __repr__zTextSpan.__repr__   sH    /$++T%7%7%9:)**,	,r   )r   r    r   )NN)__name__
__module____qualname____doc__r   r   r   r   r   r    r#   r(   r%   r1   r3   r5   r8   r:   r<   r?   rA   rE   rH    r   r   r   r      si    "I& 86(B
F3
6
4	6((((),r   r   c                       e Zd Zdj                  dd      ZddZd Zd Zd Zd	 Z	d
 Z
ddZddZedd       Zd Zd Zd Zed        Zd Zy)r   a  
    Collection of text spans yet to be learned.

    Example:
    >>> c = TextChanges()
    >>> c.insert(0, 1) # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 1]]

    Doctests:
    # insert and extend span
    >>> c = TextChanges()
    >>> c.insert(0, 1) # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 1]]
    >>> c.insert(0, 1) # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 2]]

    # extend at beginning and end
    >>> c = TextChanges()
    >>> c.insert(0, 1); c.insert(1, 1); c.insert(0, 3) # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 5]]

    # insert separated by at least one character -> multiple spans
    >>> c = TextChanges()
    >>> c.insert(1, 1); c.insert(0, 1) # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 1], [2, 1]]

    # add and delete inside single span
    >>> c = TextChanges()
    >>> c.insert(0, 9); # IGNORE_RESULT
    >>> c.delete(2, 1); # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 8]]

    # join spans when deleting
    >>> c = TextChanges()
    >>> c.insert(0, 1); c.insert(2, 1) # IGNORE_RESULT
    >>> c.delete(2, 1);                # IGNORE_RESULT
    >>> c.delete(1, 1);                # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 1]]

    # remove spans fully contained in the deleted range
    >>> c = TextChanges()
    >>> c.insert(2, 1); c.insert(4, 1) # IGNORE_RESULT
    >>> c.delete(0, 5);                # IGNORE_RESULT
    >>> c.get_span_ranges()
    [[0, 0]]

    # partially delete span, with and without recording empty spans
    #             ins     del     res with          res without
    >>> tests = [ # deletion before span
    ...          [[2, 3], [0, 5], [[0, 0]],         [[0, 0]] ],
    ...          [[3, 3], [0, 5], [[0, 1]],         [[0, 1]] ],
    ...          [[4, 3], [0, 5], [[0, 2]],         [[0, 2]] ],
    ...          [[5, 3], [0, 5], [[0, 3]],         [[0, 3]] ],
    ...          [[6, 3], [0, 5], [[0, 0], [1, 3]], [[1, 3]] ],
    ...           # deletion after span
    ...          [[0, 3], [4, 5], [[0, 3], [4, 0]], [[0, 3]] ],
    ...          [[1, 3], [4, 5], [[1, 3]],         [[1, 3]] ],
    ...          [[2, 3], [4, 5], [[2, 2]],         [[2, 2]] ],
    ...          [[3, 3], [4, 5], [[3, 1]],         [[3, 1]] ],
    ...           # deletion completely inside of span
    ...          [[4, 3], [4, 5], [[4, 0]],         [[4, 0]] ],
    ...          [[0, 9], [2, 3], [[0, 6]],         [[0, 6]] ] ]
    >>> for test in tests:
    ...     c = TextChanges()
    ...     _ = c.insert(*test[0]); _ = c.delete(test[1][0], test[1][1], True)
    ...     if c.get_span_ranges() != test[2]:
    ...        "test1: " + repr(test) + " result: " + repr(c.get_span_ranges())
    ...     c = TextChanges()
    ...     _ = c.insert(*test[0]); _ = c.delete(test[1][0], test[1][1], False)
    ...     if c.get_span_ranges() != test[3]:
    ...        "test2: " + repr(test) + " result: " + repr(c.get_span_ranges())

    # insert excluded span, include_length=0 to always insert an empty span
    #             ins     del     result
    >>> tests = [[[5, 5], [2, 3], [[2, 0], [8, 5]] ],  # insert before span
    ...          [[0, 5], [6, 3], [[0, 5], [6, 0]] ],  # insert after span
    ...          [[0, 5], [2, 3], [[0, 2], [5, 3]] ],  # insert inside span
    ...          [[0, 5], [3, 4], [[0, 3], [7, 2]] ] ] # insert at span end
    >>> for test in tests:
    ...     c = TextChanges()
    ...     _= c.insert(*test[0]); _ = c.insert(test[1][0], test[1][1], 0)
    ...     if c.get_span_ranges() != test[2]:
    ...        "test: " + repr(test) + " result: " + repr(c.get_span_ranges())

    IGNORE_RESULTzdoctest: +ELLIPSIS
    [...Nc                 8    | j                          |r|| _        y y r
   )clear_spans)r   spanss     r   r   zTextChanges.__init__1  s    

DK r   c                 .    g | _         d| _        d| _        y r   )rS   insert_countdelete_countr   s    r   rR   zTextChanges.clear6  s     r   c                 2    t        | j                        dk(  S r   )lenrS   r   s    r   r    zTextChanges.is_empty=  s    4;;1$$r   c                     | j                   S r
   )rS   r   s    r   	get_spanszTextChanges.get_spans@  s    {{r   c                 :    | j                   j                  |       y r
   )rS   remover&   s     r   remove_spanzTextChanges.remove_spanC  s    4 r   c                 4    | j                   | j                  z   S r
   )rV   rW   r   s    r   get_change_countzTextChanges.get_change_countF  s      4#4#444r   c                    ||z   }g }| j                   D ]8  }|j                  |kD  s|xj                  |z  c_        |j                  |       : |dk(  rb| j                  |      }|r|xj                  |z  c_        n't        ||      }| j                   j                  |       |j                  |       nt        ||xs d      }| j                  |      }|r|j                  }||j                  z
  |z   |_        |j                  |       ||j                  z
  }	|	dkD  s|	dk(  rx|vt        ||z   |	      }
| j                   j                  |
       |j                  |
       n:|8t        ||      }| j                   j                  |       |j                  |       t        j                         }|D ]	  }||_         |r| xj                  dz  c_	        |S )a{  
        Record insertion up to <include_length> characters,
        counted from the start of the insertion. The remaining
        inserted characters are excluded from spans. This may split
        an existing span.

        A small but non-zero <include_length> allows to skip over
        possible whitespace at the start of the insertion and
        will often result in including the very first word(s) for learning.

        include_length =   -1: include length
        include_length =   +n: include n
        include_length = None: include nothing, don't record
                               zero length span either
        r   r>   )
rS   r   appendfind_span_atr   r   r+   timer   rV   )r   r   r   include_lengthr   spans_to_updater'   max_include
old_lengthlspan2ts               r   insertzTextChanges.insertI  s     Fl KK 	-Dxx#~F"&&t,	-
 R$$S)Dv%V,""4(""4( fn&9:K$$S)D![[
!DHHn{:&&t, ,q56n4$S6\15EKK&&u-#**51#+[1""4(&&t,IIK# 	#D!"D	# "r   c                 4   |}||z   }g }t        | j                        D ]  }|j                  |k  rJt        |j	                         |z
  |      }|dk\  s5|xj
                  |z  c_        |j                  |       \||j                         z
  }|dk\  r*|xj                  |z  c_        |xj
                  |z  c_        |xj                  |z  c_        |j
                  dk  r| j                  j                  |       |j                  |        |ro| j                  |      }|s't        |d      }| j                  j                  |       | j                  | j                  |      \  | _        }|j                  |       |r| xj                  dz  c_        |S )a<  
        Record deletion.

        record_empty_spans =  True: record extra zero length spans
                                    at deletion point
        record_empty_spans = False: no extra new spans, but keep existing ones
                                    that become zero length (terminal scrolling)
        r   r>   )listrS   r   r+   r   r   rc   r   r]   find_span_excludingr   consolidate_spansrW   )	r   r   r   record_empty_spansr   r   rg   r'   ks	            r   deletezTextChanges.delete  s`    f
 % 	1Dxx3
U*F36KK1$K#**40$**,&6HHMHKK1$KF" ;;?KK&&t,#**40#	1( ++C0D  Q'""4( $ 6 6t{{D IDK""4("r   c                     t        | d       } g }d}| D ]Q  }|r:|j                         |j                         k\  r|j                  |       ||u s<|}?|j	                  |       |}S ||fS )a  
        join touching or intersecting text spans

        Doctests:
        # Join touching spans
        >>> spans = [TextSpan(0, 1),
        ...          TextSpan(2, 4),
        ...          TextSpan(1, 1),
        ...          TextSpan(10, 3),
        ...          TextSpan(8, 2)]
        >>> spans, _span = TextChanges.consolidate_spans(spans)
        >>> TextChanges.to_span_ranges(spans)
        [[0, 6], [8, 5]]

        # Join overlapping spans
        >>> spans = [TextSpan(2, 5),
        ...          TextSpan(4, 10),
        ...          TextSpan(12, 8)]
        >>> spans, _span = TextChanges.consolidate_spans(spans)
        >>> TextChanges.to_span_ranges(spans)
        [[2, 18]]

        # Join contained spans
        >>> spans = [TextSpan(5, 1),
        ...          TextSpan(2, 10),
        ...          TextSpan(3, 4)]
        >>> spans, _span = TextChanges.consolidate_spans(spans)
        >>> TextChanges.to_span_ranges(spans)
        [[2, 10]]
        c                 B    | j                         | j                         fS r
   )r   r   )xs    r   <lambda>z/TextChanges.consolidate_spans.<locals>.<lambda>  s    QWWY,@ r   )keyN)sortedr   r   r1   rc   )rT   tracked_span	new_spansslastss        r   rq   zTextChanges.consolidate_spans  s}    @ u"@A	 	Ayy{aggi'##A&1$#(L  #	 ,&&r   c                     | j                   D ]4  }|j                  |cxk  r |j                  |j                  z   k  s0|c S  6 y)z
        Doctests:
        - find empty spans (text deleted):
        >>> c = TextChanges()
        >>> c.insert(0, 0)      # doctest: +ELLIPSIS
        [TextSpan(...
        >>> c.find_span_at(0)   # doctest: +ELLIPSIS
        TextSpan(0, 0,...
        NrS   r   r   r   r   r'   s      r   rd   zTextChanges.find_span_at  sD     KK 	Dxx38$((T[["88 9	 r   c                     | j                   D ]G  }|j                  |k(  s3|j                  |cxk  r |j                  |j                  z   k  s?|c S  E|c S  y)a  
        Doctests:
        - find empty spans (text deleted):
        >>> c = TextChanges()
        >>> c.insert(0, 0)             # doctest: +ELLIPSIS
        [TextSpan(...
        >>> c.find_span_excluding(0)   # doctest: +ELLIPSIS
        TextSpan(0, 0,...

        - don't match the end
        >>> c = TextChanges()
        >>> c.insert(0, 1)      # doctest: +ELLIPSIS
        [TextSpan(...
        >>> c.find_span_excluding(1)   # doctest: +ELLIPSIS

        Nr   r   s      r   rp   zTextChanges.find_span_excluding  sT    " KK 	Dxx3xx37DKK!77 8	 r   c                 8    | j                  | j                        S r
   )to_span_rangesrS   r   s    r   get_span_rangeszTextChanges.get_span_ranges  s    ""4;;//r   c                 j    t        | D cg c]  }|j                  |j                  g c}      S c c}w r
   )rz   r   r   )rT   r'   s     r   r   zTextChanges.to_span_ranges  s(    5A4$++.ABBAs   0c           	      j    dt        | j                  D cg c]  }t        |       c}      z   S c c}w )NzTextChanges )reprrS   strr&   s     r   rH   zTextChanges.__repr__  s(    DKK%HDc$i%H III%Hs   0
r
   )rb   )T)rJ   rK   rL   rD   rM   r   rR   r    r[   r^   r`   rm   rt   staticmethodrq   rd   rp   r   r   rH   rN   r   r   r   r      s    [v 	!?@w z 
%!5@D2h ,' ,'\.0 C CJr   )

__future__r   r   r   re   logging	getLogger_loggerr   r   rN   r   r   <module>r      sC   ( B A  
'

M
*r, r,jIJ IJr   