# -*- coding: utf-8 -*-
# *****************************************************************************
#       Copyright (C) 2006  Michael Graz. <mgraz@plan10.com>
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
# *****************************************************************************
from __future__ import absolute_import, print_function, unicode_literals

import sys
import unittest
from io import StringIO

import pyreadline3.logger as logger
from pyreadline3 import keysyms
from pyreadline3.lineeditor import lineobj
from pyreadline3.logger import log
from pyreadline3.modes.vi import *
from pyreadline3.test.common import *

sys.path.insert(0, '../..')

# ----------------------------------------------------------------------


class ViModeTest(ViMode):
    tested_commands = {}

    def __init__(self):
        ViMode.__init__(self, MockReadline())
        self.mock_console = MockConsole()
        self.init_editing_mode(None)
        self.vi_set_insert_mode(True)
        self.lst_completions = []
        self.completer = self.mock_completer
        self.completer_delims = ' '
        self.tabstop = 4

    def get_mock_console(self):
        return self.mock_console
    console = property(get_mock_console)

    def _set_line(self, text):
        self.l_buffer.set_line(text)

    def get_line(self):
        return self.l_buffer.get_line_text()
    line = property(get_line)

    def get_line_cursor(self):
        return self.l_buffer.point
    line_cursor = property(get_line_cursor)

    def input(self, keytext):
        if keytext[0] == '"' and keytext[-1] == '"':
            lst_key = ['"%s"' % c for c in keytext[1:-1]]
        else:
            lst_key = [keytext]
        for key in lst_key:
            keyinfo, event = keytext_to_keyinfo_and_event(key)
            dispatch_func = self.key_dispatch.get(keyinfo.tuple(), self.vi_key)
            self.tested_commands[dispatch_func.__name__] = dispatch_func
            dispatch_func(event)

    def vi_accept_line(self, e):
        if ViMode.vi_accept_line(self, e):
            # simulate return
            # self.add_history (self.line)
            self.l_buffer.reset_line()

    def mock_completer(self, text, state):
        return self.lst_completions[state]


class ViExternalEditorTest (ViExternalEditor):
    def __init__(self, line):
        self.sio_write = StringIO()
        self.sio_read = StringIO('qwerty after')
        ViExternalEditor.__init__(self, line)

    def get_tempfile(self):
        return 'temp.py'

    def get_editor(self):
        return 'vim.exe'

    def file_open(self, filename, mode):
        if mode == 'w':
            return self.sio_write
        else:
            return self.sio_read

    def file_remove(self, filename):
        self.remove = filename

    def run_command(self, command):
        self.command = command

# ----------------------------------------------------------------------


class Tests (unittest.TestCase):

    def test_keyinfo(self):
        keyinfo, event = keytext_to_keyinfo_and_event('"d"')
        self.assertEqual('d', event.char)
        keyinfo, event = keytext_to_keyinfo_and_event('"D"')
        self.assertEqual('D', event.char)
        keyinfo, event = keytext_to_keyinfo_and_event('"$"')
        self.assertEqual('$', event.char)
        keyinfo, event = keytext_to_keyinfo_and_event('Escape')
        self.assertEqual('\x1b', event.char)

    def test_simple(self):
        r = ViModeTest()
        r._set_line('abc')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"d"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual('abcd', r.line)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual('abcd', r.line)
        r.input('"i"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual('abcd', r.line)
        r.input('"x"')
        self.assertEqual('abcxd', r.line)
        r.input('"y"')
        self.assertEqual('abcxyd', r.line)

    def test_begin_and_end(self):
        r = ViModeTest()
        r._set_line('abc')
        self.assertEqual(3, r.line_cursor)
        r.input('Escape')
        self.assertEqual(2, r.line_cursor)
        r.input('"0"')
        self.assertEqual(0, r.line_cursor)
        r.input('"$"')
        self.assertEqual(2, r.line_cursor)
        r.input('"^"')
        self.assertEqual(0, r.line_cursor)

    def test_history_add(self):
        r = ViModeTest()
        r.input('"abc"')
        r.input('Return')
        r.input('"def"')
        r.input('Return')
        r.input('Escape')
        r.input('"k"')
        self.assertEqual('def', r.line)
        r.input('"k"')
        self.assertEqual('abc', r.line)

    def test_history_alpha(self):
        r = ViModeTest()
        r.add_history('abc')
        r.add_history('def')
        r._set_line('ghi')
        r.input('Escape')
        r.input('"k"')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual('def', r.line)
        r.input('"k"')
        self.assertEqual('abc', r.line)
        self.assertEqual(0, r.console.bell_count)
        r.input('"k"')
        self.assertEqual('abc', r.line)
        # TODO up history with no more -> error ?
        # self.assertEqual (1, r.console.bell_count)
        r.input('"j"')
        self.assertEqual('def', r.line)
        self.assertEqual(0, r.line_cursor)
        r.input('"j"')
        self.assertEqual('ghi', r.line)
        # TODO returning to original line loses cursor pos?
        # self.assertEqual (2, r.line_cursor)
        r.input('"j"')
        self.assertEqual('ghi', r.line)
        self.assertEqual(2, r.line_cursor)

    def test_history_arrow(self):
        r = ViModeTest()
        r.add_history('abc')
        r.add_history('def')
        r.input('"ghi"')
        self.assertEqual(3, r.line_cursor)
        r.input('Up')
        self.assertEqual(3, r.line_cursor)
        self.assertEqual('def', r.line)
        r.input('Up')
        self.assertEqual(3, r.line_cursor)
        self.assertEqual('abc', r.line)
        self.assertEqual(0, r.console.bell_count)
        r.input('Up')
        self.assertEqual('abc', r.line)
        # TODO up history with no more -> error ?
        # self.assertEqual (1, r.console.bell_count)
        r.input('Down')
        self.assertEqual('def', r.line)
        self.assertEqual(3, r.line_cursor)
        r.input('Down')
        self.assertEqual('ghi', r.line)
        self.assertEqual(3, r.line_cursor)
        r.input('Down')
        self.assertEqual('ghi', r.line)
        self.assertEqual(3, r.line_cursor)

    def test_history_arrow_after_undo(self):
        r = ViModeTest()
        r.input('"aaa"')
        r.input('Return')
        r.input('"bbb ccc"')
        r.input('Escape')
        r.input('"F D"')
        self.assertEqual(r.line, 'bbb')
        r.input('"u"')
        self.assertEqual(r.line, 'bbb ccc')
        r.input('Return')
        self.assertEqual(r.console.bell_count, 0)
        r.input('"ddd"')
        r.input('Down')
        self.assertEqual(r.line, 'ddd')
        # TODO down history with no more -> error ?
        # self.assertEqual (r.console.bell_count, 1)

    def test_vi_is_word(self):
        self.assertTrue(vi_is_word('a'))
        self.assertTrue(not vi_is_word(' u'))

    def test_pos_word_short(self):
        self.assertEqual(0, vi_pos_word_short(''))
        self.assertEqual(4, vi_pos_word_short('abc def u'))
        self.assertEqual(4, vi_pos_word_short('abc def u', 2))
        self.assertEqual(4, vi_pos_word_short('abc def u', 3))
        self.assertEqual(8, vi_pos_word_short('abc def u', 4))
        self.assertEqual(3, vi_pos_word_short('abc.def u'))
        self.assertEqual(5, vi_pos_word_short('abc..def u', 3))
        self.assertEqual(8, vi_pos_word_short('ab  cd  ef  gh', count=2))
        self.assertEqual(8, vi_pos_word_short('ab  cd  ef  gh', 1, 2))
        self.assertEqual(8, vi_pos_word_short('ab  cd  ef  gh', 3, 2))
        self.assertEqual(14, vi_pos_word_short('ab  cd  ef  gh', count=10))

    def test_pos_word_long(self):
        self.assertEqual(0, vi_pos_word_long(''))
        self.assertEqual(4, vi_pos_word_long('abc def u'))
        self.assertEqual(4, vi_pos_word_long('abc def u', 2))
        self.assertEqual(4, vi_pos_word_long('abc def u', 3))
        self.assertEqual(8, vi_pos_word_long('abc def u', 4))
        self.assertEqual(8, vi_pos_word_long('abc.def u'))
        self.assertEqual(9, vi_pos_word_long('abc..def u', 3))
        self.assertEqual(10, vi_pos_word_long('a.b  c.d  e.f  g.h', count=2))
        self.assertEqual(10, vi_pos_word_long('a.b  c.d  e.f  g.h', 2, 2))
        self.assertEqual(10, vi_pos_word_long('a.b  c.d  e.f  g.h', 4, 2))

    def test_pos_end_short(self):
        self.assertEqual(0, vi_pos_end_short(''))
        self.assertEqual(2, vi_pos_end_short('abc def u'))
        self.assertEqual(6, vi_pos_end_short('abc def u', 2))
        self.assertEqual(6, vi_pos_end_short('abc def u', 3))
        self.assertEqual(6, vi_pos_end_short('abc def u', 4))
        self.assertEqual(2, vi_pos_end_short('abc.def u'))
        self.assertEqual(7, vi_pos_end_short('abc  ...  def u', 3))
        self.assertEqual(7, vi_pos_end_short('abc  ...  def u', 5))
        self.assertEqual(12, vi_pos_end_short('abc  ...  def u', 8))
        self.assertEqual(5, vi_pos_end_short('ab  cd  ef  gh', count=2))
        self.assertEqual(9, vi_pos_end_short('ab  cd  ef  gh', 1, 2))
        self.assertEqual(9, vi_pos_end_short('ab  cd  ef  gh', 3, 2))

    def test_pos_end_long(self):
        self.assertEqual(0, vi_pos_end_long(''))
        self.assertEqual(2, vi_pos_end_long('abc def u'))
        self.assertEqual(6, vi_pos_end_long('abc def u', 2))
        self.assertEqual(6, vi_pos_end_long('abc def u', 3))
        self.assertEqual(6, vi_pos_end_long('abc def u', 4))
        self.assertEqual(6, vi_pos_end_long('abc.def u'))
        self.assertEqual(10, vi_pos_end_long('  abc...def u'))
        self.assertEqual(10, vi_pos_end_long('abc  ...def u', 5))
        self.assertEqual(7, vi_pos_end_long('a.b  c.d  e.f  g.h', count=2))
        self.assertEqual(12, vi_pos_end_long('a.b  c.d  e.f  g.h', 2, 2))
        self.assertEqual(12, vi_pos_end_long('a.b  c.d  e.f  g.h', 4, 2))

    def test_pos_back_short(self):
        self.assertEqual(0, vi_pos_back_short(''))
        self.assertEqual(4, vi_pos_back_short('abc def', 6))
        self.assertEqual(4, vi_pos_back_short('abc def', 5))
        self.assertEqual(0, vi_pos_back_short('abc def', 4))
        self.assertEqual(0, vi_pos_back_short('abc def', 3))
        self.assertEqual(8, vi_pos_back_short('abc  ...def u', 11))
        self.assertEqual(5, vi_pos_back_short('abc  ...def u', 8))
        self.assertEqual(0, vi_pos_back_short('abc  ...def u', 5))
        self.assertEqual(0, vi_pos_back_short('abc  ...def u'))
        self.assertEqual(8, vi_pos_back_short('abc  def... u', 11))
        self.assertEqual(5, vi_pos_back_short('abc  def... u', 8))
        self.assertEqual(0, vi_pos_back_short('abc  def... u', 5))
        self.assertEqual(
            11, vi_pos_back_short(
                'abc def... ghi...', 16, count=2))
        self.assertEqual(0, vi_pos_back_short('abc def... ghi...', 11, count=3))

    def test_pos_back_long(self):
        self.assertEqual(0, vi_pos_back_long(''))
        self.assertEqual(4, vi_pos_back_long('abc def', 6))
        self.assertEqual(4, vi_pos_back_long('abc def', 5))
        self.assertEqual(0, vi_pos_back_long('abc def', 4))
        self.assertEqual(0, vi_pos_back_long('abc def', 3))
        self.assertEqual(5, vi_pos_back_long('abc  ...def u', 11))
        self.assertEqual(0, vi_pos_back_long('abc  ...def u', 5))
        self.assertEqual(0, vi_pos_back_long('abc  ...def u'))
        self.assertEqual(5, vi_pos_back_long('abc  def... u', 11))
        self.assertEqual(0, vi_pos_back_long('abc  def... u', 5))
        self.assertEqual(4, vi_pos_back_long('abc def... ghi...', 16, count=2))

    def test_pos_find_char_forward(self):
        self.assertEqual(-1, vi_pos_find_char_forward('', 'x'))
        self.assertEqual(-1, vi_pos_find_char_forward('abc def', 'x'))
        self.assertEqual(4, vi_pos_find_char_forward('abc def', 'd'))
        self.assertEqual(4, vi_pos_find_char_forward('abc def', 'd', 3))
        self.assertEqual(-1, vi_pos_find_char_forward('abc def', 'd', 4))
        self.assertEqual(-1, vi_pos_find_char_forward('abc def', 'd', count=2))
        self.assertEqual(
            12, vi_pos_find_char_forward(
                'abc def abc def', 'd', count=2))

    def test_pos_find_char_backward(self):
        self.assertEqual(-1, vi_pos_find_char_backward('', 'x'))
        self.assertEqual(-1, vi_pos_find_char_backward('abc def', 'x', 6))
        self.assertEqual(4, vi_pos_find_char_backward('abc def', 'd', 6))
        self.assertEqual(4, vi_pos_find_char_backward('abc def', 'd', 5))
        self.assertEqual(-1, vi_pos_find_char_backward('abc def', 'd', 4))
        self.assertEqual(-1,
                         vi_pos_find_char_backward('abc def',
                                                   'd',
                                                   6,
                                                   count=2))
        self.assertEqual(
            4, vi_pos_find_char_backward(
                'abc def abc def', 'd', 14, count=2))

    def test_pos_to_char_forward(self):
        self.assertEqual(-1, vi_pos_to_char_forward('', 'x'))
        self.assertEqual(-1, vi_pos_to_char_forward('abc def', 'x'))
        self.assertEqual(3, vi_pos_to_char_forward('abc def', 'd'))
        self.assertEqual(3, vi_pos_to_char_forward('abc def', 'd', 2))
        self.assertEqual(-1, vi_pos_to_char_forward('abc def', 'd', 4))
        self.assertEqual(-1, vi_pos_to_char_forward('abc def', 'd', count=2))
        self.assertEqual(
            11, vi_pos_to_char_forward(
                'abc def abc def', 'd', count=2))

    def test_pos_to_char_backward(self):
        self.assertEqual(-1, vi_pos_to_char_backward('', 'x'))
        self.assertEqual(-1, vi_pos_to_char_backward('abc def', 'x', 6))
        self.assertEqual(5, vi_pos_to_char_backward('abc def', 'd', 6))
        self.assertEqual(5, vi_pos_to_char_backward('abc def', 'd', 5))
        self.assertEqual(-1, vi_pos_to_char_backward('abc def', 'd', 4))
        self.assertEqual(-1, vi_pos_to_char_backward('abc def',
                                                     'd', 6, count=2))
        self.assertEqual(
            5, vi_pos_to_char_backward(
                'abc def abc def', 'd', 14, count=2))

    def test_motion_word(self):
        '''motions: lowercase mode is alpha, digit and _, uppercase is delim by spaces
        w/W: forward short/long word'''
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"0"')
        r.input('"w"')
        self.assertEqual(9, r.line_cursor)
        r.input('"w"')
        self.assertEqual(12, r.line_cursor)
        r.input('"w"')
        self.assertEqual(14, r.line_cursor)
        r.input('"W"')
        self.assertEqual(23, r.line_cursor)

    def test_motion_word_multiplier(self):
        r = ViModeTest()
        r._set_line('a b c d e f g h i j k l m n o p q r s t u v w x y z')
        r.input('Escape')
        r.input('"0"')
        r.input('"2"')
        self.assertEqual(0, r.line_cursor)
        r.input('"w"')
        self.assertEqual(4, r.line_cursor)
        r.input('"2"')
        r.input('"0"')
        r.input('"w"')
        self.assertEqual(44, r.line_cursor)
        r.input('"2"')
        r.input('"W"')
        self.assertEqual(48, r.line_cursor)

    def test_motion_end(self):
        '''motions: lowercase mode is alpha, digit and _, uppercase is delim by spaces
        e/E: to end of short/long word'''
        r = ViModeTest()
        r._set_line('  abc_123  --def--456.789  x')
        r.input('Escape')
        r.input('"0"')
        r.input('"e"')
        self.assertEqual(8, r.line_cursor)
        r.input('"e"')
        self.assertEqual(12, r.line_cursor)
        r.input('"e"')
        self.assertEqual(15, r.line_cursor)
        r.input('"E"')
        self.assertEqual(24, r.line_cursor)

    def test_motion_end_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef gh ij kl mn op qr st uv wx yz')
        r.input('Escape')
        r.input('"0"')
        r.input('"3"')
        r.input('"e"')
        self.assertEqual(7, r.line_cursor)
        r.input('"4"')
        r.input('"E"')
        self.assertEqual(19, r.line_cursor)

    def test_motion_backward(self):
        '''motions: lowercase mode is alpha, digit and _, uppercase is delim by spaces
        b/B: backward short/long word'''
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(23, r.line_cursor)
        r.input('"b"')
        self.assertEqual(18, r.line_cursor)
        r.input('"b"')
        self.assertEqual(17, r.line_cursor)
        r.input('"B"')
        self.assertEqual(9, r.line_cursor)
        r.input('"B"')
        self.assertEqual(0, r.line_cursor)

    def test_motion_backward_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef gh ij kl mn op qr st uv wx yz')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(37, r.line_cursor)
        r.input('"3"')
        r.input('"b"')
        self.assertEqual(30, r.line_cursor)
        r.input('"5"')
        r.input('"b"')
        self.assertEqual(15, r.line_cursor)

    def test_motion_find_char_forward(self):
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"0"')
        r.input('"f"')
        self.assertEqual(0, r.line_cursor)
        r.input('"c"')
        self.assertEqual(2, r.line_cursor)

    def test_motion_find_char_backward(self):
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(23, r.line_cursor)
        r.input('"F"')
        self.assertEqual(23, r.line_cursor)
        r.input('"c"')
        self.assertEqual(2, r.line_cursor)

    def test_motion_find_char_forward_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef 01 23 45 ab cd ef 01 23 45 ab cd ef 01 23 45')
        r.input('Escape')
        r.input('"0"')
        r.input('"2"')
        r.input('"f"')
        r.input('"0"')
        self.assertEqual(27, r.line_cursor)

    def test_motion_find_char_backward_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef 01 23 45 ab cd ef 01 23 45 ab cd ef 01 23 45')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(52, r.line_cursor)
        r.input('"2"')
        r.input('"F"')
        r.input('"0"')
        self.assertEqual(27, r.line_cursor)

    def test_motion_find_char_again(self):
        r = ViModeTest()
        r._set_line('1234512345123451234512345')
        r.input('Escape')
        r.input('"0"')
        r.input('"2"')
        r.input('"f"')
        r.input('"3"')
        self.assertEqual(7, r.line_cursor)
        r.input('";"')
        self.assertEqual(12, r.line_cursor)
        r.input('"2"')
        r.input('";"')
        self.assertEqual(22, r.line_cursor)
        r.input('","')
        self.assertEqual(17, r.line_cursor)
        r.input('"2"')
        r.input('","')
        self.assertEqual(7, r.line_cursor)

    def test_motion_find_char_opposite(self):
        r = ViModeTest()
        r._set_line('1234512345123451234512345')
        r.input('Escape')
        r.input('"$"')
        r.input('"2"')
        r.input('"F"')
        r.input('"3"')
        self.assertEqual(17, r.line_cursor)
        r.input('";"')
        self.assertEqual(12, r.line_cursor)
        r.input('"2"')
        r.input('";"')
        self.assertEqual(2, r.line_cursor)
        r.input('","')
        self.assertEqual(7, r.line_cursor)
        r.input('"2"')
        r.input('","')
        self.assertEqual(17, r.line_cursor)
        r.input('"2"')
        r.input('","')
        self.assertEqual(17, r.line_cursor)

    def test_motion_to_char_forward(self):
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"0"')
        r.input('"t"')
        self.assertEqual(0, r.line_cursor)
        r.input('"c"')
        self.assertEqual(1, r.line_cursor)

    def test_motion_to_char_backward(self):
        r = ViModeTest()
        r._set_line('abc_123  def--456.789  x')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(23, r.line_cursor)
        r.input('"T"')
        self.assertEqual(23, r.line_cursor)
        r.input('"c"')
        self.assertEqual(3, r.line_cursor)

    def test_motion_to_char_forward_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef 01 23 45 ab cd ef 01 23 45 ab cd ef 01 23 45')
        r.input('Escape')
        r.input('"0"')
        r.input('"2"')
        r.input('"t"')
        r.input('"0"')
        self.assertEqual(26, r.line_cursor)

    def test_motion_to_char_backward_multiplier(self):
        r = ViModeTest()
        r._set_line('ab cd ef 01 23 45 ab cd ef 01 23 45 ab cd ef 01 23 45')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(52, r.line_cursor)
        r.input('"2"')
        r.input('"T"')
        r.input('"0"')
        self.assertEqual(28, r.line_cursor)

    def test_delete_word(self):
        r = ViModeTest()
        r._set_line('abc de fghi jkl mnopq rst')
        r.input('Escape')
        r.input('"0"')
        r.input('"d"')
        r.input('"w"')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual(r.line, 'de fghi jkl mnopq rst')
        r.input('"d"')
        r.input('"2"')
        r.input('"w"')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual(r.line, 'jkl mnopq rst')
        r.input('"2"')
        r.input('"d"')
        r.input('"w"')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual(r.line, 'rst')

    def test_delete_word_two_multipliers(self):
        r = ViModeTest()
        r._set_line('abc de fghi jkl mnopq rst uv wx yz')
        r.input('Escape')
        r.input('"0w"')
        r.input('"2d3w"')
        self.assertEqual(4, r.line_cursor)
        self.assertEqual(r.line, 'abc wx yz')

    def test_delete_find_char_forward_two_multipliers(self):
        r = ViModeTest()
        r._set_line(
            '0123456789012345678901234567890123456789012345678901234567890123456789')
        r.input('Escape')
        r.input('"0"')
        r.input('"2d3f4"')
        self.assertEqual(r.line, '567890123456789')

    def test_delete_end_of_line(self):
        r = ViModeTest()
        r._set_line('abc de fghi jkl mnopq rst uv wx yz')
        r.input('Escape')
        r.input('"0w"')
        r.input('"D"')
        self.assertEqual(r.line, 'abc ')

    def test_two_lines(self):
        r = ViModeTest()
        r.input('"abcdef"')
        self.assertEqual(r.line, 'abcdef')
        r.input('Escape')
        r.input('"0iqq"')
        self.assertEqual(r.line, 'qqabcdef')
        r.input('Return')
        self.assertEqual(r.line, '')
        r.input('"xyz"')
        self.assertEqual(r.line, 'xyz')

    def test_delete_word_short_to_end_of_line(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"03dw"')
        self.assertEqual('', r.line)
        r._set_line('abc def ghi  ')
        r.input('Escape')
        r.input('"03dw"')
        self.assertEqual('', r.line)

    def test_delete_word_long_to_end_of_line(self):
        r = ViModeTest()
        r._set_line('a.c d.f g.i')
        r.input('Escape')
        r.input('"03dW"')
        self.assertEqual('', r.line)
        r._set_line('a.c d.f g.i  ')
        r.input('Escape')
        r.input('"03dW"')
        self.assertEqual('', r.line)

    def test_delete_end_short_to_end_of_line(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"03de"')
        self.assertEqual('', r.line)
        r._set_line('abc def ghi  ')
        r.input('Escape')
        r.input('"03de"')
        self.assertEqual('  ', r.line)

    def test_delete_end_long_to_end_of_line(self):
        r = ViModeTest()
        r._set_line('a.c d.f g.i')
        r.input('Escape')
        r.input('"03dE"')
        self.assertEqual('', r.line)
        r._set_line('a.c d.f g.i  ')
        r.input('Escape')
        r.input('"03dE"')
        self.assertEqual('  ', r.line)

    def test_delete_back_short_to_begining_of_line(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"$3db"')
        self.assertEqual('i', r.line)
        r._set_line('abc def ghi  ')
        r.input('Escape')
        r.input('"$3db"')
        self.assertEqual(' ', r.line)

    def test_delete_back_long_to_begining_of_line(self):
        r = ViModeTest()
        r._set_line('a.c d.f g.i')
        r.input('Escape')
        r.input('"$3dB"')
        self.assertEqual('i', r.line)
        r._set_line('a.c d.f g.i  ')
        r.input('Escape')
        r.input('"$3dB"')
        self.assertEqual(' ', r.line)

    def test_delete_dollar(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0ld$"')
        self.assertEqual(r.line, 'a')
        self.assertEqual(r.line_cursor, 0)

    def test_motion_left(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(10, r.line_cursor)
        r.input('"h"')
        self.assertEqual(9, r.line_cursor)
        r.input('"2h"')
        self.assertEqual(7, r.line_cursor)
        r.input('"2d3h"')
        self.assertEqual(1, r.line_cursor)
        self.assertEqual('a ghi', r.line)
        r.input('"4dh"')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual(' ghi', r.line)

    def test_motion_right(self):
        r = ViModeTest()
        r.input('Escape')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)
        r.input('"a"')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)
        r.input('"abc"')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 3)
        r.input('Escape')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 2)
        r.input('"l"')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 2)
        r.input('Left')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 1)
        r.input('"l"')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 2)
        r.input('"l"')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 2)

    def test_motion_right_delete(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0"')
        self.assertEqual(0, r.line_cursor)
        r.input('"l"')
        self.assertEqual(1, r.line_cursor)
        r.input('"2l"')
        self.assertEqual(3, r.line_cursor)
        r.input('"2d3l"')
        self.assertEqual(3, r.line_cursor)
        self.assertEqual('abchi', r.line)
        r.input('"4dl"')
        self.assertEqual(2, r.line_cursor)
        self.assertEqual('abc', r.line)

    def test_backspace_motion(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(10, r.line_cursor)
        r.input('BackSpace')
        self.assertEqual(9, r.line_cursor)
        r.input('"2"')
        r.input('BackSpace')
        self.assertEqual(7, r.line_cursor)
        r.input('"2d3"')
        r.input('BackSpace')
        self.assertEqual(1, r.line_cursor)
        self.assertEqual('a ghi', r.line)
        r.input('"4d"')
        r.input('BackSpace')
        self.assertEqual(0, r.line_cursor)
        self.assertEqual(' ghi', r.line)

    def test_backspace_insert(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"$"')
        self.assertEqual(10, r.line_cursor)
        r.input('"i"')
        self.assertEqual(10, r.line_cursor)
        r.input('BackSpace')
        self.assertEqual(9, r.line_cursor)
        self.assertEqual('abc def gi', r.line)

    def test_insert_lower_i(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0w"')
        r.input('"i"')
        r.input('"zz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'abc zzdef ghi')
        r.input('Escape')
        r.input('"w"')
        r.input('"2iyy"')
        r.input('Escape')
        self.assertEqual(r.line, 'abc zzdef yyyyghi')

    def test_insert_upper_i(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0w"')
        r.input('"I"')
        r.input('"zz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zzabc def ghi')
        r.input('Escape')
        r.input('"w"')
        r.input('"2Iyy"')
        r.input('Escape')
        self.assertEqual(r.line, 'yyyyzzabc def ghi')

    def test_append_lower_a(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0"')
        r.input('"a"')
        r.input('"zz"')
        self.assertEqual(r.line, 'azzbc def ghi')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        r.input('"w"')
        r.input('"2ayy"')
        r.input('Escape')
        self.assertEqual(r.line, 'azzbc dyyyyef ghi')

    def test_append_upper_a_simple(self):
        r = ViModeTest()
        r._set_line('')
        r.input('Escape')
        r.input('"2A"')
        r.input('"jj"')
        r.input('Escape')
        self.assertEqual(r.line, 'jjjj')
        self.assertTrue(not r.vi_is_insert_mode)

    def test_append_upper_a(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0"')
        r.input('"A"')
        r.input('"zz"')
        self.assertEqual(r.line, 'abc def ghizz')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        r.input('"0w"')
        r.input('"2Ayy"')
        r.input('Escape')
        self.assertEqual(r.line, 'abc def ghizzyyyy')

    def test_delete_lower_x(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0w"')
        r.input('"x"')
        self.assertEqual(r.line, 'abc ef')
        r.input('"4x"')
        self.assertEqual(r.line, 'abc ')
        self.assertEqual(r.line_cursor, 3)
        r.input('"x"')
        self.assertEqual(r.line, 'abc')
        self.assertEqual(r.line_cursor, 2)
        r.input('"x"')
        self.assertEqual(r.line, 'ab')
        self.assertEqual(r.line_cursor, 1)
        r.input('"x"')
        self.assertEqual(r.line, 'a')
        self.assertEqual(r.line_cursor, 0)
        r.input('"x"')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)
        r.input('"x"')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)

    def test_delete_upper_x(self):
        r = ViModeTest()
        r._set_line('abc def')
        self.assertEqual(r.line_cursor, 7)
        r.input('Escape')
        self.assertEqual(r.line_cursor, 6)
        r.input('"$"')
        self.assertEqual(r.line_cursor, 6)
        r.input('"X"')
        self.assertEqual(r.line, 'abc df')
        self.assertEqual(r.line_cursor, 5)
        r.input('"4X"')
        self.assertEqual(r.line, 'af')
        self.assertEqual(r.line_cursor, 1)
        r.input('"2X"')
        self.assertEqual(r.line, 'f')
        self.assertEqual(r.line_cursor, 0)
        r.input('"X"')
        self.assertEqual(r.line, 'f')
        self.assertEqual(r.line_cursor, 0)

    def test_substitute_lower_s(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0"')
        r.input('"s"')
        r.input('"qq"')
        r.input('Escape')
        self.assertEqual(r.line, 'qqbc def')
        self.assertEqual(r.line_cursor, 1)
        r.input('"3s"')
        r.input('"yyy"')
        r.input('Escape')
        self.assertEqual(r.line, 'qyyy def')
        self.assertEqual(r.line_cursor, 3)
        r.input('"w"')
        r.input('"5"')
        r.input('"s"')
        r.input('"zz"')
        r.input('Escape')
        self.assertEqual(r.line, 'qyyy zz')
        self.assertEqual(r.line_cursor, 6)

    def test_change_to_end_of_line(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0w"')
        r.input('"C"')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"123"')
        self.assertEqual(r.line, 'abc 123')
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)

    def test_change_whole_line(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0w"')
        r.input('"S"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, '')
        r.input('"123"')
        self.assertEqual(r.line, '123')
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)

    def test_change_word_short(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0cwzzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zzz def ghi')
        self.assertEqual(r.line_cursor, 3)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 2)
        r.input('"w"')
        self.assertEqual(r.line_cursor, 4)
        r.input('"2cwyyy"')
        self.assertEqual(r.line, 'zzz yyy')
        r.input('Escape')
        self.assertEqual(r.line, 'zzz yyy')

    def test_change_word_long(self):
        r = ViModeTest()
        r._set_line('abc.def ghi.jkl mno.pqr')
        r.input('Escape')
        r.input('"0cWss"')
        self.assertEqual(r.line, 'ss ghi.jkl mno.pqr')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 2)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 1)
        r.input('"w2."')
        self.assertEqual(r.line, 'ss ss')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 4)

    def test_change_end_short(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0cezzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zzz def ghi')
        self.assertEqual(r.line_cursor, 3)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 2)
        r.input('"w2."')
        self.assertEqual(r.line, 'zzz zzz')

    def test_change_end_long(self):
        r = ViModeTest()
        r._set_line('abc.def ghi jkl.mno pqr stu.vwx')
        r.input('Escape')
        r.input('"02cEzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zz jkl.mno pqr stu.vwx')
        self.assertEqual(r.line_cursor, 2)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 1)
        r.input('"w2."')
        self.assertEqual(r.line, 'zz zz stu.vwx')
        self.assertEqual(r.line_cursor, 5)

    def test_change_back_short(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"$cbzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'abc def zzi')
        self.assertEqual(r.line_cursor, 10)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 9)
        r.input('"b2."')
        self.assertEqual(r.line, 'zzzzi')
        self.assertEqual(r.line_cursor, 2)

    def test_change_back_long(self):
        r = ViModeTest()
        r._set_line('abc.def ghi jkl.mno pqr stu.vwx')
        r.input('Escape')
        r.input('"$2cBzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'abc.def ghi jkl.mno zzx')
        self.assertEqual(r.line_cursor, 22)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 21)
        r.input('"5."')
        self.assertEqual(r.line, 'zzzx')
        self.assertEqual(r.line_cursor, 2)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_change_find_lower(self):
        r = ViModeTest()
        r._set_line('aa bb cc dd ee aa bb cc dd ee')
        r.input('Escape')
        r.input('"0cfbzz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zzb cc dd ee aa bb cc dd ee')
        self.assertEqual(r.line_cursor, 2)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 1)
        r.input('"c2fcyy"')
        self.assertEqual(r.line, 'zyy dd ee aa bb cc dd ee')
        r.input('Escape')
        r.input('"."')
        self.assertEqual(r.line, 'zyyy dd ee')

    def test_change_find_upper(self):
        r = ViModeTest()
        r._set_line('aa bb cc aa bb cc')
        r.input('Escape')
        r.input('"$2c2Fazz"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'zzc')
        self.assertEqual(r.line_cursor, 2)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 1)
        r.input('"."')
        self.assertEqual(r.line, 'zzc')

    def test_change_to_lower(self):
        r = ViModeTest()
        r._set_line('aa bb cc aa bb cc aa bb cc')
        r.input('Escape')
        r.input('"02c2ta"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'aa bb cc')
        self.assertEqual(r.line_cursor, 0)
        r.input('"zz "')
        self.assertEqual(r.line, 'zz aa bb cc')
        self.assertEqual(r.line_cursor, 3)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 2)

    def test_change_to_upper(self):
        r = ViModeTest()
        r._set_line('aa bb cc aa bb cc aa bb cc')
        r.input('Escape')
        r.input('"$2c2Ta"')
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.line, 'aa bb cc ac')
        self.assertEqual(r.line_cursor, 10)
        r.input('"zz"')
        self.assertEqual(r.line, 'aa bb cc azzc')
        self.assertEqual(r.line_cursor, 12)
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 11)
        r.input('"3."')
        self.assertEqual(r.line, 'azzzc')
        # The following fails but it does not seem that important
        # self.assertEqual (r.line_cursor, 2)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_pos_matching(self):
        self.assertEqual(6, vi_pos_matching('aa (bb)'))
        self.assertEqual(6, vi_pos_matching('aa (bb)', 3))
        self.assertEqual(3, vi_pos_matching('aa (bb)', 6))
        self.assertEqual(11, vi_pos_matching('aa (bb (cc))'))
        self.assertEqual(3, vi_pos_matching('aa (bb (cc))', 11))
        self.assertEqual(10, vi_pos_matching('aa (bb (cc))', 4))
        self.assertEqual(7, vi_pos_matching('aa (bb (cc))', 10))
        self.assertEqual(7, vi_pos_matching('aa (bb (cc))', 8))
        self.assertEqual(3, vi_pos_matching('aa (bb (cc) dd)', 12))
        self.assertEqual(3, vi_pos_matching('aa (bb (cc) dd)', 14))
        self.assertEqual(-1, vi_pos_matching('aa ((bb (cc) dd)', 3))
        self.assertEqual(-1, vi_pos_matching('aa (bb (cc) dd) ee)', 16))
        self.assertEqual(-1, vi_pos_matching('aa (bb (cc) dd) ee)', 18))
        self.assertEqual(6, vi_pos_matching('aa <bb>'))
        self.assertEqual(11, vi_pos_matching('aa <bb <cc>>'))
        self.assertEqual(10, vi_pos_matching('aa <bb <cc>>', 4))
        self.assertEqual(6, vi_pos_matching('aa {bb}'))
        self.assertEqual(11, vi_pos_matching('aa {bb {cc}}'))
        self.assertEqual(10, vi_pos_matching('aa {bb {cc}}', 4))
        self.assertEqual(6, vi_pos_matching('aa [bb]'))
        self.assertEqual(11, vi_pos_matching('aa [bb [cc]]'))
        self.assertEqual(10, vi_pos_matching('aa [bb [cc]]', 4))

    def test_matching_paren_forward(self):
        r = ViModeTest()
        r._set_line('abc (def (ghi)) jkl')
        r.input('Escape')
        r.input('"0w"')
        r.input('"d"')
        r.input('"%"')
        self.assertEqual(r.line, 'abc  jkl')
        self.assertEqual(r.line_cursor, 4)

    def test_matching_paren_backward(self):
        r = ViModeTest()
        r._set_line('abc (def (ghi)) jkl')
        r.input('Escape')
        r.input('"0w"')
        r.input('"%"')
        self.assertEqual(r.line_cursor, 14)
        r.input('"d"')
        r.input('"%"')
        self.assertEqual(r.line, 'abc  jkl')
        self.assertEqual(r.line_cursor, 4)

    def test_yank_and_put(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0"')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"yw"')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line, 'abc def')
        self.assertEqual(r.line_cursor, 0)
        r.input('"P"')
        self.assertEqual(r.line, 'abc abc def')
        self.assertEqual(r.line_cursor, 3)
        r.input('"p"')
        self.assertEqual(r.line, 'abc abc abc def')
        self.assertEqual(r.line_cursor, 7)

    def test_put_multiple(self):
        r = ViModeTest()
        r._set_line('001122')
        r.input('Escape')
        r.input('"0"')
        r.input('"y3l"')
        self.assertTrue(not r.vi_is_insert_mode)
        self.assertEqual(r.line_cursor, 0)
        r.input('"2P"')
        self.assertEqual(r.line, '001001001122')
        self.assertEqual(r.line_cursor, 5)
        r.input('"f2"')
        r.input('"3p"')
        self.assertEqual(r.line, '001001001120010010012')
        self.assertEqual(r.line_cursor, 19)

    def test_put_undo(self):
        r = ViModeTest()
        r._set_line('aaa b ccc')
        r.input('Escape')
        r.input('"0ywwp"')
        self.assertEqual(r.line, 'aaa baaa  ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa b ccc')
        r.input('"P"')
        self.assertEqual(r.line, 'aaa aaa b ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa b ccc')

    def test_x_and_p(self):
        r = ViModeTest()
        r._set_line('abc')
        r.input('Escape')
        r.input('"0xp"')
        self.assertEqual(r.line, 'bac')

    def test_delete_and_put(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0dwep"')
        self.assertEqual(r.line, 'defabc ')
        r.input('"0xp"')
        self.assertEqual(r.line, 'edfabc ')
        r.input('"p"')
        self.assertEqual(r.line, 'eddfabc ')

    def test_dot_simple(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0x"')
        self.assertEqual(r.line, 'bc def')
        r.input('"."')
        self.assertEqual(r.line, 'c def')
        r.input('"3."')
        self.assertEqual(r.line, 'ef')

    def test_dot_movement_not_repeated_one(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0x"')
        self.assertEqual(r.line, 'bc def')
        r.input('"$."')
        self.assertEqual(r.line, 'bc de')
        r.input('"0."')
        self.assertEqual(r.line, 'c de')
        r.input('"$."')
        self.assertEqual(r.line, 'c d')
        r.input('"^."')
        self.assertEqual(r.line, ' d')

    def test_dot_movement_not_repeated_two(self):
        r = ViModeTest()
        r._set_line('abc def ghi jkl mno pqr')
        r.input('Escape')
        r.input('"0x"')
        self.assertEqual(r.line, 'bc def ghi jkl mno pqr')
        r.input('"w."')
        self.assertEqual(r.line, 'bc ef ghi jkl mno pqr')
        r.input('"fg."')
        self.assertEqual(r.line, 'bc ef hi jkl mno pqr')
        r.input('"2b."')
        self.assertEqual(r.line, 'c ef hi jkl mno pqr')
        r.input('"3e."')
        self.assertEqual(r.line, 'c ef hi jk mno pqr')
        r.input('"Fh."')
        self.assertEqual(r.line, 'c ef i jk mno pqr')
        r.input('"tn."')
        self.assertEqual(r.line, 'c ef i jk no pqr')
        r.input('"3h."')
        self.assertEqual(r.line, 'c ef i k no pqr')
        r.input('"5l."')
        self.assertEqual(r.line, 'c ef i k no qr')

    def test_dot_insert(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0"')
        r.input('"2izz "')
        r.input('Escape')
        self.assertEqual(r.line, 'zz zz abc def')
        r.input('"2w."')
        self.assertEqual(r.line, 'zz zz abc zz zz def')

    def test_dot_delete_word(self):
        r = ViModeTest()
        r._set_line('0 1 2 3 4 5 6 7 8 9')
        r.input('Escape')
        r.input('"02dw"')
        self.assertEqual(r.line, '2 3 4 5 6 7 8 9')
        r.input('"."')
        self.assertEqual(r.line, '4 5 6 7 8 9')
        r.input('"1."')
        self.assertEqual(r.line, '5 6 7 8 9')

    def test_dot_override_multiplier(self):
        r = ViModeTest()
        r._set_line('ab ab ab ab  ab ab ab ab  ab ab ab ab  ab ab ab ab')
        r.input('Escape')
        r.input('"02d2fb"')
        self.assertEqual(r.line, '  ab ab ab ab  ab ab ab ab  ab ab ab ab')
        r.input('"."')
        self.assertEqual(r.line, '  ab ab ab ab  ab ab ab ab')
        r.input('"3."')
        self.assertEqual(r.line, ' ab  ab ab ab ab')
        r.input('"."')
        self.assertEqual(r.line, ' ab ab')

    def test_dot_yank_and_put(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0ywP"')
        self.assertEqual(r.line, 'abc abc def')
        r.input('"."')
        self.assertEqual(r.line, 'abcabc  abc def')
        r.input('"p"')
        self.assertEqual(r.line, 'abcabc abc  abc def')
        r.input('"2."')
        self.assertEqual(r.line, 'abcabc abc abc abc  abc def')

    def test_dot_insert_begin(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"Izz "')
        r.input('Escape')
        self.assertEqual(r.line, 'zz abc def')
        r.input('"."')
        self.assertEqual(r.line, 'zz zz abc def')
        r.input('"2."')
        self.assertEqual(r.line, 'zz zz zz zz abc def')

    def test_dot_append_end(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"A yy"')
        r.input('Escape')
        self.assertEqual(r.line, 'abc def yy')
        r.input('"."')
        self.assertEqual(r.line, 'abc def yy yy')
        r.input('"2."')
        self.assertEqual(r.line, 'abc def yy yy yy yy')

    def test_dot_insert_lower(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"b2izz "')
        r.input('Escape')
        self.assertEqual(r.line, 'abc zz zz def')
        r.input('"3."')
        self.assertEqual(r.line, 'abc zz zzzz zz zz  def')

    def test_dot_append_lower(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"0e2a zz"')
        r.input('Escape')
        self.assertEqual(r.line, 'abc zz zz def')
        r.input('"1."')
        self.assertEqual(r.line, 'abc zz zz zz def')

    def test_dot_substitute_lower(self):
        r = ViModeTest()
        r._set_line('abc def')
        r.input('Escape')
        r.input('"03sqq"')
        r.input('Escape')
        self.assertEqual(r.line, 'qq def')
        r.input('"2."')
        self.assertEqual(r.line, 'qqqdef')

    def test_undo(self):
        r = ViModeTest()
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"0wdw"')
        self.assertEqual(r.line, 'abc ghi')
        self.assertEqual(r.line_cursor, 4)
        r.input('"u"')
        self.assertEqual(r.line, 'abc def ghi')
        self.assertEqual(r.line_cursor, 4)

    def test_undo_line(self):
        r = ViModeTest()
        r._set_line('')
        r.input('"abc def ghi"')
        r.input('Escape')
        r.input('"0dwdw"')
        self.assertEqual(r.line, 'ghi')
        r.input('"U"')
        self.assertEqual(r.line, '')

    def test_undo_line_with_history(self):
        r = ViModeTest()
        r.add_history('abc 123')
        r._set_line('')
        r.input('"abc def ghi"')
        r.input('Escape')
        r.input('"0dwdw"')
        self.assertEqual(r.line, 'ghi')
        r.input('"U"')
        self.assertEqual(r.line, '')

    def test_history_no_match(self):
        r = ViModeTest()
        r.add_history('abc 123')
        r.add_history('def 456')
        r.add_history('ghi 789')
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"/"')
        self.assertEqual(r.line, '/')
        self.assertEqual(r.line_cursor, 1)
        r.input('"zz"')
        self.assertEqual(r.line, '/zz')
        self.assertEqual(r.line_cursor, 3)
        self.assertEqual(r.console.bell_count, 0)
        r.input('Return')
        # TODO should bell be rung here?
        # self.assertEqual (r.console.bell_count, 1)
        self.assertEqual(r.line, 'abc def ghi')
        self.assertEqual(r.line_cursor, 10)

    def test_history_found_match(self):
        r = ViModeTest()
        r.add_history('abc 123')
        r.add_history('def 456')
        r.add_history('ghi 789')
        r._set_line('abc def ghi')
        r.input('Escape')
        r.input('"/"')
        self.assertEqual(r.line, '/')
        self.assertEqual(r.line_cursor, 1)
        r.input('"de"')
        self.assertEqual(r.line, '/de')
        self.assertEqual(r.line_cursor, 3)
        r.input('Return')
        self.assertEqual(r.line, 'def 456')
        self.assertEqual(r.line_cursor, 0)

    def test_history_multi_match(self):
        r = ViModeTest()
        r.add_history('xyz 123')
        r.add_history('xyz 456')
        r.add_history('xyz 789')
        r._set_line('abc def')
        r.input('Escape')
        r.input('"/xyz"')
        r.input('Return')
        self.assertEqual(r.line, 'xyz 789')
        r.input('"n"')
        self.assertEqual(r.line, 'xyz 456')
        r.input('"n"')
        self.assertEqual(r.line, 'xyz 123')
        self.assertEqual(r.console.bell_count, 0)
        r.input('"n"')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 1)
        self.assertEqual(r.line, 'xyz 123')
        r.input('"N"')
        self.assertEqual(r.line, 'xyz 456')
        r.input('"N"')
        self.assertEqual(r.line, 'xyz 789')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 1)
        r.input('"N"')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 2)
        self.assertEqual(r.line, 'xyz 789')

    def test_history_search_empty_string(self):
        r = ViModeTest()
        r.add_history('xyz 123')
        r.add_history('xyz 456')
        r.add_history('xyz 789')
        r.input('Escape')
        r.input('"/"')
        r.input('Return')
        self.assertEqual(r.line, '')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 1)
        r.input('"/"')
        r.input('Return')
        self.assertEqual(r.line, '')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 2)
        r.input('"/x"')
        r.input('Return')
        self.assertEqual(r.line, 'xyz 789')
        r.input('"/"')
        r.input('Return')
        self.assertEqual(r.line, 'xyz 456')

    def test_history_search_again_after_return(self):
        r = ViModeTest()
        r.add_history('xyz 123')
        r.add_history('xyz 456')
        r.add_history('xyz 789')
        r._set_line('abc def')
        r.input('Escape')
        r.input('"/xyz"')
        r.input('Return')
        self.assertEqual(r.line, 'xyz 789')
        r.input('"n"')
        self.assertEqual(r.line, 'xyz 456')
        r.input('Return')
        self.assertEqual(r.line, '')
        r.input('Escape')
        r.input('"n"')
        self.assertEqual(r.line, 'xyz 123')
        r.input('Return')
        r.input('Escape')
        r.input('"N"')
        self.assertEqual(r.line, 'xyz 456')

    def test_history_search_again_after_search_failed(self):
        r = ViModeTest()
        r.add_history('xyz 123')
        r.add_history('xyz 456')
        r.add_history('xyz 789')
        r._set_line('abc def')
        r.input('Escape')
        r.input('"/xyz"')
        r.input('Return')
        self.assertEqual(r.line, 'xyz 789')
        r.input('"C"')
        self.assertEqual(r.line, '')
        r.input('Escape')
        self.assertEqual(r.console.bell_count, 0)
        r.input('"/abc"')
        r.input('Return')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 1)
        self.assertEqual(r.line, '')
        r.input('Escape')
        r.input('"n"')
        self.assertEqual(r.line, '')

    def test_history_search_and_backspace(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.add_history('bbb')
        r._set_line('')
        r.input('Escape')
        r.input('"/aaz"')
        self.assertEqual(r.line, '/aaz')
        r.input('BackSpace')
        self.assertEqual(r.line, '/aa')
        r.input('Return')
        self.assertEqual(r.line, 'aaa')
        r.input('Escape')
        r.input('"/z"')
        r.input('BackSpace')
        r.input('BackSpace')
        self.assertEqual(r.line, '')
        r.input('"j"')
        self.assertEqual(r.line, 'bbb')
        r.input('"k"')
        self.assertEqual(r.line, 'aaa')

    def test_history_insert_mode(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.add_history('bbb')
        r.add_history('ccc')
        r.input('Up')
        self.assertEqual(r.line, 'ccc')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"z"')
        self.assertEqual(r.line, 'cccz')
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('Up')
        self.assertEqual(r.line, 'bbb')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"z"')
        self.assertEqual(r.line, 'bbbz')
        r.input('Escape')
        r.input('"k"')
        self.assertEqual(r.line, 'aaa')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"iz"')
        self.assertEqual(r.line, 'zaaa')
        r.input('Down')
        self.assertEqual(r.line, 'bbb')
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"y"')
        self.assertEqual(r.line, 'bbby')
        r.input('Escape')
        r.input('"j"')
        self.assertEqual(r.line, 'ccc')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"iy"')
        self.assertEqual(r.line, 'yccc')
        self.assertTrue(r.vi_is_insert_mode)

    def test_history_cursor_arrow(self):
        r = ViModeTest()
        self.assertEqual(r._history.history_cursor, 0)
        r.add_history('aaa')
        self.assertEqual(r._history.history_cursor, 1)
        r.add_history('bbb')
        self.assertEqual(r._history.history_cursor, 2)
        self.assertEqual(r.line, '')
        r.input('Up')
        self.assertEqual(r._history.history_cursor, 1)
        self.assertEqual(r.line, 'bbb')
        r.input('Up')
        self.assertEqual(r._history.history_cursor, 0)
        self.assertEqual(r.line, 'aaa')
        r.input('Down')
        self.assertEqual(r._history.history_cursor, 1)
        self.assertEqual(r.line, 'bbb')
        r.input('Down')
        self.assertEqual(r._history.history_cursor, 2)
        self.assertEqual(r.line, '')
        r.input('Up')
        self.assertEqual(r._history.history_cursor, 1)
        self.assertEqual(r.line, 'bbb')

    def test_history_control_n_and_p(self):
        r = ViModeTest()
        r.add_history('aa')
        r.add_history('bbb')
        self.assertEqual(r.line, '')
        r.input('Control-p')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Control-p')
        self.assertEqual(r.line, 'aa')
        self.assertEqual(r.line_cursor, 2)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Control-n')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Control-n')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Control-p')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 2)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('Control-p')
        self.assertEqual(r.line, 'aa')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('Control-n')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('Control-n')
        self.assertEqual(r.line, '')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"/a"')
        r.input('Return')
        self.assertEqual(r.line, 'aa')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('Control-n')
        self.assertEqual(r.line, 'bbb')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_history_cursor_j_and_k(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.input('Escape')
        r.input('"kiz"')
        self.assertEqual(r.line, 'zaaa')
        r.input('Escape')
        r.input('"j"')
        self.assertEqual(r.line, '')

    def test_history_input_j_and_k(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.input('Escape')
        r.input('"kAjk"')
        self.assertEqual(r.line, 'aaajk')

    def test_history_cursor_search(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.input('Escape')
        r.input('"/a"')
        r.input('Return')
        self.assertEqual(r.line, 'aaa')
        r.input('"iz"')
        self.assertEqual(r.line, 'zaaa')
        self.assertEqual(r.console.bell_count, 0)
        r.input('Escape')
        r.input('"j"')
        self.assertEqual(r.line, 'zaaa')
        # TODO check bell ringing
        # self.assertEqual (r.console.bell_count, 1)

    def test_history_undo(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.input('Escape')
        r.input('"k"')
        r.input('"A b"')
        r.input('Escape')
        r.input('"A c"')
        r.input('Escape')
        self.assertEqual(r.line, 'aaa b c')
        r.input('"U"')
        self.assertEqual(r.line, 'aaa')

    def test_history_arrow_undo(self):
        r = ViModeTest()
        r.add_history('aaa')
        r.input('Up')
        r.input('" zz"')
        self.assertEqual(r.line, 'aaa zz')
        r.input('Escape')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa')

# TODO: mode support?
#     def test_mode (self):
#         r = ViModeTest ()
#         self.assertEqual (r.editing_mode, Readline.mode_vi)
#         self.assertEqual (r.mode (verbose=False), Readline.mode_vi)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 0)
#         r.vi ()
#         self.assertEqual (r.editing_mode, Readline.mode_vi)
#         self.assertEqual (r.mode (verbose=False), Readline.mode_vi)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 0)
#         r.emacs ()
#         self.assertEqual (r.editing_mode, Readline.mode_emacs)
#         self.assertEqual (r.mode (verbose=False), Readline.mode_emacs)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 1)
#         r.emacs ()
#         self.assertEqual (r.editing_mode, Readline.mode_emacs)
#         self.assertEqual (r.mode (verbose=False), Readline.mode_emacs)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 1)
#         r.vi ()
#         self.assertEqual (r.editing_mode, Readline.mode_vi)
#         self.assertEqual (r.mode (verbose=False), Readline.mode_vi)
#         self.assertEqual (r.count_vi_editing_mode, 2)
#         self.assertEqual (r.count_emacs_editing_mode, 1)
#
#     def test_switch_mode (self):
#         r = ViModeTest ()
#         r._set_line ('')
#         r.input ('Escape')
#         self.assertEqual (r.editing_mode, Readline.mode_vi)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 0)
#         r.input ('"abc"')
#         r.input ('Control-e')
#         self.assertEqual (r.editing_mode, Readline.mode_emacs)
#         self.assertEqual (r.count_vi_editing_mode, 1)
#         self.assertEqual (r.count_emacs_editing_mode, 1)
#         r.input ('Meta-Control-j')
#         self.assertEqual (r.editing_mode, Readline.mode_vi)
#         self.assertEqual (r.count_vi_editing_mode, 2)
#         self.assertEqual (r.count_emacs_editing_mode, 1)

# TODO: show history support?
#     def test_history_output (self):
#         import StringIO
#         sio = StringIO.StringIO ()
#         r = ViModeTest ()
#         r.add_history ('abc')
#         r.add_history ('def')
#         r.add_history ('ghi')
#         r.show_history (sio)
#         sio.seek (0)
#         self.assertEqual (sio.read(), '  1 abc\n  2 def\n  3 ghi\n')

    def test_editor(self):
        vee = ViExternalEditorTest('qwerty before')
        self.assertTrue(vee.sio_write.closed)
        self.assertEqual(vee.command, 'vim.exe temp.py')
        self.assertTrue(vee.sio_read.closed)
        self.assertEqual(vee.remove, 'temp.py')
        self.assertEqual(vee.result, 'qwerty after')

    def test_completer(self):
        r = ViModeTest()
        r.lst_completions = ['aab', 'aac', 'aad', ]
        r.input('"aa"')
        r.input('Tab')
        self.assertEqual(r.line, 'aa')
        self.assertEqual(r.console.text, '\naab \naac \naad \n')

    def test_completer_star(self):
        r = ViModeTest()
        r.lst_completions = ['bbc', 'bbd', 'bbe', ]
        r.input('"aa bb"')
        r.input('Escape')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"*"')
        self.assertEqual(r.line, 'aa bbc bbd bbe ')
        self.assertEqual(r.line_cursor, 15)
        self.assertTrue(r.vi_is_insert_mode)
        self.assertEqual(r.console.text, '')
        r.input('" "')
        r.input('Escape')
        self.assertEqual(r.line, 'aa bbc bbd bbe  ')
        self.assertEqual(r.line_cursor, 15)
        r.input('"."')
        self.assertEqual(r.line, 'aa bbc bbd bbe bbc bbd bbe ')
        self.assertEqual(r.line_cursor, 27)

    def test_completer_beginning_of_line(self):
        r = ViModeTest()
        r.input('Tab')
        self.assertEqual(r.line, '    ')
        self.assertEqual(r.line_cursor, 4)
        r.input('Space')
        self.assertEqual(r.line, '     ')
        self.assertEqual(r.line_cursor, 5)
        r.input('Tab')
        self.assertEqual(r.line, '        ')
        self.assertEqual(r.line_cursor, 8)
        r.input('Space')
        r.input('Space')
        r.input('Space')
        self.assertEqual(r.line, '           ')
        self.assertEqual(r.line_cursor, 11)
        r.input('Tab')
        self.assertEqual(r.line, '            ')
        self.assertEqual(r.line_cursor, 12)
        r.input('Tab')
        self.assertEqual(r.line, '                ')
        self.assertEqual(r.line_cursor, 16)

    def test_replace_lower(self):
        r = ViModeTest()
        r._set_line('aaa bbb ccc')
        r.input('Escape')
        r.input('"0ry"')
        self.assertEqual(r.line, 'yaa bbb ccc')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"i"')
        self.assertEqual(r.line, 'yaa bbb ccc')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"x"')
        self.assertEqual(r.line, 'xyaa bbb ccc')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        self.assertEqual(r.line, 'xyaa bbb ccc')
        self.assertEqual(r.line_cursor, 0)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"ll"')
        r.input('"2rz"')
        self.assertEqual(r.line, 'xyzz bbb ccc')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"w7."')
        self.assertEqual(r.line, 'xyzz zzzzzzz')
        self.assertEqual(r.line_cursor, 11)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_replace_lower_undo(self):
        r = ViModeTest()
        r._set_line('aaa')
        r.input('Escape')
        # print('xx', sys._getframe().f_lineno, r._vi_undo_cursor, r._vi_undo_stack)
        r.input('"0rz"')
        self.assertEqual(r.line, 'zaa')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa')
        r.input('"2."')
        self.assertEqual(r.line, 'zza')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa')

    def test_replace_lower_escape(self):
        r = ViModeTest()
        r._set_line('aaa')
        r.input('Escape')
        r.input('"0r"')
        self.assertEqual(r.line, 'aaa')
        r.input('Escape')
        self.assertEqual(r.line, 'aaa')
        r.input('"r"')
        self.assertEqual(r.line, 'aaa')
        r.input('"z"')
        self.assertEqual(r.line, 'zaa')

    def test_replace_lower_escape_undo(self):
        r = ViModeTest()
        r._set_line('aa bb cc')
        r.input('Escape')
        r.input('"0cwdd"')
        r.input('Escape')
        self.assertEqual(r.line, 'dd bb cc')
        r.input('"wr"')
        r.input('Escape')
        self.assertEqual(r.line, 'dd bb cc')
        r.input('"."')
        self.assertEqual(r.line, 'dd dd cc')
        r.input('"u"')
        self.assertEqual(r.line, 'dd bb cc')
        r.input('"u"')
        self.assertEqual(r.line, 'aa bb cc')

    def test_replace_dot(self):
        r = ViModeTest()
        r._set_line('ab')
        r.input('Escape')
        r.input('"0rzl"')
        self.assertEqual(r.line, 'zb')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"r."')
        self.assertEqual(r.line, 'z.')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_replace_upper(self):
        r = ViModeTest()
        r._set_line('aaa bbb')
        r.input('Escape')
        r.input('"0wR"')
        self.assertEqual(r.line, 'aaa bbb')
        self.assertEqual(r.line_cursor, 4)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"z"')
        self.assertEqual(r.line, 'aaa zbb')
        self.assertEqual(r.line_cursor, 5)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"zzz"')
        self.assertEqual(r.line, 'aaa zzzz')
        self.assertEqual(r.line_cursor, 8)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        self.assertEqual(r.line_cursor, 7)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_replace_upper_dot(self):
        r = ViModeTest()
        r._set_line('aaa bbb ccc ddd')
        r.input('Escape')
        r.input('"02Rz"')
        r.input('Escape')
        self.assertEqual(r.line, 'zza bbb ccc ddd')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"w."')
        self.assertEqual(r.line, 'zza zzb ccc ddd')
        self.assertEqual(r.line_cursor, 5)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"w6."')
        self.assertEqual(r.line, 'zza zzb zzzzzzd')
        self.assertEqual(r.line_cursor, 13)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_replace_upper_undo(self):
        r = ViModeTest()
        r._set_line('aaa bbb ccc')
        r.input('Escape')
        r.input('"0Rzz"')
        r.input('Escape')
        self.assertEqual(r.line, 'zza bbb ccc')
        r.input('"w3."')
        self.assertEqual(r.line, 'zza zzzzzzc')
        r.input('"u"')
        self.assertEqual(r.line, 'zza bbb ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa bbb ccc')

    def test_replace_backspace_and_dot(self):
        r = ViModeTest()
        r._set_line('aa bb')
        r.input('Escape')
        r.input('"0wRc"')
        self.assertEqual(r.line, 'aa cb')
        self.assertEqual(r.line_cursor, 4)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"c"')
        self.assertEqual(r.line, 'aa cc')
        self.assertEqual(r.line_cursor, 5)
        r.input('"c"')
        self.assertEqual(r.line, 'aa ccc')
        self.assertEqual(r.line_cursor, 6)
        r.input('BackSpace')
        self.assertEqual(r.line, 'aa cc')
        self.assertEqual(r.line_cursor, 5)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('BackSpace')
        self.assertEqual(r.line, 'aa cb')
        self.assertEqual(r.line_cursor, 4)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('BackSpace')
        self.assertEqual(r.line, 'aa bb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('BackSpace')
        self.assertEqual(r.line, 'aa bb')
        self.assertEqual(r.line_cursor, 2)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('"d"')
        self.assertEqual(r.line, 'aadbb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(r.vi_is_insert_mode)
        r.input('Escape')
        self.assertEqual(r.line, 'aadbb')
        self.assertEqual(r.line_cursor, 2)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"u"')
        self.assertEqual(r.line, 'aa bb')
        self.assertEqual(r.line_cursor, 3)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"hh"')
        r.input('"."')
        self.assertEqual(r.line, 'da bb')
        self.assertTrue(not r.vi_is_insert_mode)

    def test_yank_line(self):
        r = ViModeTest()
        r._set_line('aa bb')
        r.input('Escape')
        r.input('"0wY"')
        self.assertEqual(r.line, 'aa bb')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"P"')
        self.assertEqual(r.line, 'aa aa bbbb')
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"u"')
        self.assertEqual(r.line, 'aa bb')
        self.assertTrue(not r.vi_is_insert_mode)

    def test_column(self):
        r = ViModeTest()
        r._set_line('aaa bbb')
        r.input('Escape')
        r.input('"099|"')
        self.assertEqual(r.line, 'aaa bbb')
        self.assertEqual(r.line_cursor, 6)
        r.input('"4|"')
        self.assertEqual(r.line, 'aaa bbb')
        self.assertEqual(r.line_cursor, 3)
        r.input('"d1|"')
        self.assertEqual(r.line, ' bbb')
        self.assertEqual(r.line_cursor, 0)
        r.input('"u"')
        self.assertEqual(r.line, 'aaa bbb')
        self.assertEqual(r.line_cursor, 3)
        r.input('"d7|"')
        self.assertEqual(r.line, 'aaab')
        self.assertEqual(r.line_cursor, 3)

    def test_change_case(self):
        r = ViModeTest()
        r._set_line('aaa B7B ccc')
        r.input('Escape')
        r.input('"0~"')
        self.assertEqual(r.line, 'Aaa B7B ccc')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"h."')
        self.assertEqual(r.line, 'aaa B7B ccc')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"9~"')
        self.assertEqual(r.line, 'aAA b7b CCc')
        self.assertEqual(r.line_cursor, 10)
        self.assertTrue(not r.vi_is_insert_mode)
        r.input('"u"')
        self.assertEqual(r.line, 'aaa B7B ccc')
        self.assertEqual(r.line_cursor, 1)
        self.assertTrue(not r.vi_is_insert_mode)

    def test_redo(self):
        r = ViModeTest()
        r._set_line('')
        r.input('Escape')
        r.input('"Saaa"')
        self.assertEqual(r.line, 'aaa')
        r.input('Escape')
        r.input('"Sbbb"')
        self.assertEqual(r.line, 'bbb')
        r.input('Escape')
        r.input('"Sccc"')
        r.input('Escape')
        self.assertEqual(r.line, 'ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'bbb')
        r.input('Control-r')
        self.assertEqual(r.line, 'ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'bbb')
        r.input('"u"')
        self.assertEqual(r.line, 'aaa')
        r.input('"u"')
        self.assertEqual(r.line, '')
        r.input('"u"')
        self.assertEqual(r.line, '')
        r.input('Control-r')
        self.assertEqual(r.line, 'aaa')
        r.input('Control-r')
        self.assertEqual(r.line, 'bbb')
        r.input('Control-r')
        self.assertEqual(r.line, 'ccc')
        r.input('Control-r')
        self.assertEqual(r.line, 'ccc')
        r.input('"u"')
        self.assertEqual(r.line, 'bbb')
        r.input('"Szzz"')
        r.input('Escape')
        self.assertEqual(r.line, 'zzz')
        r.input('"u"')
        self.assertEqual(r.line, 'bbb')
        r.input('Control-r')
        self.assertEqual(r.line, 'zzz')
        r.input('"U"')
        self.assertEqual(r.line, '')
        r.input('Control-r')
        self.assertEqual(r.line, 'aaa')
        r.input('Control-r')
        self.assertEqual(r.line, 'bbb')
        r.input('Control-r')
        self.assertEqual(r.line, 'zzz')
        r.input('Control-r')
        self.assertEqual(r.line, 'zzz')

# ----------------------------------------------------------------------
# utility functions


# ----------------------------------------------------------------------

if __name__ == '__main__':
    Tester()

    tested = sorted(ViModeTest.tested_commands.keys())
    print(" Tested functions ".center(60, "-"))
    print("\n".join(tested))
    print()

    all_funcs = dict([(x.__name__, x)
                      for x in list(ViModeTest().key_dispatch.values())])
    all_funcs = list(all_funcs.keys())
    not_tested = sorted([x for x in all_funcs if x not in tested])
    print(" Not tested functions ".center(60, "-"))
    print("\n".join(not_tested))
