1"""
2The `Document` that implements all the text operations/querying.
3"""
4from __future__ import unicode_literals
5
6import bisect
7import re
8import six
9import string
10import weakref
11from six.moves import range, map
12
13from .selection import SelectionType, SelectionState, PasteMode
14from .clipboard import ClipboardData
15
16__all__ = ('Document',)
17
18
19# Regex for finding "words" in documents. (We consider a group of alnum
20# characters a word, but also a group of special characters a word, as long as
21# it doesn't contain a space.)
22# (This is a 'word' in Vi.)
23_FIND_WORD_RE = re.compile(r'([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)')
24_FIND_CURRENT_WORD_RE = re.compile(r'^([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)')
25_FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(r'^(([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)\s*)')
26
27# Regex for finding "WORDS" in documents.
28# (This is a 'WORD in Vi.)
29_FIND_BIG_WORD_RE = re.compile(r'([^\s]+)')
30_FIND_CURRENT_BIG_WORD_RE = re.compile(r'^([^\s]+)')
31_FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(r'^([^\s]+\s*)')
32
33# Share the Document._cache between all Document instances.
34# (Document instances are considered immutable. That means that if another
35# `Document` is constructed with the same text, it should have the same
36# `_DocumentCache`.)
37_text_to_document_cache = weakref.WeakValueDictionary()  # Maps document.text to DocumentCache instance.
38
39
40class _ImmutableLineList(list):
41    """
42    Some protection for our 'lines' list, which is assumed to be immutable in the cache.
43    (Useful for detecting obvious bugs.)
44    """
45    def _error(self, *a, **kw):
46        raise NotImplementedError('Attempt to modifiy an immutable list.')
47
48    __setitem__ = _error
49    append = _error
50    clear = _error
51    extend = _error
52    insert = _error
53    pop = _error
54    remove = _error
55    reverse = _error
56    sort = _error
57
58
59class _DocumentCache(object):
60    def __init__(self):
61        #: List of lines for the Document text.
62        self.lines = None
63
64        #: List of index positions, pointing to the start of all the lines.
65        self.line_indexes = None
66
67
68class Document(object):
69    """
70    This is a immutable class around the text and cursor position, and contains
71    methods for querying this data, e.g. to give the text before the cursor.
72
73    This class is usually instantiated by a :class:`~prompt_toolkit.buffer.Buffer`
74    object, and accessed as the `document` property of that class.
75
76    :param text: string
77    :param cursor_position: int
78    :param selection: :class:`.SelectionState`
79    """
80    __slots__ = ('_text', '_cursor_position', '_selection', '_cache')
81
82    def __init__(self, text='', cursor_position=None, selection=None):
83        assert isinstance(text, six.text_type), 'Got %r' % text
84        assert selection is None or isinstance(selection, SelectionState)
85
86        # Check cursor position. It can also be right after the end. (Where we
87        # insert text.)
88        assert cursor_position is None or cursor_position <= len(text), AssertionError(
89                'cursor_position=%r, len_text=%r' % (cursor_position, len(text)))
90
91        # By default, if no cursor position was given, make sure to put the
92        # cursor position is at the end of the document. This is what makes
93        # sense in most places.
94        if cursor_position is None:
95            cursor_position = len(text)
96
97        # Keep these attributes private. A `Document` really has to be
98        # considered to be immutable, because otherwise the caching will break
99        # things. Because of that, we wrap these into read-only properties.
100        self._text = text
101        self._cursor_position = cursor_position
102        self._selection = selection
103
104        # Cache for lines/indexes. (Shared with other Document instances that
105        # contain the same text.
106        try:
107            self._cache = _text_to_document_cache[self.text]
108        except KeyError:
109            self._cache = _DocumentCache()
110            _text_to_document_cache[self.text] = self._cache
111
112        # XX: For some reason, above, we can't use 'WeakValueDictionary.setdefault'.
113        #     This fails in Pypy3. `self._cache` becomes None, because that's what
114        #     'setdefault' returns.
115        # self._cache = _text_to_document_cache.setdefault(self.text, _DocumentCache())
116        # assert self._cache
117
118    def __repr__(self):
119        return '%s(%r, %r)' % (self.__class__.__name__, self.text, self.cursor_position)
120
121    @property
122    def text(self):
123        " The document text. "
124        return self._text
125
126    @property
127    def cursor_position(self):
128        " The document cursor position. "
129        return self._cursor_position
130
131    @property
132    def selection(self):
133        " :class:`.SelectionState` object. "
134        return self._selection
135
136    @property
137    def current_char(self):
138        """ Return character under cursor or an empty string. """
139        return self._get_char_relative_to_cursor(0) or ''
140
141    @property
142    def char_before_cursor(self):
143        """ Return character before the cursor or an empty string. """
144        return self._get_char_relative_to_cursor(-1) or ''
145
146    @property
147    def text_before_cursor(self):
148        return self.text[:self.cursor_position:]
149
150    @property
151    def text_after_cursor(self):
152        return self.text[self.cursor_position:]
153
154    @property
155    def current_line_before_cursor(self):
156        """ Text from the start of the line until the cursor. """
157        _, _, text = self.text_before_cursor.rpartition('\n')
158        return text
159
160    @property
161    def current_line_after_cursor(self):
162        """ Text from the cursor until the end of the line. """
163        text, _, _ = self.text_after_cursor.partition('\n')
164        return text
165
166    @property
167    def lines(self):
168        """
169        Array of all the lines.
170        """
171        # Cache, because this one is reused very often.
172        if self._cache.lines is None:
173            self._cache.lines = _ImmutableLineList(self.text.split('\n'))
174
175        return self._cache.lines
176
177    @property
178    def _line_start_indexes(self):
179        """
180        Array pointing to the start indexes of all the lines.
181        """
182        # Cache, because this is often reused. (If it is used, it's often used
183        # many times. And this has to be fast for editing big documents!)
184        if self._cache.line_indexes is None:
185            # Create list of line lengths.
186            line_lengths = map(len, self.lines)
187
188            # Calculate cumulative sums.
189            indexes = [0]
190            append = indexes.append
191            pos = 0
192
193            for line_length in line_lengths:
194                pos += line_length + 1
195                append(pos)
196
197            # Remove the last item. (This is not a new line.)
198            if len(indexes) > 1:
199                indexes.pop()
200
201            self._cache.line_indexes = indexes
202
203        return self._cache.line_indexes
204
205    @property
206    def lines_from_current(self):
207        """
208        Array of the lines starting from the current line, until the last line.
209        """
210        return self.lines[self.cursor_position_row:]
211
212    @property
213    def line_count(self):
214        r""" Return the number of lines in this document. If the document ends
215        with a trailing \n, that counts as the beginning of a new line. """
216        return len(self.lines)
217
218    @property
219    def current_line(self):
220        """ Return the text on the line where the cursor is. (when the input
221        consists of just one line, it equals `text`. """
222        return self.current_line_before_cursor + self.current_line_after_cursor
223
224    @property
225    def leading_whitespace_in_current_line(self):
226        """ The leading whitespace in the left margin of the current line.  """
227        current_line = self.current_line
228        length = len(current_line) - len(current_line.lstrip())
229        return current_line[:length]
230
231    def _get_char_relative_to_cursor(self, offset=0):
232        """
233        Return character relative to cursor position, or empty string
234        """
235        try:
236            return self.text[self.cursor_position + offset]
237        except IndexError:
238            return ''
239
240    @property
241    def on_first_line(self):
242        """
243        True when we are at the first line.
244        """
245        return self.cursor_position_row == 0
246
247    @property
248    def on_last_line(self):
249        """
250        True when we are at the last line.
251        """
252        return self.cursor_position_row == self.line_count - 1
253
254    @property
255    def cursor_position_row(self):
256        """
257        Current row. (0-based.)
258        """
259        row, _ = self._find_line_start_index(self.cursor_position)
260        return row
261
262    @property
263    def cursor_position_col(self):
264        """
265        Current column. (0-based.)
266        """
267        # (Don't use self.text_before_cursor to calculate this. Creating
268        # substrings and doing rsplit is too expensive for getting the cursor
269        # position.)
270        _, line_start_index = self._find_line_start_index(self.cursor_position)
271        return self.cursor_position - line_start_index
272
273    def _find_line_start_index(self, index):
274        """
275        For the index of a character at a certain line, calculate the index of
276        the first character on that line.
277
278        Return (row, index) tuple.
279        """
280        indexes = self._line_start_indexes
281
282        pos = bisect.bisect_right(indexes, index) - 1
283        return pos, indexes[pos]
284
285    def translate_index_to_position(self, index):
286        """
287        Given an index for the text, return the corresponding (row, col) tuple.
288        (0-based. Returns (0, 0) for index=0.)
289        """
290        # Find start of this line.
291        row, row_index = self._find_line_start_index(index)
292        col = index - row_index
293
294        return row, col
295
296
297    def translate_row_col_to_index(self, row, col):
298        """
299        Given a (row, col) tuple, return the corresponding index.
300        (Row and col params are 0-based.)
301
302        Negative row/col values are turned into zero.
303        """
304        try:
305            result = self._line_start_indexes[row]
306            line = self.lines[row]
307        except IndexError:
308            if row < 0:
309                result = self._line_start_indexes[0]
310                line = self.lines[0]
311            else:
312                result = self._line_start_indexes[-1]
313                line = self.lines[-1]
314
315        result += max(0, min(col, len(line)))
316
317        # Keep in range. (len(self.text) is included, because the cursor can be
318        # right after the end of the text as well.)
319        result = max(0, min(result, len(self.text)))
320        return result
321
322    @property
323    def is_cursor_at_the_end(self):
324        """ True when the cursor is at the end of the text. """
325        return self.cursor_position == len(self.text)
326
327    @property
328    def is_cursor_at_the_end_of_line(self):
329        """ True when the cursor is at the end of this line. """
330        return self.current_char in ('\n', '')
331
332    def has_match_at_current_position(self, sub):
333        """
334        `True` when this substring is found at the cursor position.
335        """
336        return self.text.find(sub, self.cursor_position) == self.cursor_position
337
338    def find(self, sub, in_current_line=False, include_current_position=False,
339             ignore_case=False, count=1):
340        """
341        Find `text` after the cursor, return position relative to the cursor
342        position. Return `None` if nothing was found.
343
344        :param count: Find the n-th occurance.
345        """
346        assert isinstance(ignore_case, bool)
347
348        if in_current_line:
349            text = self.current_line_after_cursor
350        else:
351            text = self.text_after_cursor
352
353        if not include_current_position:
354            if len(text) == 0:
355                return  # (Otherwise, we always get a match for the empty string.)
356            else:
357                text = text[1:]
358
359        flags = re.IGNORECASE if ignore_case else 0
360        iterator = re.finditer(re.escape(sub), text, flags)
361
362        try:
363            for i, match in enumerate(iterator):
364                if i + 1 == count:
365                    if include_current_position:
366                        return match.start(0)
367                    else:
368                        return match.start(0) + 1
369        except StopIteration:
370            pass
371
372    def find_all(self, sub, ignore_case=False):
373        """
374        Find all occurances of the substring. Return a list of absolute
375        positions in the document.
376        """
377        flags = re.IGNORECASE if ignore_case else 0
378        return [a.start() for a in re.finditer(re.escape(sub), self.text, flags)]
379
380    def find_backwards(self, sub, in_current_line=False, ignore_case=False, count=1):
381        """
382        Find `text` before the cursor, return position relative to the cursor
383        position. Return `None` if nothing was found.
384
385        :param count: Find the n-th occurance.
386        """
387        if in_current_line:
388            before_cursor = self.current_line_before_cursor[::-1]
389        else:
390            before_cursor = self.text_before_cursor[::-1]
391
392        flags = re.IGNORECASE if ignore_case else 0
393        iterator = re.finditer(re.escape(sub[::-1]), before_cursor, flags)
394
395        try:
396            for i, match in enumerate(iterator):
397                if i + 1 == count:
398                    return - match.start(0) - len(sub)
399        except StopIteration:
400            pass
401
402    def get_word_before_cursor(self, WORD=False):
403        """
404        Give the word before the cursor.
405        If we have whitespace before the cursor this returns an empty string.
406        """
407        if self.text_before_cursor[-1:].isspace():
408            return ''
409        else:
410            return self.text_before_cursor[self.find_start_of_previous_word(WORD=WORD):]
411
412    def find_start_of_previous_word(self, count=1, WORD=False):
413        """
414        Return an index relative to the cursor position pointing to the start
415        of the previous word. Return `None` if nothing was found.
416        """
417        # Reverse the text before the cursor, in order to do an efficient
418        # backwards search.
419        text_before_cursor = self.text_before_cursor[::-1]
420
421        regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
422        iterator = regex.finditer(text_before_cursor)
423
424        try:
425            for i, match in enumerate(iterator):
426                if i + 1 == count:
427                    return - match.end(1)
428        except StopIteration:
429            pass
430
431    def find_boundaries_of_current_word(self, WORD=False, include_leading_whitespace=False,
432                                        include_trailing_whitespace=False):
433        """
434        Return the relative boundaries (startpos, endpos) of the current word under the
435        cursor. (This is at the current line, because line boundaries obviously
436        don't belong to any word.)
437        If not on a word, this returns (0,0)
438        """
439        text_before_cursor = self.current_line_before_cursor[::-1]
440        text_after_cursor = self.current_line_after_cursor
441
442        def get_regex(include_whitespace):
443            return {
444                (False, False): _FIND_CURRENT_WORD_RE,
445                (False, True): _FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
446                (True, False): _FIND_CURRENT_BIG_WORD_RE,
447                (True, True): _FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
448            }[(WORD, include_whitespace)]
449
450        match_before = get_regex(include_leading_whitespace).search(text_before_cursor)
451        match_after = get_regex(include_trailing_whitespace).search(text_after_cursor)
452
453        # When there is a match before and after, and we're not looking for
454        # WORDs, make sure that both the part before and after the cursor are
455        # either in the [a-zA-Z_] alphabet or not. Otherwise, drop the part
456        # before the cursor.
457        if not WORD and match_before and match_after:
458            c1 = self.text[self.cursor_position - 1]
459            c2 = self.text[self.cursor_position]
460            alphabet = string.ascii_letters + '0123456789_'
461
462            if (c1 in alphabet) != (c2 in alphabet):
463                match_before = None
464
465        return (
466            - match_before.end(1) if match_before else 0,
467            match_after.end(1) if match_after else 0
468        )
469
470    def get_word_under_cursor(self, WORD=False):
471        """
472        Return the word, currently below the cursor.
473        This returns an empty string when the cursor is on a whitespace region.
474        """
475        start, end = self.find_boundaries_of_current_word(WORD=WORD)
476        return self.text[self.cursor_position + start: self.cursor_position + end]
477
478    def find_next_word_beginning(self, count=1, WORD=False):
479        """
480        Return an index relative to the cursor position pointing to the start
481        of the next word. Return `None` if nothing was found.
482        """
483        if count < 0:
484            return self.find_previous_word_beginning(count=-count, WORD=WORD)
485
486        regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
487        iterator = regex.finditer(self.text_after_cursor)
488
489        try:
490            for i, match in enumerate(iterator):
491                # Take first match, unless it's the word on which we're right now.
492                if i == 0 and match.start(1) == 0:
493                    count += 1
494
495                if i + 1 == count:
496                    return match.start(1)
497        except StopIteration:
498            pass
499
500    def find_next_word_ending(self, include_current_position=False, count=1, WORD=False):
501        """
502        Return an index relative to the cursor position pointing to the end
503        of the next word. Return `None` if nothing was found.
504        """
505        if count < 0:
506            return self.find_previous_word_ending(count=-count, WORD=WORD)
507
508        if include_current_position:
509            text = self.text_after_cursor
510        else:
511            text = self.text_after_cursor[1:]
512
513        regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
514        iterable = regex.finditer(text)
515
516        try:
517            for i, match in enumerate(iterable):
518                if i + 1 == count:
519                    value = match.end(1)
520
521                    if include_current_position:
522                        return value
523                    else:
524                        return value + 1
525
526        except StopIteration:
527            pass
528
529    def find_previous_word_beginning(self, count=1, WORD=False):
530        """
531        Return an index relative to the cursor position pointing to the start
532        of the previous word. Return `None` if nothing was found.
533        """
534        if count < 0:
535            return self.find_next_word_beginning(count=-count, WORD=WORD)
536
537        regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
538        iterator = regex.finditer(self.text_before_cursor[::-1])
539
540        try:
541            for i, match in enumerate(iterator):
542                if i + 1 == count:
543                    return - match.end(1)
544        except StopIteration:
545            pass
546
547    def find_previous_word_ending(self, count=1, WORD=False):
548        """
549        Return an index relative to the cursor position pointing to the end
550        of the previous word. Return `None` if nothing was found.
551        """
552        if count < 0:
553            return self.find_next_word_ending(count=-count, WORD=WORD)
554
555        text_before_cursor = self.text_after_cursor[:1] + self.text_before_cursor[::-1]
556
557        regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
558        iterator = regex.finditer(text_before_cursor)
559
560        try:
561            for i, match in enumerate(iterator):
562                # Take first match, unless it's the word on which we're right now.
563                if i == 0 and match.start(1) == 0:
564                    count += 1
565
566                if i + 1 == count:
567                    return -match.start(1) + 1
568        except StopIteration:
569            pass
570
571    def find_next_matching_line(self, match_func, count=1):
572        """
573        Look downwards for empty lines.
574        Return the line index, relative to the current line.
575        """
576        result = None
577
578        for index, line in enumerate(self.lines[self.cursor_position_row + 1:]):
579            if match_func(line):
580                result = 1 + index
581                count -= 1
582
583            if count == 0:
584                break
585
586        return result
587
588    def find_previous_matching_line(self, match_func, count=1):
589        """
590        Look upwards for empty lines.
591        Return the line index, relative to the current line.
592        """
593        result = None
594
595        for index, line in enumerate(self.lines[:self.cursor_position_row][::-1]):
596            if match_func(line):
597                result = -1 - index
598                count -= 1
599
600            if count == 0:
601                break
602
603        return result
604
605    def get_cursor_left_position(self, count=1):
606        """
607        Relative position for cursor left.
608        """
609        if count < 0:
610            return self.get_cursor_right_position(-count)
611
612        return - min(self.cursor_position_col, count)
613
614    def get_cursor_right_position(self, count=1):
615        """
616        Relative position for cursor_right.
617        """
618        if count < 0:
619            return self.get_cursor_left_position(-count)
620
621        return min(count, len(self.current_line_after_cursor))
622
623    def get_cursor_up_position(self, count=1, preferred_column=None):
624        """
625        Return the relative cursor position (character index) where we would be if the
626        user pressed the arrow-up button.
627
628        :param preferred_column: When given, go to this column instead of
629                                 staying at the current column.
630        """
631        assert count >= 1
632        column = self.cursor_position_col if preferred_column is None else preferred_column
633
634        return self.translate_row_col_to_index(
635            max(0, self.cursor_position_row - count), column) - self.cursor_position
636
637    def get_cursor_down_position(self, count=1, preferred_column=None):
638        """
639        Return the relative cursor position (character index) where we would be if the
640        user pressed the arrow-down button.
641
642        :param preferred_column: When given, go to this column instead of
643                                 staying at the current column.
644        """
645        assert count >= 1
646        column = self.cursor_position_col if preferred_column is None else preferred_column
647
648        return self.translate_row_col_to_index(
649            self.cursor_position_row + count, column) - self.cursor_position
650
651    def find_enclosing_bracket_right(self, left_ch, right_ch, end_pos=None):
652        """
653        Find the right bracket enclosing current position. Return the relative
654        position to the cursor position.
655
656        When `end_pos` is given, don't look past the position.
657        """
658        if self.current_char == right_ch:
659            return 0
660
661        if end_pos is None:
662            end_pos = len(self.text)
663        else:
664            end_pos = min(len(self.text), end_pos)
665
666        stack = 1
667
668        # Look forward.
669        for i in range(self.cursor_position + 1, end_pos):
670            c = self.text[i]
671
672            if c == left_ch:
673                stack += 1
674            elif c == right_ch:
675                stack -= 1
676
677            if stack == 0:
678                return i - self.cursor_position
679
680    def find_enclosing_bracket_left(self, left_ch, right_ch, start_pos=None):
681        """
682        Find the left bracket enclosing current position. Return the relative
683        position to the cursor position.
684
685        When `start_pos` is given, don't look past the position.
686        """
687        if self.current_char == left_ch:
688            return 0
689
690        if start_pos is None:
691            start_pos = 0
692        else:
693            start_pos = max(0, start_pos)
694
695        stack = 1
696
697        # Look backward.
698        for i in range(self.cursor_position - 1, start_pos - 1, -1):
699            c = self.text[i]
700
701            if c == right_ch:
702                stack += 1
703            elif c == left_ch:
704                stack -= 1
705
706            if stack == 0:
707                return i - self.cursor_position
708
709    def find_matching_bracket_position(self, start_pos=None, end_pos=None):
710        """
711        Return relative cursor position of matching [, (, { or < bracket.
712
713        When `start_pos` or `end_pos` are given. Don't look past the positions.
714        """
715
716        # Look for a match.
717        for A, B in '()', '[]', '{}', '<>':
718            if self.current_char == A:
719                return self.find_enclosing_bracket_right(A, B, end_pos=end_pos) or 0
720            elif self.current_char == B:
721                return self.find_enclosing_bracket_left(A, B, start_pos=start_pos) or 0
722
723        return 0
724
725    def get_start_of_document_position(self):
726        """ Relative position for the start of the document. """
727        return - self.cursor_position
728
729    def get_end_of_document_position(self):
730        """ Relative position for the end of the document. """
731        return len(self.text) - self.cursor_position
732
733    def get_start_of_line_position(self, after_whitespace=False):
734        """ Relative position for the start of this line. """
735        if after_whitespace:
736            current_line = self.current_line
737            return len(current_line) - len(current_line.lstrip()) - self.cursor_position_col
738        else:
739            return - len(self.current_line_before_cursor)
740
741    def get_end_of_line_position(self):
742        """ Relative position for the end of this line. """
743        return len(self.current_line_after_cursor)
744
745    def last_non_blank_of_current_line_position(self):
746        """
747        Relative position for the last non blank character of this line.
748        """
749        return len(self.current_line.rstrip()) - self.cursor_position_col - 1
750
751    def get_column_cursor_position(self, column):
752        """
753        Return the relative cursor position for this column at the current
754        line. (It will stay between the boundaries of the line in case of a
755        larger number.)
756        """
757        line_length = len(self.current_line)
758        current_column = self.cursor_position_col
759        column = max(0, min(line_length, column))
760
761        return column - current_column
762
763    def selection_range(self):  # XXX: shouldn't this return `None` if there is no selection???
764        """
765        Return (from, to) tuple of the selection.
766        start and end position are included.
767
768        This doesn't take the selection type into account. Use
769        `selection_ranges` instead.
770        """
771        if self.selection:
772            from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
773        else:
774            from_, to = self.cursor_position, self.cursor_position
775
776        return from_, to
777
778    def selection_ranges(self):
779        """
780        Return a list of (from, to) tuples for the selection or none if nothing
781        was selected.  start and end position are always included in the
782        selection.
783
784        This will yield several (from, to) tuples in case of a BLOCK selection.
785        """
786        if self.selection:
787            from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
788
789            if self.selection.type == SelectionType.BLOCK:
790                from_line, from_column = self.translate_index_to_position(from_)
791                to_line, to_column = self.translate_index_to_position(to)
792                from_column, to_column = sorted([from_column, to_column])
793                lines = self.lines
794
795                for l in range(from_line, to_line + 1):
796                    line_length = len(lines[l])
797                    if from_column < line_length:
798                        yield (self.translate_row_col_to_index(l, from_column),
799                               self.translate_row_col_to_index(l, min(line_length - 1, to_column)))
800            else:
801                # In case of a LINES selection, go to the start/end of the lines.
802                if self.selection.type == SelectionType.LINES:
803                    from_ = max(0, self.text.rfind('\n', 0, from_) + 1)
804
805                    if self.text.find('\n', to) >= 0:
806                        to = self.text.find('\n', to)
807                    else:
808                        to = len(self.text) - 1
809
810                yield from_, to
811
812    def selection_range_at_line(self, row):
813        """
814        If the selection spans a portion of the given line, return a (from, to) tuple.
815        Otherwise, return None.
816        """
817        if self.selection:
818            row_start = self.translate_row_col_to_index(row, 0)
819            row_end = self.translate_row_col_to_index(row, max(0, len(self.lines[row]) - 1))
820
821            from_, to = sorted([self.cursor_position, self.selection.original_cursor_position])
822
823            # Take the intersection of the current line and the selection.
824            intersection_start = max(row_start, from_)
825            intersection_end = min(row_end, to)
826
827            if intersection_start <= intersection_end:
828                if self.selection.type == SelectionType.LINES:
829                    intersection_start = row_start
830                    intersection_end = row_end
831                elif self.selection.type == SelectionType.BLOCK:
832                    _, col1 = self.translate_index_to_position(from_)
833                    _, col2 = self.translate_index_to_position(to)
834                    col1, col2 = sorted([col1, col2])
835                    intersection_start = self.translate_row_col_to_index(row, col1)
836                    intersection_end = self.translate_row_col_to_index(row, col2)
837
838                _, from_column = self.translate_index_to_position(intersection_start)
839                _, to_column = self.translate_index_to_position(intersection_end)
840
841                return from_column, to_column
842
843    def cut_selection(self):
844        """
845        Return a (:class:`.Document`, :class:`.ClipboardData`) tuple, where the
846        document represents the new document when the selection is cut, and the
847        clipboard data, represents whatever has to be put on the clipboard.
848        """
849        if self.selection:
850            cut_parts = []
851            remaining_parts = []
852            new_cursor_position = self.cursor_position
853
854            last_to = 0
855            for from_, to in self.selection_ranges():
856                if last_to == 0:
857                    new_cursor_position = from_
858
859                remaining_parts.append(self.text[last_to:from_])
860                cut_parts.append(self.text[from_:to + 1])
861                last_to = to + 1
862
863            remaining_parts.append(self.text[last_to:])
864
865            cut_text = '\n'.join(cut_parts)
866            remaining_text = ''.join(remaining_parts)
867
868            # In case of a LINES selection, don't include the trailing newline.
869            if self.selection.type == SelectionType.LINES and cut_text.endswith('\n'):
870                cut_text = cut_text[:-1]
871
872            return (Document(text=remaining_text, cursor_position=new_cursor_position),
873                    ClipboardData(cut_text, self.selection.type))
874        else:
875            return self, ClipboardData('')
876
877    def paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1):
878        """
879        Return a new :class:`.Document` instance which contains the result if
880        we would paste this data at the current cursor position.
881
882        :param paste_mode: Where to paste. (Before/after/emacs.)
883        :param count: When >1, Paste multiple times.
884        """
885        assert isinstance(data, ClipboardData)
886        assert paste_mode in (PasteMode.VI_BEFORE, PasteMode.VI_AFTER, PasteMode.EMACS)
887
888        before = (paste_mode == PasteMode.VI_BEFORE)
889        after = (paste_mode == PasteMode.VI_AFTER)
890
891        if data.type == SelectionType.CHARACTERS:
892            if after:
893                new_text = (self.text[:self.cursor_position + 1] + data.text * count +
894                            self.text[self.cursor_position + 1:])
895            else:
896                new_text = self.text_before_cursor + data.text * count + self.text_after_cursor
897
898            new_cursor_position = self.cursor_position + len(data.text) * count
899            if before:
900                new_cursor_position -= 1
901
902        elif data.type == SelectionType.LINES:
903            l = self.cursor_position_row
904            if before:
905                lines = self.lines[:l] + [data.text] * count + self.lines[l:]
906                new_text = '\n'.join(lines)
907                new_cursor_position = len(''.join(self.lines[:l])) + l
908            else:
909                lines = self.lines[:l + 1] + [data.text] * count + self.lines[l + 1:]
910                new_cursor_position = len(''.join(self.lines[:l + 1])) + l + 1
911                new_text = '\n'.join(lines)
912
913        elif data.type == SelectionType.BLOCK:
914            lines = self.lines[:]
915            start_line = self.cursor_position_row
916            start_column = self.cursor_position_col + (0 if before else 1)
917
918            for i, line in enumerate(data.text.split('\n')):
919                index = i + start_line
920                if index >= len(lines):
921                    lines.append('')
922
923                lines[index] = lines[index].ljust(start_column)
924                lines[index] = lines[index][:start_column] + line * count + lines[index][start_column:]
925
926            new_text = '\n'.join(lines)
927            new_cursor_position = self.cursor_position + (0 if before else 1)
928
929        return Document(text=new_text, cursor_position=new_cursor_position)
930
931    def empty_line_count_at_the_end(self):
932        """
933        Return number of empty lines at the end of the document.
934        """
935        count = 0
936        for line in self.lines[::-1]:
937            if not line or line.isspace():
938                count += 1
939            else:
940                break
941
942        return count
943
944    def start_of_paragraph(self, count=1, before=False):
945        """
946        Return the start of the current paragraph. (Relative cursor position.)
947        """
948        def match_func(text):
949            return not text or text.isspace()
950
951        line_index = self.find_previous_matching_line(match_func=match_func, count=count)
952
953        if line_index:
954            add = 0 if before else 1
955            return min(0, self.get_cursor_up_position(count=-line_index) + add)
956        else:
957            return -self.cursor_position
958
959    def end_of_paragraph(self, count=1, after=False):
960        """
961        Return the end of the current paragraph. (Relative cursor position.)
962        """
963        def match_func(text):
964            return not text or text.isspace()
965
966        line_index = self.find_next_matching_line(match_func=match_func, count=count)
967
968        if line_index:
969            add = 0 if after else 1
970            return max(0, self.get_cursor_down_position(count=line_index) - add)
971        else:
972            return len(self.text_after_cursor)
973
974    # Modifiers.
975
976    def insert_after(self, text):
977        """
978        Create a new document, with this text inserted after the buffer.
979        It keeps selection ranges and cursor position in sync.
980        """
981        return Document(
982                text=self.text + text,
983                cursor_position=self.cursor_position,
984                selection=self.selection)
985
986    def insert_before(self, text):
987        """
988        Create a new document, with this text inserted before the buffer.
989        It keeps selection ranges and cursor position in sync.
990        """
991        selection_state = self.selection
992
993        if selection_state:
994            selection_state = SelectionState(
995                original_cursor_position=selection_state.original_cursor_position + len(text),
996                type=selection_state.type)
997
998        return Document(
999                text=text + self.text,
1000                cursor_position=self.cursor_position + len(text),
1001                selection=selection_state)
1002