local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local assert_alive = helpers.assert_alive
local clear, feed = helpers.clear, helpers.feed
local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq
local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect
local funcs = helpers.funcs
local curbufmeths = helpers.curbufmeths
local command = helpers.command
local meths = helpers.meths
local poke_eventloop = helpers.poke_eventloop

describe('completion', function()
  local screen

  before_each(function()
    clear()
    screen = Screen.new(60, 8)
    screen:attach()
    screen:set_default_attr_ids({
      [0] = {bold=true, foreground=Screen.colors.Blue},
      [1] = {background = Screen.colors.LightMagenta},
      [2] = {background = Screen.colors.Grey},
      [3] = {bold = true},
      [4] = {bold = true, foreground = Screen.colors.SeaGreen},
      [5] = {foreground = Screen.colors.Red},
      [6] = {background = Screen.colors.Black},
      [7] = {foreground = Screen.colors.White, background = Screen.colors.Red},
      [8] = {reverse = true},
      [9] = {bold = true, reverse = true},
      [10] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow},
    })
  end)

  describe('v:completed_item', function()
    it('is empty dict until completion', function()
      eq({}, eval('v:completed_item'))
    end)
    it('is empty dict if the candidate is not inserted', function()
      feed('ifoo<ESC>o<C-x><C-n>')
      screen:expect([[
        foo                                                         |
        foo^                                                         |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) The only match}           |
      ]])
      feed('<C-e>')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<ESC>')
      eq({}, eval('v:completed_item'))
    end)
    it('returns expected dict in normal completion', function()
      feed('ifoo<ESC>o<C-x><C-n>')
      eq('foo', eval('getline(2)'))
      eq({word = 'foo', abbr = '', menu = '',
          info = '', kind = '', user_data = ''},
        eval('v:completed_item'))
    end)
    it('is readonly', function()
      screen:try_resize(80, 8)
      feed('ifoo<ESC>o<C-x><C-n><ESC>')
      feed_command('let v:completed_item.word = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')

      feed_command('let v:completed_item.abbr = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')

      feed_command('let v:completed_item.menu = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')

      feed_command('let v:completed_item.info = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')

      feed_command('let v:completed_item.kind = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')

      feed_command('let v:completed_item.user_data = "bar"')
      neq(nil, string.find(eval('v:errmsg'), '^E46: '))
      feed_command('let v:errmsg = ""')
    end)
    it('returns expected dict in omni completion', function()
      source([[
      function! TestOmni(findstart, base) abort
        return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar',
        \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'},
        \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu',
        \  'info': 'info', 'kind': 'kind'}]
      endfunction
      setlocal omnifunc=TestOmni
      ]])
      feed('i<C-x><C-o>')
      eq('foo', eval('getline(1)'))
      screen:expect([[
        foo^                                                         |
        {2:bar  foobaz baz  }{0:                                           }|
        {1:abbr kind   menu }{0:                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Omni completion (^O^N^P) }{4:match 1 of 2}                    |
      ]])
      eq({word = 'foo', abbr = 'bar', menu = 'baz',
          info = 'foobar', kind = 'foobaz', user_data = ''},
        eval('v:completed_item'))
    end)
  end)

  describe('completeopt', function()
    before_each(function()
      source([[
      function! TestComplete() abort
        call complete(1, ['foo'])
        return ''
      endfunction
      ]])
    end)

    it('inserts the first candidate if default', function()
      feed_command('set completeopt+=menuone')
      feed('ifoo<ESC>o')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<C-x>')
      -- the ^X prompt, only test this once
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)}                    |
      ]])
      feed('<C-n>')
      screen:expect([[
        foo                                                         |
        foo^                                                         |
        {2:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) The only match}           |
      ]])
      feed('bar<ESC>')
      eq('foobar', eval('getline(2)'))
      feed('o<C-r>=TestComplete()<CR>')
      screen:expect([[
        foo                                                         |
        foobar                                                      |
        foo^                                                         |
        {2:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      eq('foo', eval('getline(3)'))
    end)
    it('selects the first candidate if noinsert', function()
      feed_command('set completeopt+=menuone,noinsert')
      feed('ifoo<ESC>o<C-x><C-n>')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {2:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) The only match}           |
      ]])
      feed('<C-y>')
      screen:expect([[
        foo                                                         |
        foo^                                                         |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<ESC>')
      eq('foo', eval('getline(2)'))
      feed('o<C-r>=TestComplete()<CR>')
      screen:expect([[
        foo                                                         |
        foo                                                         |
        ^                                                            |
        {2:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<C-y><ESC>')
      eq('foo', eval('getline(3)'))
    end)
    it('does not insert the first candidate if noselect', function()
      feed_command('set completeopt+=menuone,noselect')
      feed('ifoo<ESC>o<C-x><C-n>')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {1:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) }{5:Back at original}         |
      ]])
      feed('b')
      screen:expect([[
        foo                                                         |
        b^                                                           |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) }{5:Back at original}         |
      ]])
      feed('ar<ESC>')
      eq('bar', eval('getline(2)'))
      feed('o<C-r>=TestComplete()<CR>')
      screen:expect([[
        foo                                                         |
        bar                                                         |
        ^                                                            |
        {1:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('bar<ESC>')
      eq('bar', eval('getline(3)'))
    end)
    it('does not select/insert the first candidate if noselect and noinsert', function()
      feed_command('set completeopt+=menuone,noselect,noinsert')
      feed('ifoo<ESC>o<C-x><C-n>')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {1:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- Keyword Local completion (^N^P) }{5:Back at original}         |
      ]])
      feed('<ESC>')
      screen:expect([[
        foo                                                         |
        ^                                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
                                                                    |
      ]])
      eq('', eval('getline(2)'))
      feed('o<C-r>=TestComplete()<CR>')
      screen:expect([[
        foo                                                         |
                                                                    |
        ^                                                            |
        {1:foo            }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<ESC>')
      screen:expect([[
        foo                                                         |
                                                                    |
        ^                                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
                                                                    |
      ]])
      eq('', eval('getline(3)'))
    end)
    it('does not change modified state if noinsert', function()
      feed_command('set completeopt+=menuone,noinsert')
      feed_command('setlocal nomodified')
      feed('i<C-r>=TestComplete()<CR><ESC>')
      eq(0, eval('&l:modified'))
    end)
    it('does not change modified state if noselect', function()
      feed_command('set completeopt+=menuone,noselect')
      feed_command('setlocal nomodified')
      feed('i<C-r>=TestComplete()<CR><ESC>')
      eq(0, eval('&l:modified'))
    end)
  end)

  describe('completeopt+=noinsert does not add blank undo items', function()
    before_each(function()
      source([[
      function! TestComplete() abort
        call complete(1, ['foo', 'bar'])
        return ''
      endfunction
      ]])
      feed_command('set completeopt+=noselect,noinsert')
      feed_command('inoremap <right> <c-r>=TestComplete()<cr>')
    end)

    local tests = {
      ['<up>, <down>, <cr>'] = {'<down><cr>', '<up><cr>'},
      ['<c-n>, <c-p>, <c-y>'] = {'<c-n><c-y>', '<c-p><c-y>'},
    }

    for name, seq in pairs(tests) do
      it('using ' .. name, function()
        feed('iaaa<esc>')
        feed('A<right>' .. seq[1] .. '<esc>')
        feed('A<right><esc>A<right><esc>')
        feed('A<cr>bbb<esc>')
        feed('A<right>' .. seq[2] .. '<esc>')
        feed('A<right><esc>A<right><esc>')
        feed('A<cr>ccc<esc>')
        feed('A<right>' .. seq[1] .. '<esc>')
        feed('A<right><esc>A<right><esc>')

        local expected = {
          {'foo', 'bar', 'foo'},
          {'foo', 'bar', 'ccc'},
          {'foo', 'bar'},
          {'foo', 'bbb'},
          {'foo'},
          {'aaa'},
          {''},
        }

        for i = 1, #expected do
          if i > 1 then
            feed('u')
          end
          eq(expected[i], eval('getline(1, "$")'))
        end

        for i = #expected, 1, -1 do
          if i < #expected then
            feed('<c-r>')
          end
          eq(expected[i], eval('getline(1, "$")'))
        end
      end)
    end
  end)

  describe("refresh:always", function()
    before_each(function()
      source([[
        function! TestCompletion(findstart, base) abort
          if a:findstart
            let line = getline('.')
            let start = col('.') - 1
            while start > 0 && line[start - 1] =~ '\a'
              let start -= 1
            endwhile
            return start
          else
            let ret = []
            for m in split("January February March April May June July August September October November December")
              if m =~ a:base  " match by regex
                call add(ret, m)
              endif
            endfor
            return {'words':ret, 'refresh':'always'}
          endif
        endfunction

        set completeopt=menuone,noselect
        set completefunc=TestCompletion
      ]])
    end )

    it('completes on each input char', function ()
      feed('i<C-x><C-u>')
      screen:expect([[
        ^                                                            |
        {1:January        }{6: }{0:                                            }|
        {1:February       }{6: }{0:                                            }|
        {1:March          }{6: }{0:                                            }|
        {1:April          }{2: }{0:                                            }|
        {1:May            }{2: }{0:                                            }|
        {1:June           }{2: }{0:                                            }|
        {3:-- User defined completion (^U^N^P) }{5:Back at original}        |
      ]])
      feed('u')
      screen:expect([[
        u^                                                           |
        {1:January        }{0:                                             }|
        {1:February       }{0:                                             }|
        {1:June           }{0:                                             }|
        {1:July           }{0:                                             }|
        {1:August         }{0:                                             }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{5:Back at original}        |
      ]])
      feed('g')
      screen:expect([[
        ug^                                                          |
        {1:August         }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{5:Back at original}        |
      ]])
      feed('<Down>')
      screen:expect([[
        ug^                                                          |
        {2:August         }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) The only match}          |
      ]])
      feed('<C-y>')
      screen:expect([[
        August^                                                      |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
      expect('August')
    end)

    it("repeats correctly after backspace #2674", function ()
      feed('o<C-x><C-u>Ja')
      screen:expect([[
                                                                    |
        Ja^                                                          |
        {1:January        }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{5:Back at original}        |
      ]])
      feed('<BS>')
      screen:expect([[
                                                                    |
        J^                                                           |
        {1:January        }{0:                                             }|
        {1:June           }{0:                                             }|
        {1:July           }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{5:Back at original}        |
      ]])
      feed('<C-n>')
      screen:expect([[
                                                                    |
        January^                                                     |
        {2:January        }{0:                                             }|
        {1:June           }{0:                                             }|
        {1:July           }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{4:match 1 of 3}            |
      ]])
      feed('<C-n>')
      screen:expect([[
                                                                    |
        June^                                                        |
        {1:January        }{0:                                             }|
        {2:June           }{0:                                             }|
        {1:July           }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- User defined completion (^U^N^P) }{4:match 2 of 3}            |
      ]])
      feed('<Esc>')
      screen:expect([[
                                                                    |
        Jun^e                                                        |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
                                                                    |
      ]])
      feed('.')
      screen:expect([[
                                                                    |
        June                                                        |
        Jun^e                                                        |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
                                                                    |
      ]])
      expect([[

        June
        June]])
    end)
  end)

  describe('with a lot of items', function()
    before_each(function()
      source([[
      function! TestComplete() abort
        call complete(1, map(range(0,100), "string(v:val)"))
        return ''
      endfunction
      ]])
      feed_command("set completeopt=menuone,noselect")
    end)

    it("works", function()
      feed('i<C-r>=TestComplete()<CR>')
      screen:expect([[
        ^                                                            |
        {1:0              }{6: }{0:                                            }|
        {1:1              }{2: }{0:                                            }|
        {1:2              }{2: }{0:                                            }|
        {1:3              }{2: }{0:                                            }|
        {1:4              }{2: }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('7')
      screen:expect([[
        7^                                                           |
        {1:7              }{6: }{0:                                            }|
        {1:70             }{6: }{0:                                            }|
        {1:71             }{6: }{0:                                            }|
        {1:72             }{2: }{0:                                            }|
        {1:73             }{2: }{0:                                            }|
        {1:74             }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<c-n>')
      screen:expect([[
        7^                                                           |
        {2:7              }{6: }{0:                                            }|
        {1:70             }{6: }{0:                                            }|
        {1:71             }{6: }{0:                                            }|
        {1:72             }{2: }{0:                                            }|
        {1:73             }{2: }{0:                                            }|
        {1:74             }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<c-n>')
      screen:expect([[
        70^                                                          |
        {1:7              }{6: }{0:                                            }|
        {2:70             }{6: }{0:                                            }|
        {1:71             }{6: }{0:                                            }|
        {1:72             }{2: }{0:                                            }|
        {1:73             }{2: }{0:                                            }|
        {1:74             }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
    end)

    it('can be navigated with <PageDown>, <PageUp>', function()
      feed('i<C-r>=TestComplete()<CR>')
      screen:expect([[
        ^                                                            |
        {1:0              }{6: }{0:                                            }|
        {1:1              }{2: }{0:                                            }|
        {1:2              }{2: }{0:                                            }|
        {1:3              }{2: }{0:                                            }|
        {1:4              }{2: }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageDown>')
      screen:expect([[
        ^                                                            |
        {1:0              }{6: }{0:                                            }|
        {1:1              }{2: }{0:                                            }|
        {1:2              }{2: }{0:                                            }|
        {2:3               }{0:                                            }|
        {1:4              }{2: }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageDown>')
      screen:expect([[
        ^                                                            |
        {1:5              }{6: }{0:                                            }|
        {1:6              }{2: }{0:                                            }|
        {2:7               }{0:                                            }|
        {1:8              }{2: }{0:                                            }|
        {1:9              }{2: }{0:                                            }|
        {1:10             }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<Down>')
      screen:expect([[
        ^                                                            |
        {1:5              }{6: }{0:                                            }|
        {1:6              }{2: }{0:                                            }|
        {1:7              }{2: }{0:                                            }|
        {2:8               }{0:                                            }|
        {1:9              }{2: }{0:                                            }|
        {1:10             }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageUp>')
      screen:expect([[
        ^                                                            |
        {1:2              }{6: }{0:                                            }|
        {1:3              }{2: }{0:                                            }|
        {2:4               }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {1:6              }{2: }{0:                                            }|
        {1:7              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageUp>') -- stop on first item
      screen:expect([[
        ^                                                            |
        {2:0              }{6: }{0:                                            }|
        {1:1              }{2: }{0:                                            }|
        {1:2              }{2: }{0:                                            }|
        {1:3              }{2: }{0:                                            }|
        {1:4              }{2: }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageUp>') -- when on first item, unselect
      screen:expect([[
        ^                                                            |
        {1:0              }{6: }{0:                                            }|
        {1:1              }{2: }{0:                                            }|
        {1:2              }{2: }{0:                                            }|
        {1:3              }{2: }{0:                                            }|
        {1:4              }{2: }{0:                                            }|
        {1:5              }{2: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageUp>') -- when unselected, select last item
      screen:expect([[
        ^                                                            |
        {1:95             }{2: }{0:                                            }|
        {1:96             }{2: }{0:                                            }|
        {1:97             }{2: }{0:                                            }|
        {1:98             }{2: }{0:                                            }|
        {1:99             }{2: }{0:                                            }|
        {2:100            }{6: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<PageUp>')
      screen:expect([[
        ^                                                            |
        {1:94             }{2: }{0:                                            }|
        {1:95             }{2: }{0:                                            }|
        {2:96              }{0:                                            }|
        {1:97             }{2: }{0:                                            }|
        {1:98             }{2: }{0:                                            }|
        {1:99             }{6: }{0:                                            }|
        {3:-- INSERT --}                                                |
      ]])
      feed('<cr>')
      screen:expect([[
        96^                                                          |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
    end)
  end)

  it("does not indent until an item is selected #8345", function ()
    -- Indents on "ind", unindents on "unind".
    source([[
      function! TestIndent()
        let line = getline(v:lnum)
        if (line =~ '^\s*ind')
          return indent(v:lnum-1) + shiftwidth()
        elseif (line =~ '^\s*unind')
          return indent(v:lnum-1) - shiftwidth()
        else
          return indent(v:lnum-1)
        endif
      endfunction
      set indentexpr=TestIndent()
      set indentkeys=o,O,!^F,=ind,=unind
      set completeopt+=menuone
    ]])

    -- Give some words to complete.
    feed("iinc uninc indent unindent<CR>")

    -- Does not indent when "ind" is typed.
    feed("in<C-X><C-N>")
    -- Completion list is generated incorrectly if we send everything at once
    -- via nvim_input().  So poke_eventloop() before sending <BS>. #8480
    poke_eventloop()
    feed("<BS>d")

    screen:expect([[
      inc uninc indent unindent                                   |
      ind^                                                         |
      {2:indent         }{0:                                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2}             |
    ]])

    -- Indents when the item is selected
    feed("<C-Y>")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent^                                              |
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- INSERT --}                                                |
    ]])
    -- Indents when completion is exited using ESC.
    feed("<CR>in<C-N><BS>d<Esc>")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent                                              |
                      in^d                                         |
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
                                                                  |
    ]])
    -- Works for unindenting too.
    feed("ounin<C-X><C-N>")
    helpers.poke_eventloop()
    feed("<BS>d")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent                                              |
                      ind                                         |
                      unind^                                       |
      {0:~              }{2: unindent       }{0:                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2}             |
    ]])
    -- Works when going back and forth.
    feed("<BS>c")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent                                              |
                      ind                                         |
                      uninc^                                       |
      {0:~              }{2: uninc          }{0:                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2}             |
    ]])
    feed("<BS>d")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent                                              |
                      ind                                         |
                      unind^                                       |
      {0:~              }{2: unindent       }{0:                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2}             |
    ]])
    feed("<C-N><C-N><C-Y><Esc>")
    screen:expect([[
      inc uninc indent unindent                                   |
              indent                                              |
                      ind                                         |
              uninden^t                                            |
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
                                                                  |
    ]])
  end)

  it('disables folding during completion', function ()
    feed_command("set foldmethod=indent")
    feed('i<Tab>foo<CR><Tab>bar<Esc>gg')
    screen:expect([[
              ^foo                                                 |
              bar                                                 |
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
                                                                  |
    ]])
    feed('A<C-x><C-l>')
    screen:expect([[
              foo^                                                 |
              bar                                                 |
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Whole line completion (^L^N^P) }{7:Pattern not found}         |
    ]])
    eq(-1, eval('foldclosed(1)'))
  end)

  it('popupmenu is not interrupted by events', function ()
    feed_command("set complete=.")

    feed('ifoobar fooegg<cr>f<c-p>')
    screen:expect([[
      foobar fooegg                                               |
      fooegg^                                                      |
      {1:foobar         }{0:                                             }|
      {2:fooegg         }{0:                                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 1 of 2}                   |
    ]])

    assert_alive()
    -- popupmenu still visible
    screen:expect{grid=[[
      foobar fooegg                                               |
      fooegg^                                                      |
      {1:foobar         }{0:                                             }|
      {2:fooegg         }{0:                                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 1 of 2}                   |
    ]], unchanged=true}

    feed('<c-p>')
    -- Didn't restart completion: old matches still used
    screen:expect([[
      foobar fooegg                                               |
      foobar^                                                      |
      {2:foobar         }{0:                                             }|
      {1:fooegg         }{0:                                             }|
      {0:~                                                           }|
      {0:~                                                           }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 2 of 2}                   |
    ]])
  end)

  describe('lua completion', function()
    it('expands when there is only one match', function()
      feed(':lua CURRENT_TESTING_VAR = 1<CR>')
      feed(':lua CURRENT_TESTING_<TAB>')
      screen:expect{grid=[[
                                                                    |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        :lua CURRENT_TESTING_VAR^                                    |
      ]]}
    end)

    it('expands when there is only one match', function()
      feed(':lua CURRENT_TESTING_FOO = 1<CR>')
      feed(':lua CURRENT_TESTING_BAR = 1<CR>')
      feed(':lua CURRENT_TESTING_<TAB>')
      screen:expect{ grid = [[
                                                                    |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {10:CURRENT_TESTING_BAR}{9:  CURRENT_TESTING_FOO                    }|
        :lua CURRENT_TESTING_BAR^                                    |
      ]], unchanged = true }
    end)

    it('provides completion from `getcompletion()`', function()
      eq({'vim'}, funcs.getcompletion('vi', 'lua'))
      eq({'api'}, funcs.getcompletion('vim.ap', 'lua'))
      eq({'tbl_filter'}, funcs.getcompletion('vim.tbl_fil', 'lua'))
      eq({'vim'}, funcs.getcompletion('print(vi', 'lua'))
    end)
  end)

  describe('from the commandline window', function()
    it('is cleared after CTRL-C', function ()
      feed('q:')
      feed('ifoo faa fee f')
      screen:expect([[
                                                                    |
        {8:[No Name]                                                   }|
        {0::}foo faa fee f^                                              |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {9:[Command Line]                                              }|
        {3:-- INSERT --}                                                |
      ]] )
      feed('<c-x><c-n>')
      screen:expect([[
                                                                    |
        {8:[No Name]                                                   }|
        {0::}foo faa fee foo^                                            |
        {0:~           }{2: foo            }{0:                                }|
        {0:~           }{1: faa            }{0:                                }|
        {0:~           }{1: fee            }{0:                                }|
        {9:[Command Line]                                              }|
        {3:-- Keyword Local completion (^N^P) }{4:match 1 of 3}             |
      ]])
      feed('<c-c>')
      screen:expect([[
                                                                    |
        {8:[No Name]                                                   }|
        {0::}foo faa fee foo                                            |
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {9:[Command Line]                                              }|
        :foo faa fee foo^                                            |
      ]])
    end)
  end)

  describe('with numeric items', function()
    before_each(function()
      source([[
        function! TestComplete() abort
          call complete(1, g:_complist)
          return ''
        endfunction
      ]])
      meths.set_option('completeopt', 'menuone,noselect')
      meths.set_var('_complist', {{
        word=0,
        abbr=1,
        menu=2,
        kind=3,
        info=4,
        icase=5,
        dup=6,
        empty=7,
      }})
    end)

    it('shows correct variant as word', function()
      feed('i<C-r>=TestComplete()<CR>')
      screen:expect([[
        ^                                                            |
        {1:1 3 2          }{0:                                             }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {0:~                                                           }|
        {3:-- INSERT --}                                                |
      ]])
    end)
  end)

  it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function()
    feed_command('set ignorecase infercase')
    feed_command('edit BACKERS.md')
    feed('oX<C-X><C-N>')
    screen:expect([[
      # Bountysource Backers                                      |
      Xnull^                                                       |
      {2:Xnull          }{6: }                                            |
      {1:Xoxomoon       }{6: }ryone who backed our [Bountysource fundraise|
      {1:Xu             }{6: }ountysource.com/teams/neovim/fundraiser)!   |
      {1:Xpayn          }{2: }                                            |
      {1:Xinity         }{2: }d URL in BACKERS.md.                        |
      {3:-- Keyword Local completion (^N^P) }{4:match 1 of 7}             |
    ]])
  end)

  it('TextChangedP autocommand', function()
    curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'})
    source([[
      set complete=. completeopt=menuone
      let g:foo = []
      autocmd! TextChanged * :call add(g:foo, "N")
      autocmd! TextChangedI * :call add(g:foo, "I")
      autocmd! TextChangedP * :call add(g:foo, "P")
      call cursor(3, 1)
    ]])

    command('let g:foo = []')
    feed('o')
    poke_eventloop()
    feed('<esc>')
    eq({'I'}, eval('g:foo'))

    command('let g:foo = []')
    feed('S')
    poke_eventloop()
    feed('f')
    poke_eventloop()
    eq({'I', 'I'}, eval('g:foo'))
    feed('<esc>')

    command('let g:foo = []')
    feed('S')
    poke_eventloop()
    feed('f')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    eq({'I', 'I', 'P'}, eval('g:foo'))
    feed('<esc>')

    command('let g:foo = []')
    feed('S')
    poke_eventloop()
    feed('f')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    eq({'I', 'I', 'P', 'P'}, eval('g:foo'))
    feed('<esc>')

    command('let g:foo = []')
    feed('S')
    poke_eventloop()
    feed('f')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo'))
    feed('<esc>')

    command('let g:foo = []')
    feed('S')
    poke_eventloop()
    feed('f')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    poke_eventloop()
    feed('<C-N>')
    eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo'))
    feed('<esc>')

    eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")'))

    source([[
      au! TextChanged
      au! TextChangedI
      au! TextChangedP
      set complete&vim completeopt&vim
    ]])
  end)

  it('CompleteChanged autocommand', function()
    curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar', ''})
    source([[
      set complete=. completeopt=noinsert,noselect,menuone
      function! OnPumChange()
        let g:event = copy(v:event)
        let g:item = get(v:event, 'completed_item', {})
        let g:word = get(g:item, 'word', v:null)
      endfunction
      autocmd! CompleteChanged * :call OnPumChange()
      call cursor(4, 1)
    ]])

    feed('Sf<C-N>')
    screen:expect([[
      foo                                                         |
      bar                                                         |
      foobar                                                      |
      f^                                                           |
      {1:foo            }{0:                                             }|
      {1:foobar         }{0:                                             }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{5:Back at original}               |
    ]])
    eq({completed_item = {}, width = 15,
      height = 2, size = 2,
      col = 0, row = 4, scrollbar = false},
      eval('g:event'))
    feed('<C-N>')
    screen:expect([[
      foo                                                         |
      bar                                                         |
      foobar                                                      |
      foo^                                                         |
      {2:foo            }{0:                                             }|
      {1:foobar         }{0:                                             }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 1 of 2}                   |
    ]])
    eq('foo', eval('g:word'))
    feed('<C-N>')
    screen:expect([[
      foo                                                         |
      bar                                                         |
      foobar                                                      |
      foobar^                                                      |
      {1:foo            }{0:                                             }|
      {2:foobar         }{0:                                             }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 2 of 2}                   |
    ]])
    eq('foobar', eval('g:word'))
    feed('<up>')
    screen:expect([[
      foo                                                         |
      bar                                                         |
      foobar                                                      |
      foobar^                                                      |
      {2:foo            }{0:                                             }|
      {1:foobar         }{0:                                             }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 1 of 2}                   |
    ]])
    eq('foo', eval('g:word'))
    feed('<down>')
    screen:expect([[
      foo                                                         |
      bar                                                         |
      foobar                                                      |
      foobar^                                                      |
      {1:foo            }{0:                                             }|
      {2:foobar         }{0:                                             }|
      {0:~                                                           }|
      {3:-- Keyword completion (^N^P) }{4:match 2 of 2}                   |
    ]])
    eq('foobar', eval('g:word'))
    feed('<esc>')
  end)

  it('is stopped by :stopinsert from timer #12976', function()
    screen:try_resize(32,14)
    command([[call setline(1, ['hello', 'hullo', 'heeee', ''])]])
    feed('Gah<c-x><c-n>')
    screen:expect([[
      hello                           |
      hullo                           |
      heeee                           |
      hello^                           |
      {2:hello          }{0:                 }|
      {1:hullo          }{0:                 }|
      {1:heeee          }{0:                 }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {3:-- }{4:match 1 of 3}                 |
    ]])
    command([[call timer_start(100, { -> execute('stopinsert') })]])
    helpers.sleep(200)
    feed('k')  -- cursor should move up in Normal mode
    screen:expect([[
      hello                           |
      hullo                           |
      heee^e                           |
      hello                           |
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
      {0:~                               }|
                                      |
    ]])
  end)

  it('does not crash if text is changed by first call to complete function #17489', function()
    source([[
      func Complete(findstart, base) abort
        if a:findstart
          let col = col('.')
          call complete_add('#')
          return col - 1
        else
          return []
        endif
      endfunc

      set completeopt=longest
      set completefunc=Complete
    ]])
    feed('ifoo#<C-X><C-U>')
    assert_alive()
  end)
end)
