[
  {
    "path": ".githooks/pre-commit",
    "content": "#!/bin/sh\n\nDIR=\"$(dirname $(dirname $( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )))\"\n\ncd $DIR\nmake pre-commit\nfor FILE in `git diff --staged --name-only`; do\n    git add $FILE\ndone\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: Report a problem in nvim-cmp\nlabels: [bug]\nbody:\n  - type: checkboxes\n    id: faq-prerequisite\n    attributes:\n      label: FAQ\n      options:\n        - label: I have checked the [FAQ](https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt) and it didn't resolve my problem.\n          required: true\n\n  - type: checkboxes\n    id: announcement-prerequisite\n    attributes:\n      label: Announcement\n      options:\n        - label: I have checked [Breaking change announcement](https://github.com/hrsh7th/nvim-cmp/issues/231).\n          required: true\n\n  - type: textarea\n    attributes:\n      label: \"Minimal reproducible full config\"\n      description: |\n        You must provide a working config based on [this](https://github.com/hrsh7th/nvim-cmp/blob/main/utils/vimrc.vim). Not part of config.\n        1. Copy the base minimal config to the `~/cmp-repro.vim`\n        2. Edit `~/cmp-repro.vim` for reproducing the issue\n        3. Open `nvim -u ~/cmp-repro.vim`\n        4. Check reproduction step\n      value: |\n        ```vim\n        \n        ```\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Description\"\n      description: \"Describe in detail what happens\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Steps to reproduce\"\n      description: \"Full reproduction steps. Include a sample file if your issue relates to a specific filetype.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Expected behavior\"\n      description: \"A description of the behavior you expected.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Actual behavior\"\n      description: \"A description of the actual behavior.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Additional context\"\n      description: \"Any other relevant information\"\n"
  },
  {
    "path": ".github/workflows/format.yaml",
    "content": "name: format\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - '**.lua'\n\njobs:\n  postprocessing:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Format with Stylua\n        uses: JohnnyMorganz/stylua-action@v2\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          version: v0.16.1\n          args: ./lua\n\n      - uses: stefanzweifel/git-auto-commit-action@v4\n        with:\n          commit_message: \"Format with stylua\"\n"
  },
  {
    "path": ".github/workflows/integration.yaml",
    "content": "name: integration\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  integration:\n    runs-on: ubuntu-latest\n    steps:\n\n    - name: Checkout\n      uses: actions/checkout@v2\n\n    - name: Setup neovim\n      uses: rhysd/action-setup-vim@v1\n      with:\n        version: nightly\n        neovim: true\n\n    - name: Setup lua\n      uses: leafo/gh-actions-lua@v10\n      with:\n        luaVersion: \"luajit-openresty\"\n\n    - name: Setup luarocks\n      uses: leafo/gh-actions-luarocks@v6\n\n    - name: Setup tools\n      shell: bash\n      run: |\n        luarocks install luacheck\n        luarocks install vusted\n\n    - name: Run tests\n      shell: bash\n      run: make integration\n"
  },
  {
    "path": ".github/workflows/release-please.yaml",
    "content": "---\npermissions:\n  contents: write\n  pull-requests: write\n\nname: Release Please\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n\njobs:\n  release:\n    name: release\n    runs-on: ubuntu-latest\n    steps:\n      - uses: googleapis/release-please-action@v4\n        with:\n          release-type: simple\n"
  },
  {
    "path": ".gitignore",
    "content": "doc/tags\nutils/stylua\n.DS_Store\n\n"
  },
  {
    "path": ".luacheckrc",
    "content": "globals = { 'vim', 'describe', 'it', 'before_each', 'after_each', 'assert', 'async' }\nmax_line_length = false\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 hrsh7th\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: lint\nlint:\n\tluacheck ./lua\n\n.PHONY: test\ntest:\n\tvusted --output=gtest ./lua\n\n.PHONY: pre-commit\npre-commit:\n\tluacheck lua\n\tvusted lua\n\n.PHONY: integration\nintegration:\n\tluacheck lua\n\tvusted lua\n"
  },
  {
    "path": "README.md",
    "content": "# nvim-cmp\n\nA completion engine plugin for neovim written in Lua.\nCompletion sources are installed from external repositories and \"sourced\".\n\nhttps://github.com/hrsh7th/nvim-cmp/assets/22756295/afa70011-9121-4e42-aedd-0153b630eeab\n\nReadme!\n====================\n\n1. There is a GitHub issue that documents [breaking changes](https://github.com/hrsh7th/nvim-cmp/issues/231) for nvim-cmp. Subscribe to the issue to be notified of upcoming breaking changes.\n2. This is my hobby project. You can support me via GitHub sponsors.\n3. Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.\n4. The `cmp.mapping.preset.*` is pre-defined configuration that aims to mimic neovim's native like behavior. It can be changed without announcement. Please manage key-mapping by yourself.\n\nConcept\n====================\n\n- Full support for LSP completion related capabilities\n- Powerful customizability via Lua functions\n- Smart handling of key mappings\n- No flicker\n\n\nSetup\n====================\n\n### Recommended Configuration\n\nThis example configuration uses `vim-plug` as the plugin manager and `vim-vsnip` as a snippet plugin.\n\n```vim\ncall plug#begin(s:plug_dir)\nPlug 'neovim/nvim-lspconfig'\nPlug 'hrsh7th/cmp-nvim-lsp'\nPlug 'hrsh7th/cmp-buffer'\nPlug 'hrsh7th/cmp-path'\nPlug 'hrsh7th/cmp-cmdline'\nPlug 'hrsh7th/nvim-cmp'\n\n\" For vsnip users.\nPlug 'hrsh7th/cmp-vsnip'\nPlug 'hrsh7th/vim-vsnip'\n\n\" For luasnip users.\n\" Plug 'L3MON4D3/LuaSnip'\n\" Plug 'saadparwaiz1/cmp_luasnip'\n\n\" For mini.snippets users.\n\" Plug 'echasnovski/mini.snippets'\n\" Plug 'abeldekat/cmp-mini-snippets'\n\n\" For ultisnips users.\n\" Plug 'SirVer/ultisnips'\n\" Plug 'quangnguyen30192/cmp-nvim-ultisnips'\n\n\" For snippy users.\n\" Plug 'dcampos/nvim-snippy'\n\" Plug 'dcampos/cmp-snippy'\n\ncall plug#end()\n\nlua <<EOF\n  -- Set up nvim-cmp.\n  local cmp = require'cmp'\n\n  cmp.setup({\n    snippet = {\n      -- REQUIRED - you must specify a snippet engine\n      expand = function(args)\n        vim.fn[\"vsnip#anonymous\"](args.body) -- For `vsnip` users.\n        -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.\n        -- require('snippy').expand_snippet(args.body) -- For `snippy` users.\n        -- vim.fn[\"UltiSnips#Anon\"](args.body) -- For `ultisnips` users.\n        -- vim.snippet.expand(args.body) -- For native neovim snippets (Neovim v0.10+)\n\n        -- For `mini.snippets` users:\n        -- local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert\n        -- insert({ body = args.body }) -- Insert at cursor\n        -- cmp.resubscribe({ \"TextChangedI\", \"TextChangedP\" })\n        -- require(\"cmp.config\").set_onetime({ sources = {} })\n      end,\n    },\n    window = {\n      -- completion = cmp.config.window.bordered(),\n      -- documentation = cmp.config.window.bordered(),\n    },\n    mapping = cmp.mapping.preset.insert({\n      ['<C-b>'] = cmp.mapping.scroll_docs(-4),\n      ['<C-f>'] = cmp.mapping.scroll_docs(4),\n      ['<C-Space>'] = cmp.mapping.complete(),\n      ['<C-e>'] = cmp.mapping.abort(),\n      ['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.\n    }),\n    sources = cmp.config.sources({\n      { name = 'nvim_lsp' },\n      { name = 'vsnip' }, -- For vsnip users.\n      -- { name = 'luasnip' }, -- For luasnip users.\n      -- { name = 'ultisnips' }, -- For ultisnips users.\n      -- { name = 'snippy' }, -- For snippy users.\n    }, {\n      { name = 'buffer' },\n    })\n  })\n\n  -- To use git you need to install the plugin petertriho/cmp-git and uncomment lines below\n  -- Set configuration for specific filetype.\n  --[[ cmp.setup.filetype('gitcommit', {\n    sources = cmp.config.sources({\n      { name = 'git' },\n    }, {\n      { name = 'buffer' },\n    })\n })\n require(\"cmp_git\").setup() ]]--\n\n  -- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).\n  cmp.setup.cmdline({ '/', '?' }, {\n    mapping = cmp.mapping.preset.cmdline(),\n    sources = {\n      { name = 'buffer' }\n    }\n  })\n\n  -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).\n  cmp.setup.cmdline(':', {\n    mapping = cmp.mapping.preset.cmdline(),\n    sources = cmp.config.sources({\n      { name = 'path' }\n    }, {\n      { name = 'cmdline' }\n    }),\n    matching = { disallow_symbol_nonprefix_matching = false }\n  })\n\n  -- Set up lspconfig.\n  local capabilities = require('cmp_nvim_lsp').default_capabilities()\n  -- Replace <YOUR_LSP_SERVER> with each lsp server you've enabled.\n  vim.lsp.config('<YOUR_LSP_SERVER>', {\n    capabilities = capabilities\n  })\n  vim.lsp.enable('<YOUR_LSP_SERVER>')\nEOF\n```\n\n\n### Where can I find more completion sources?\n\nHave a look at the [Wiki](https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources) and the `nvim-cmp` [GitHub topic](https://github.com/topics/nvim-cmp).\n\n\n### Where can I find advanced configuration examples?\n\nSee the [Wiki](https://github.com/hrsh7th/nvim-cmp/wiki).\n"
  },
  {
    "path": "autoload/cmp.vim",
    "content": "let s:bridge_id = 0\nlet s:sources = {}\n\n\"\n\" cmp#register_source\n\"\nfunction! cmp#register_source(name, source) abort\n  let l:methods = []\n  for l:method in [\n  \\   'is_available',\n  \\   'get_debug_name',\n  \\   'get_position_encoding_kind',\n  \\   'get_trigger_characters',\n  \\   'get_keyword_pattern',\n  \\   'complete',\n  \\   'execute',\n  \\   'resolve'\n  \\ ]\n    if has_key(a:source, l:method) && type(a:source[l:method]) == v:t_func\n      call add(l:methods, l:method)\n    endif\n  endfor\n\n  let s:bridge_id += 1\n  let a:source.bridge_id = s:bridge_id\n  let a:source.id = luaeval('require(\"cmp\").register_source(_A[1], require(\"cmp.vim_source\").new(_A[2], _A[3]))', [a:name, s:bridge_id, l:methods])\n  let s:sources[s:bridge_id] = a:source\n  return a:source.id\nendfunction\n\n\"\n\" cmp#unregister_source\n\"\nfunction! cmp#unregister_source(id) abort\n  if has_key(s:sources, a:id)\n    unlet s:sources[a:id]\n  endif\n  call luaeval('require(\"cmp\").unregister_source(_A)', a:id)\nendfunction\n\n\"\n\" cmp#_method\n\"\nfunction! cmp#_method(bridge_id, method, args) abort\n  try\n    let l:source = s:sources[a:bridge_id]\n    if a:method ==# 'is_available'\n      return l:source[a:method]()\n    elseif a:method ==# 'get_debug_name'\n      return l:source[a:method]()\n    elseif a:method ==# 'get_position_encoding_kind'\n      return l:source[a:method](a:args[0])\n    elseif a:method ==# 'get_keyword_pattern'\n      return l:source[a:method](a:args[0])\n    elseif a:method ==# 'get_trigger_characters'\n      return l:source[a:method](a:args[0])\n    elseif a:method ==# 'complete'\n      return l:source[a:method](a:args[0], s:callback(a:args[1]))\n    elseif a:method ==# 'resolve'\n      return l:source[a:method](a:args[0], s:callback(a:args[1]))\n    elseif a:method ==# 'execute'\n      return l:source[a:method](a:args[0], s:callback(a:args[1]))\n    endif\n  catch /.*/\n    echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })\n  endtry\n  return v:null\nendfunction\n\n\"\n\" s:callback\n\"\nfunction! s:callback(id) abort\n  return { ... -> luaeval('require(\"cmp.vim_source\").on_callback(_A[1], _A[2])', [a:id, a:000]) }\nendfunction\n"
  },
  {
    "path": "doc/cmp.txt",
    "content": "*nvim-cmp* *cmp*\n\nA completion plugin for neovim coded in Lua.\n\n==============================================================================\nCONTENTS                                                          *cmp-contents*\n\nAbstract                                                          |cmp-abstract|\nConcept                                                            |cmp-concept|\nUsage                                                                |cmp-usage|\nFunction                                                          |cmp-function|\nMapping                                                            |cmp-mapping|\nCommand                                                            |cmp-command|\nHighlight                                                        |cmp-highlight|\nFileType                                                          |cmp-filetype|\nAutocmd                                                            |cmp-autocmd|\nConfig                                                              |cmp-config|\nConfig Helper                                                |cmp-config-helper|\nDevelop                                                            |cmp-develop|\nFAQ                                                                    |cmp-faq|\n\n==============================================================================\nAbstract                                                          *cmp-abstract*\n\nThis is nvim-cmp's document.\n\n1. This help file uses the type definition notation like `{lsp,cmp,vim}.*`\n  - You can find it in `../lua/cmp/types/init.lua`.\n2. Advanced configuration is described in the wiki.\n  - https://github.com/hrsh7th/nvim-cmp/wiki\n\n==============================================================================\nConcept                                                            *cmp-concept*\n\n- Full support for LSP completion related capabilities\n- Powerful customization abilities via Lua functions\n- Smart handling of key mappings\n- No flicker\n\n==============================================================================\nUsage                                                                *cmp-usage*\n\nA recommended configuration can be found below.\n  NOTE:\n    1. You must provide a `snippet.expand` function.\n    2. `cmp.setup.cmdline` won't work if you use the `native` completion menu.\n    3. You can disable the `default` options by specifying `cmp.config.disable` value.\n>vim\n  call plug#begin(s:plug_dir)\n  Plug 'neovim/nvim-lspconfig'\n  Plug 'hrsh7th/cmp-nvim-lsp'\n  Plug 'hrsh7th/cmp-buffer'\n  Plug 'hrsh7th/cmp-path'\n  Plug 'hrsh7th/cmp-cmdline'\n  Plug 'hrsh7th/nvim-cmp'\n\n  \" For vsnip users.\n  Plug 'hrsh7th/cmp-vsnip'\n  Plug 'hrsh7th/vim-vsnip'\n\n  \" For luasnip users.\n  \" Plug 'L3MON4D3/LuaSnip'\n  \" Plug 'saadparwaiz1/cmp_luasnip'\n\n  \" For mini.snippets users.\n  \" Plug 'echasnovski/mini.snippets'\n  \" Plug 'abeldekat/cmp-mini-snippets'\n\n  \" For snippy users.\n  \" Plug 'dcampos/nvim-snippy'\n  \" Plug 'dcampos/cmp-snippy'\n\n  \" For ultisnips users.\n  \" Plug 'SirVer/ultisnips'\n  \" Plug 'quangnguyen30192/cmp-nvim-ultisnips'\n\n  call plug#end()\n\n  set completeopt=menu,menuone,noselect\n\n  lua <<EOF\n    local cmp = require'cmp'\n\n    -- Global setup.\n    cmp.setup({\n      snippet = {\n        expand = function(args)\n          vim.fn[\"vsnip#anonymous\"](args.body) -- For `vsnip` users.\n          -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.\n          -- require'snippy'.expand_snippet(args.body) -- For `snippy` users.\n          -- vim.fn[\"UltiSnips#Anon\"](args.body) -- For `ultisnips` users.\n          -- vim.snippet.expand(args.body) -- For native neovim snippets (Neovim v0.10+)\n\n          -- For `mini.snippets` users:\n          -- local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert\n          -- insert({ body = args.body }) -- Insert at cursor\n          -- cmp.resubscribe({ \"TextChangedI\", \"TextChangedP\" })\n          -- require(\"cmp.config\").set_onetime({ sources = {} })\n        end,\n      },\n      window = {\n        -- completion = cmp.config.window.bordered(),\n        -- documentation = cmp.config.window.bordered(),\n      },\n      mapping = cmp.mapping.preset.insert({\n        ['<C-d>'] = cmp.mapping.scroll_docs(-4),\n        ['<C-f>'] = cmp.mapping.scroll_docs(4),\n        ['<C-Space>'] = cmp.mapping.complete(),\n        ['<CR>'] = cmp.mapping.confirm({ select = true }),\n      }),\n      sources = cmp.config.sources({\n        { name = 'nvim_lsp' },\n        { name = 'vsnip' }, -- For vsnip users.\n        -- { name = 'luasnip' }, -- For luasnip users.\n        -- { name = 'snippy' }, -- For snippy users.\n        -- { name = 'ultisnips' }, -- For ultisnips users.\n      }, {\n        { name = 'buffer' },\n      })\n    })\n\n    -- `/` cmdline setup.\n    cmp.setup.cmdline('/', {\n      mapping = cmp.mapping.preset.cmdline(),\n      sources = {\n        { name = 'buffer' }\n      }\n    })\n\n    -- `:` cmdline setup.\n    cmp.setup.cmdline(':', {\n      mapping = cmp.mapping.preset.cmdline(),\n      sources = cmp.config.sources({\n        { name = 'path' }\n      }, {\n        { name = 'cmdline' }\n      }),\n      matching = { disallow_symbol_nonprefix_matching = false }\n    })\n\n    -- Setup lspconfig.\n    local capabilities = require('cmp_nvim_lsp').default_capabilities()\n    vim.lsp.config(%YOUR_LSP_SERVER%, {\n      capabilities = capabilities\n    })\n    vim.lsp.enable(%YOUR_LSP_SERVER%)\n  EOF\n<\n==============================================================================\nFunction                                                          *cmp-function*\n\nNOTE: `<Cmd>lua require('cmp').complete()<CR>` can be used to call these functions in a mapping.\n\n*cmp.setup* (config: cmp.ConfigSchema)\n  Setup global configuration. See configuration options.\n\n*cmp.setup.filetype* (filetype: string, config: cmp.ConfigSchema)\n  Setup filetype-specific configuration.\n\n*cmp.setup.buffer* (config: cmp.ConfigSchema)\n  Setup configuration for the current buffer.\n\n*cmp.setup.cmdline* (cmdtype: string, config: cmp.ConfigSchema)\n  Setup cmdline configuration for the specific type of command.\n  See |getcmdtype()|.\n  NOTE: nvim-cmp does not support the `=` command type.\n\n*cmp.get_registered_sources* ()\n  Get all registered sources.\n\n*cmp.visible* ()\n  Return a boolean showing whether the completion menu is visible or not.\n\n*cmp.visible_docs* ()\n  Return a boolean showing whether the docs window is visible or not.\n\n*cmp.get_entries* ()\n  Return all current entries.\n\n*cmp.get_selected_entry* ()\n  Return currently selected entry (including preselected).\n\n*cmp.get_active_entry* ()\n  Return currently selected entry (excluding preselected).\n\n*cmp.close* ()\n  Close the completion menu.\n\n*cmp.abort* ()\n  Closes the completion menu and restore the current line to the state before the current completion was started.\n\n*cmp.select_next_item* (option: { behavior = cmp.SelectBehavior, count = 1 })\n  Select the next item. Set count with large number to select pagedown.\n  `behavior` can be one of:\n  - `cmp.SelectBehavior.Insert`: Inserts the text at cursor.\n  - `cmp.SelectBehavior.Select`: Only selects the text, potentially adds ghost_text at\n    cursor.\n>lua\n  cmp.setup {\n    mapping = {\n      [\"<C-j>\"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }),\n    }\n  }\n<\n\n*cmp.select_prev_item* (option: { behavior = cmp.SelectBehavior, count = 1 })\n  Select the previous item. Set count with large number to select pageup.\n  `behavior` can be one of:\n  - `cmp.SelectBehavior.Insert`: Inserts the text at cursor.\n  - `cmp.SelectBehavior.Select`: Only selects the text, potentially adds ghost_text at\n    cursor.\n>lua\n  cmp.setup {\n    mapping = {\n      [\"<C-k>\"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }),\n    }\n  }\n<\n*cmp.open_docs* ()\n  Open docs view.\n\n*cmp.close_docs* ()\n  Close docs view.\n\n*cmp.scroll_docs* (delta: number)\n  Scroll the documentation window if visible.\n\n*cmp.complete* (option: { reason = cmp.ContextReason, config = cmp.ConfigSchema })\n  Invoke completion.\n\n  The following configuration defines a key mapping to show completion only for vsnip snippets.\n>lua\n  cmp.setup {\n    mapping = {\n      ['<C-s>'] = cmp.mapping.complete({\n        config = {\n          sources = {\n            { name = 'vsnip' }\n          }\n        }\n      })\n    }\n  }\n< >vim\n  inoremap <C-S> <Cmd>lua require('cmp').complete({ config = { sources = { { name = 'vsnip' } } } })<CR>\n<\n  NOTE: `config` in that case means a temporary setting, but `config.mapping` remains permanent.\n\n*cmp.complete_common_string* ()\n  Complete common string (similar to shell completion behavior).\n>lua\n  cmp.setup {\n    mapping = {\n      ['<C-l>'] = cmp.mapping(function(fallback)\n        if cmp.visible() then\n          return cmp.complete_common_string()\n        end\n        fallback()\n      end, { 'i', 'c' }),\n    }\n  }\n<\n*cmp.confirm* (option: cmp.ConfirmOption, callback: function)\n  Accepts the currently selected completion item.\n  If you didn't select any item and the option table contains `select = true`,\n  nvim-cmp will automatically select the first item.\n\n  You can control how the completion item is injected into\n  the file through the `behavior` option:\n\n  `behavior=cmp.ConfirmBehavior.Insert`: inserts the selected item and\n    moves adjacent text to the right (default).\n  `behavior=cmp.ConfirmBehavior.Replace`: replaces adjacent text with\n    the selected item.\n>lua\n  cmp.setup {\n    mapping = {\n      [\"<CR>\"] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }),\n    }\n  }\n<\n*cmp.event:on* (%EVENT_NAME%, callback)\n  Subscribe to nvim-cmp's event. Events are listed below.\n\n  - `complete_done`: emit after current completion is done.\n  - `confirm_done`: emit after confirmation is done.\n  - `menu_opened`: emit after opening a new completion menu. Called with a table holding a key\n    named `window`, pointing to the completion menu implementation.\n  - `menu_closed`: emit after completion menu is closed. Called with a table holding a key\n    named `window`, pointing to the completion menu implementation.\n\n==============================================================================\nMapping                                                            *cmp-mapping*\n\nNvim-cmp's mapping mechanism is complex but flexible and user-friendly.\n\nYou can specify a mapping function that receives a `fallback` function as an argument.\nThe `fallback` function can be used to call an existing mapping.\n\nFor example, typical pair-wise plugins automatically define mappings for `<CR>` and `(`.\nNvim-cmp will overwrite it if you provide a mapping. To call the existing mapping,\nyou would need to invoke the `fallback` function.\n>lua\n  cmp.setup {\n    mapping = {\n      ['<CR>'] = function(fallback)\n        if cmp.visible() then\n          cmp.confirm()\n        else\n          fallback() -- If you use vim-endwise, this fallback will behave the same as vim-endwise.\n        end\n      end\n    }\n  }\n< >lua\n  cmp.setup {\n    mapping = {\n      ['<Tab>'] = function(fallback)\n        if cmp.visible() then\n          cmp.select_next_item()\n        else\n          fallback()\n        end\n      end\n    }\n  }\n<\n\nIt is possible to specify the modes the mapping should be active in (`i` = insert mode, `c` = command mode, `s` = select mode):\n>lua\n  cmp.setup {\n    mapping = {\n      ['<CR>'] = cmp.mapping(your_mapping_function, { 'i', 'c' })\n    }\n  }\n<\nYou can also specify different mappings for different modes by passing a table:\n>lua\n  cmp.setup {\n    mapping = {\n      ['<CR>'] = cmp.mapping({\n        i = your_mapping_function_a,\n        c = your_mapping_function_b,\n      })\n    }\n  }\n<\nThere are also builtin mapping helper functions you can use:\n\n  *cmp.mapping.close* ()\n    Same as |cmp.close|.\n\n  *cmp.mapping.abort* ()\n    Same as |cmp.abort|.\n\n  *cmp.mapping.select_next_item* (option: { behavior = cmp.SelectBehavior, count = 1 })\n    Same as |cmp.select_next_item|.\n\n  *cmp.mapping.select_prev_item* (option: { behavior = cmp.SelectBehavior, count = 1 })\n    Same as |cmp.select_prev_item|.\n\n  *cmp.mapping.open_docs* ()\n    Same as |cmp.open_docs|.\n\n  *cmp.mapping.close_docs* ()\n    Same as |cmp.close_docs|.\n\n  *cmp.mapping.scroll_docs* (delta: number)\n    Same as |cmp.scroll_docs|.\n\n  *cmp.mapping.complete* (option: cmp.CompleteParams)\n    Same as |cmp.complete|.\n\n  *cmp.mapping.complete_common_string* ()\n    Same as |cmp.complete_common_string|.\n\n  *cmp.mapping.confirm* (option: cmp.ConfirmOption)\n    Same as |cmp.confirm|.\n\nBuilt-in mapping helpers are only available as a configuration option.\nIf you want to call nvim-cmp features directly, please use |cmp-function| instead.\n\n\n\n==============================================================================\nCommand                                                            *cmp-command*\n\n*CmpStatus*\n  Describes statuses and states of sources.\n  Sometimes `unknown` will be printed - this is expected.\n  For example, `cmp-nvim-lsp` registers itself on InsertEnter autocommand\n  so the status will be shown as `unknown` when running the command.\n\n\n\n==============================================================================\nHighlight                                                        *cmp-highlight*\n\n*CmpItemAbbr*\n  Highlight group for unmatched characters of each completion field.\n\n*CmpItemAbbrDeprecated*\n  Highlight group for unmatched characters of each deprecated completion field.\n\n*CmpItemAbbrMatch*\n  Highlight group for matched characters of each completion field. Matched characters\n  must form a substring of a field which share a starting position.\n\n*CmpItemAbbrMatchFuzzy*\n  Highlight group for fuzzy-matched characters of each completion field.\n\n*CmpItemKind*\n  Highlight group for the kind of the field.\n\n  NOTE: `kind` is a symbol after each completion option.\n\n*CmpItemKindIcon\n  Highlight group for the icons used for each `lsp.CompletionItemKind`.\n\n*CmpItemKind%KIND_NAME%*\n  Highlight group for the kind of the field for a specific `lsp.CompletionItemKind`.\n  If you only want to overwrite the `method` kind's highlight group, you can do this:\n>vim\n    highlight CmpItemKindMethod guibg=NONE guifg=Orange\n\n*CmpItemKind%KIND_NAME%Icon\n  Highlight group for the icon shown for a specific `lsp.CompletionItemKind`.\n  Can be overriden the same way as shown above OR with a custom function\n  in your format table (e.g.):\n    \n      local function set_colors(str)\n        local color = vim.api.nvim_get_hl(args...)\n        vim.api.nvim_set_hl(args...)\n      end\n<\n*CmpItemMenu*\n  The menu field's highlight group.\n\n==============================================================================\nFileType                                                          *cmp-filetype*\n\n*cmp_menu*\n  The completion menu buffer's filetype.\n\n*cmp_docs*\n  The documentation window buffer's filetype.\n\n==============================================================================\nAutocmd                                                            *cmp-autocmd*\n\nYou can create custom autocommands for certain nvim-cmp events by defining\nautocommands for the User event with the following patterns:\n\n*CmpReady*\n  Invoked when nvim-cmp gets sourced from `plugin/cmp.lua`.\n\n*CmpRegisterSource*\n  Invoke when source was registered.\n\n*CmpUnregisterSource*\n  Invoke when source was un-registered.\n\n==============================================================================\nConfig                                                              *cmp-config*\n\nYou can use the following options via `cmp.setup { ... }` .\n\n                                                            *cmp-config.enabled*\nenabled~\n  `boolean | fun(): boolean`\n  Toggles the plugin on and off.\n\n                                                *cmp-config.performance.debounce*\nperformance.debounce~\n  `number`\n  Sets debounce time\n  This is the interval used to group up completions from different sources\n  for filtering and displaying.\n\n                                                *cmp-config.performance.throttle*\nperformance.throttle~\n  `number`\n  Sets throttle time\n  This is used to delay filtering and displaying completions.\n\n                                        *cmp-config.performance.fetching_timeout*\nperformance.fetching_timeout~\n    `number`\n    Sets the timeout of candidate fetching process.\n    The nvim-cmp will wait to display the most prioritized source.\n\n                                *cmp-config.performance.filtering_context_budget*\nperformance.filtering_context_budget~\n    `number`\n    Sets the filtering context budget in ms.\n    If filtering takes longer than this, it will be deferred.\n\n                                 *cmp-config.performance.confirm_resolve_timeout*\nperformance.confirm_resolve_timeout~\n    `number`\n    Sets the timeout for resolving item before confirmation.\n\n                                            *cmp-config.performance.async_budget*\nperformance.async_budget~\n    `number`\n    Maximum time (in ms) an async function is allowed to run during\n    one step of the event loop.\n\n                                        *cmp-config.performance.max_view_entries*\nperformance.max_view_entries~\n    `number`\n    Maximum number of items to show in the entries list.\n\n                                                          *cmp-config.preselect*\npreselect~\n  `cmp.PreselectMode`\n\n  1. `cmp.PreselectMode.Item`\n    nvim-cmp will preselect the item that the source specified.\n  2. `cmp.PreselectMode.None`\n    nvim-cmp will not preselect any items.\n\n                                                            *cmp-config.mapping*\nmapping~\n  `table<string, fun(fallback: function)`\n  See |cmp-mapping| section.\n\n                                                     *cmp-config.snippet.expand*\nsnippet.expand~\n  `fun(option: cmp.SnippetExpansionParams)`\n  The snippet expansion function. That's how nvim-cmp interacts with a\n  particular snippet engine.\n\n                                          *cmp-config.completion.keyword_length*\ncompletion.keyword_length~\n  `number`\n  The number of characters needed to trigger auto-completion.\n\n                                         *cmp-config.completion.keyword_pattern*\ncompletion.keyword_pattern~\n  `string`\n  The default keyword pattern.\n\n                                            *cmp-config.completion.autocomplete*\ncompletion.autocomplete~\n  `cmp.TriggerEvent[] | false`\n  The event to trigger autocompletion. If set to `false`, then completion is\n  only invoked manually (e.g. by calling `cmp.complete`).\n\n                                             *cmp-config.completion.completeopt*\ncompletion.completeopt~\n  `string`\n  Like vim's completeopt setting. See 'completeopt'.\n  In general, you don't need to change this.\n\n                                 *cmp-config.confirmation.get_commit_characters*\nconfirmation.get_commit_characters~\n  `fun(commit_characters:string[]):string[]`\n  You can append or exclude commitCharacters via this configuration option\n  function. The commitCharacters are defined by the LSP spec.\n\n                                    *cmp-config.formatting.expandable_indicator*\nformatting.expandable_indicator~\n  `cmp.expandable_indicator`\n  Boolean to show the `~` expandable indicator in cmp's floating window.\n\n                                                  *cmp-config.formatting.fields*\nformatting.fields~\n  `cmp.ItemField[]`\n  An array of completion fields to specify their order.\n\n                                                  *cmp-config.formatting.format*\nformatting.format~\n  `fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem`\n  The function used to customize the appearance of the completion menu. See\n  |complete-items|. This value can also be used to modify the `dup` property.\n  NOTE: The `vim.CompletedItem` can contain the special properties\n  `abbr_hl_group`, `icon_hl_group`, `kind_hl_group` and `menu_hl_group`.\n\n                                   *cmp-config.matching.disallow_fuzzy_matching*\nmatching.disallow_fuzzy_matching~\n  `boolean`\n  Whether to allow fuzzy matching.\n\n                               *cmp-config.matching.disallow_fullfuzzy_matching*\nmatching.disallow_fullfuzzy_matching~\n  `boolean`\n  Whether to allow full-fuzzy matching.\n\n                           *cmp-config.matching.disallow_partial_fuzzy_matching*\nmatching.disallow_partial_fuzzy_matching~\n  `boolean`\n  Whether to allow fuzzy matching without prefix matching.\n                                 *cmp-config.matching.disallow_partial_matching*\nmatching.disallow_partial_matching~\n  `boolean`\n  Whether to allow partial matching.\n\n                                *cmp-config.matching.disallow_prefix_unmatching*\nmatching.disallow_prefix_unmatching~\n  `boolean`\n  Whether to allow prefix unmatching.\n\n                                cmp-config.matching.disallow_symbol_nonprefix_matching\nmatching.disallow_symbol_nonprefix_matching\n  `boolean`\n  Whether to allow symbols in matches if the match is not a prefix match.\n\n                                            *cmp-config.sorting.priority_weight*\nsorting.priority_weight~\n  `number`\n  Each item's original priority (given by its corresponding source) will be\n  increased by `#sources - (source_index - 1)` and multiplied by `priority_weight`.\n  That is, the final priority is calculated by the following formula:\n>lua\n  final_score = orig_score + ((#sources - (source_index - 1)) * sorting.priority_weight)\n<\n                                                *cmp-config.sorting.comparators*\nsorting.comparators~\n  `(fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil)[]`\n  The function to customize the sorting behavior.\n  You can use built-in comparators via `cmp.config.compare.*`.\n\n                                                            *cmp-config.sources*\nsources~\n  `cmp.SourceConfig[]`\n  List of the sources and their configurations to use.\n  The order of the sources determines their order in the completion results.\n\n                                                    *cmp-config.sources[n].name*\nsources[n].name~\n  `string`\n  The name of the source.\n\n                                                  *cmp-config.sources[n].option*\nsources[n].option~\n  `table`\n  Any specific options defined by the source itself.\n\n                                          *cmp-config.sources[n].keyword_length*\nsources[n].keyword_length~\n  `number`\n  The source-specific keyword length to trigger auto completion.\n\n                                         *cmp-config.sources[n].keyword_pattern*\nsources[n].keyword_pattern~\n  `string`\n  The source-specific keyword pattern.\n\n                                      *cmp-config.sources[n].trigger_characters*\nsources[n].trigger_characters~\n  `string[]`\n  A source-specific keyword pattern.\n\n                                                *cmp-config.sources[n].priority*\nsources[n].priority~\n  `number`\n  The source-specific priority value.\n\n                                          *cmp-config.sources[n].max_item_count*\nsources[n].max_item_count~\n  `number`\n  The source-specific maximum item count option\n  Note: This is applied before sorting, so items that aren't well-matched may be selected.\n\n                                             *cmp-config.sources[n].group_index*\nsources[n].group_index~\n  `number`\n  The source group index.\n\n  For instance, you can set the `buffer`'s source `group_index` to a larger number\n  if you don't want to see `buffer` source items while `nvim-lsp` source is available:\n>lua\n    cmp.setup {\n      sources = {\n        { name = 'nvim_lsp', group_index = 1 },\n        { name = 'buffer', group_index = 2 },\n      }\n    }\n<\n  You can also achieve this by using the built-in configuration helper like this:\n>lua\n    cmp.setup {\n      sources = cmp.config.sources({\n        { name = 'nvim_lsp' },\n      }, {\n        { name = 'buffer' },\n      })\n    }\n<\n\n                                             *cmp-config.sources[n].entry_filter*\nsources[n].entry_filter~\n  `function`\n  A source-specific entry filter, with the following function signature:\n>\n  function(entry: cmp.Entry, ctx: cmp.Context): boolean\n<\n\n  Returning `true` will keep the entry, while returning `false` will remove it.\n\n  This can be used to hide certain entries from a given source. For instance, you\n  could hide all entries with kind `Text` from the `nvim_lsp` filter using the\n  following source definition:\n>lua\n  {\n    name = 'nvim_lsp',\n    entry_filter = function(entry, ctx)\n      return require('cmp.types').lsp.CompletionItemKind[entry:get_kind()] ~= 'Text'\n    end\n  }\n<\n  Using the `ctx` parameter, you can further customize the behaviour of the\n  source.\n\n                                                               *cmp-config.view*\nview~\n  `{ docs: cmp.DocsViewConfig }`\n  `{ entries: cmp.EntriesViewConfig|string }`\n  The view class used to customize nvim-cmp's appearance.\n  Currently available configuration options are:\n\n                                                *cmp-config.view.docs.auto_open*\nview.docs.auto_open~\n  `boolean`\n\n  Specify whether to show the docs_view when selecting an item.\n\n                                                *cmp-config.view.entries.selection_order*\nview.entries.selection_order~\n  `string`\n\n  Specify whether to select the option in the pmenu that is at\n  the top (`top_down`) or nearest to the cursor (`near_cursor`).\n  Useful if pmenu is above cursor and you want to change default\n  selection direction. Custom view only. `top_down` by default.\n\n                                                 *cmp-config.view.entries.follow_cursor*\nview.entries.follow_cursor~\n  `boolean`\n\n  Specify whether the pmenu should follow the current position of the cursor\n  as the user types. Custom view only. `false` by default.\n\n                           *cmp-config.window.{completion,documentation}.border*\nwindow.{completion,documentation}.border~\n  `string | string[] | nil`\n  Border characters used for the completion popup menu when |experimental.native_menu| is disabled.\n  See |nvim_open_win|.\n\n                     *cmp-config.window.{completion,documentation}.winhighlight*\nwindow.{completion,documentation}.winhighlight~\n  `string | cmp.WinhighlightConfig`\n  Specify the window's winhighlight option.\n  See |nvim_open_win|.\n\n                     *cmp-config.window.{completion,documentation}.winblend*\nwindow.{completion,documentation}.winblend~\n  `string | cmp.WinhighlightConfig`\n  Specify the window's winblend option.\n  See |nvim_open_win|.\n\n                           *cmp-config.window.{completion,documentation}.zindex*\nwindow.{completion,documentation}.zindex~\n  `number`\n  The completion window's zindex.\n  See |nvim_open_win|.\n\n                        *cmp-config.window.{completion,documentation}.scrolloff*\nwindow.completion.scrolloff~\n  `number`\n  Specify the window's scrolloff option.\n  See |'scrolloff'|.\n\n                       *cmp-config.window.{completion,documentation}.col_offset*\nwindow.completion.col_offset~\n  `number`\n  Offsets the completion window relative to the cursor.\n  Offsets the documentation window relative to the completion window.\n\n                                     *cmp-config.window.completion.side_padding*\nwindow.completion.side_padding~\n  `number`\n  The amount of padding to add on the completion window's sides\n\n                                     *cmp-config.window.completion.scrollbar*\nwindow.completion.scrollbar~\n  `boolean`\n  Whether the scrollbar should be enabled if there are more items that fit\n\n                                     *cmp-config.window.completion.max_height*\nwindow.completion.max_height~\n  `number | nil`\n  The completion window's max height, can be set to 0 to use all available\n  space. To use `vim.o.pumheight` set this to `nil`.\n  See |'pumheight'|.\n\n                                     *cmp-config.window.documentation.max_width*\nwindow.documentation.max_width~\n  `number`\n  The documentation window's max width, can be set to 0 to use all available\n  space.\n\n                                    *cmp-config.window.documentation.max_height*\nwindow.documentation.max_height~\n  `number`\n  The documentation window's max height, can be set to 0 to use all available\n  space.\n\n                                            *cmp-config.experimental.ghost_text*\nexperimental.ghost_text~\n  `boolean | { hl_group = string }`\n  Whether to enable the ghost_text feature.\n\n==============================================================================\nConfig Helper                                                *cmp-config-helper*\n\nYou can use the following configuration helpers:\n\ncmp.config.compare~\n\n  TBD\n\ncmp.config.context~\n\n  The `cmp.config.context` can be used for context-aware completion toggling.\n>lua\n    cmp.setup {\n      enabled = function()\n        -- disable completion if the cursor is `Comment` syntax group.\n        return not cmp.config.context.in_syntax_group('Comment')\n      end\n    }\n<\n  *cmp.config.context.in_syntax_group* (group)\n    You can specify the vim's built-in syntax group.\n    If you use tree-sitter, you should use `cmp.config.context.in_treesitter_capture` instead.\n\n  *cmp.config.context.in_treesitter_capture* (capture)\n    You can specify the treesitter capture name.\n    If you don't use the `nvim-treesitter` plugin, this helper will not work correctly.\n\ncmp.config.mapping~\n\n  See |cmp-mapping|.\n\ncmp.config.sources~\n\n  *cmp.config.sources* (...sources)\n    You can specify multiple source arrays. The sources are grouped in the\n    order you specify, and the groups are displayed as a fallback, like chain\n    completion.\n>lua\n    cmp.setup {\n      sources = cmp.config.sources({\n        { name = 'nvim_lsp' },\n      }, {\n        { name = 'buffer' },\n      })\n    }\n<\ncmp.config.window~\n\n  *cmp.config.window.bordered* (option)\n    Make the completion window `bordered`.\n    The option is described in `cmp.ConfigSchema`.\n>lua\n    cmp.setup {\n      window = {\n        completion = cmp.config.window.bordered(),\n        documentation = cmp.config.window.bordered(),\n      }\n    }\n<\n==============================================================================\nDevelop                                                            *cmp-develop*\n\nCreating a custom source~\n\nNOTE:\n  1. The `complete` method is required. Others can be omitted.\n  2. The `callback` function must always be called.\n  3. You can use only `require('cmp')` in custom source.\n  4. If the LSP spec was changed, nvim-cmp may implement it without any announcement (potentially introducing breaking changes).\n  5. You should read ./lua/cmp/types and https://microsoft.github.io/language-server-protocol/specifications/specification-current.\n  6. Please add your source to the list of sources in the Wiki (https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources)\n  and if you publish it on GitHub, add the `nvim-cmp` topic so users can find it more easily.\n\nHere is an example on how to create a custom source:\n>lua\n  local source = {}\n\n  ---Return whether this source is available in the current context or not (optional).\n  ---@return boolean\n  function source:is_available()\n    return true\n  end\n\n  ---Return the debug name of this source (optional).\n  ---@return string\n  function source:get_debug_name()\n    return 'debug name'\n  end\n\n  ---Return LSP's PositionEncodingKind.\n  ---@NOTE: If this method is omitted, the default value will be `utf-16`.\n  ---@return lsp.PositionEncodingKind\n  function source:get_position_encoding_kind()\n    return 'utf-16'\n  end\n\n  ---Return the keyword pattern for triggering completion (optional).\n  ---If this is omitted, nvim-cmp will use a default keyword pattern. See |cmp-config.completion.keyword_pattern|.\n  ---@return string\n  function source:get_keyword_pattern()\n    return [[\\k\\+]]\n  end\n\n  ---Return trigger characters for triggering completion (optional).\n  function source:get_trigger_characters()\n    return { '.' }\n  end\n\n  ---Invoke completion (required).\n  ---@param params cmp.SourceCompletionApiParams\n  ---@param callback fun(response: lsp.CompletionResponse|nil)\n  function source:complete(params, callback)\n    callback({\n      { label = 'January' },\n      { label = 'February' },\n      { label = 'March' },\n      { label = 'April' },\n      { label = 'May' },\n      { label = 'June' },\n      { label = 'July' },\n      { label = 'August' },\n      { label = 'September' },\n      { label = 'October' },\n      { label = 'November' },\n      { label = 'December' },\n    })\n  end\n\n  ---Resolve completion item (optional). This is called right before the completion is about to be displayed.\n  ---Useful for setting the text shown in the documentation window (`completion_item.documentation`).\n  ---@param completion_item lsp.CompletionItem\n  ---@param callback fun(completion_item: lsp.CompletionItem|nil)\n  function source:resolve(completion_item, callback)\n    callback(completion_item)\n  end\n\n  ---Executed after the item was selected.\n  ---@param completion_item lsp.CompletionItem\n  ---@param callback fun(completion_item: lsp.CompletionItem|nil)\n  function source:execute(completion_item, callback)\n    callback(completion_item)\n  end\n\n  ---Register your source to nvim-cmp.\n  require('cmp').register_source('month', source)\n<\n==============================================================================\nFAQ                                                                    *cmp-faq*\n\nWhy does cmp automatically select a particular item? ~\nHow to disable the preselect feature? ~\n\n  Nvim-cmp respects the LSP (Language Server Protocol) specification.\n  The LSP spec defines the `preselect` feature for completion.\n\n  You can disable the `preselect` feature like this:\n>lua\n  cmp.setup {\n    preselect = cmp.PreselectMode.None\n  }\n<\nHow to disable only specific language-server's completion?~\n\n  You can disable `completionProvider` in lspconfig configuration.\n>lua\n  lspconfig[%SERVER_NAME%].setup {\n    on_attach = function(client)\n      client.server_capabilities.completionProvider = false\n    end\n  }\n<\n\n\nHow to disable commitCharacters?~\n\n  You can disable the commitCharacters feature (which is defined in LSP spec):\n>lua\n  cmp.setup {\n    confirmation = {\n      get_commit_characters = function(commit_characters)\n        return {}\n      end\n    }\n  }\n<\n\nHow to disable automatic display of docs view?~\n\n  You can add the `view.docs.auto_open = false` for configuration.\n>lua\n  cmp.setup {\n    ...\n    view = {\n      docs = {\n        auto_open = false\n      }\n    }\n    ...\n  }\n<\n\n  additionally, if you want to open/close docs view via your key mapping, you\n  can define keymapping as the following.\n>lua\n  cmp.setup {\n    ...\n    mapping = {\n      ['<C-g>'] = function()\n        if cmp.visible_docs() then\n          cmp.close_docs()\n        else\n          cmp.open_docs()\n        end\n      end\n    }\n    ...\n  }\n<\n\nHow to disable auto-completion?~\nHow to use nvim-cmp as omnifunc?~\n\n  You can disable auto-completion like this:\n>lua\n  cmp.setup {\n    ...\n    completion = {\n      autocomplete = false\n    }\n    ...\n  }\n<\n  Then you will need to invoke completion manually.\n>vim\n  inoremap <C-x><C-o> <Cmd>lua require('cmp').complete()<CR>\n<\n\nHow to disable nvim-cmp for a specific buffer?~\nHow to setup nvim-cmp for a specific buffer?~\n\n  You can setup buffer-specific configuration like this:\n>lua\n  cmp.setup.filetype({ 'markdown', 'help' }, {\n    sources = {\n      { name = 'path' },\n      { name = 'buffer' },\n    }\n  })\n<\n\nHow to disable the documentation window?~\n\n  Simply use the following config:\n>lua\n  cmp.setup.filetype({ 'markdown', 'help' }, {\n    window = {\n      documentation = cmp.config.disable\n    }\n  })\n<\n\nI'm using clangd. The menu items are mis-indented.~\n\n  It's caused by clangd. You can specify `--header-insertion-decorators` for\n  clangd's command-line arguments. See #999.\n\n\nHow to integrate with copilot.vim?~\n\n  Copilot.vim and nvim-cmp both have a `key-mapping fallback` mechanism.\n  Therefore, you should manage those plugins by yourself.\n\n  Fortunately, the copilot.vim has a feature that disables the fallback mechanism.\n>vim\n  let g:copilot_no_tab_map = v:true\n  imap <expr> <Plug>(vimrc:copilot-dummy-map) copilot#Accept(\"\\<Tab>\")\n<\n  You can manage copilot.vim's accept feature inside nvim-cmp's key-mapping function:\n>lua\n  cmp.setup {\n    mapping = {\n      ['<C-g>'] = cmp.mapping(function(fallback)\n        vim.api.nvim_feedkeys(vim.fn['copilot#Accept'](vim.api.nvim_replace_termcodes('<Tab>', true, true, true)), 'n', true)\n      end)\n    },\n    experimental = {\n      ghost_text = false -- this feature conflict with copilot.vim's preview.\n    }\n  }\n<\nnvim-cmp does not work as expected.~\n\n  There are some known issues. Please check the following.\n\n  - nvim-cmp does not work with `set paste` option.\n  - Command line mode key mapping is unified regardless of `:`, `/`, `?`. Therefore, it is impossible to apply the mapping only to `:`.\n\nHow to customize the menu appearance?~\n\n  Have a look at the wiki (https://github.com/hrsh7th/nvim-cmp/wiki).\n\n==============================================================================\n vim:tw=78:ts=2:et:ft=help:norl:\n"
  },
  {
    "path": "init.sh",
    "content": "DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nrm $DIR/.git/hooks/*\ncp $DIR/.githooks/* $DIR/.git/hooks/\nchmod 755 $DIR/.git/hooks/*\n\n\n"
  },
  {
    "path": "lua/cmp/config/compare.lua",
    "content": "local types = require('cmp.types')\nlocal cache = require('cmp.utils.cache')\n\n---@type cmp.Comparator[]\nlocal compare = {}\n\n--- Comparators (:help cmp-config.sorting.comparators) should return\n--- true when the first entry should come EARLIER (i.e., higher ranking) than the second entry,\n--- or nil if no pairwise ordering preference from the comparator.\n--- See also :help table.sort() and cmp.view.open() to see how comparators are used.\n\n---@class cmp.ComparatorFunctor\n---@overload fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil\n---@alias cmp.ComparatorFunction fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil\n---@alias cmp.Comparator cmp.ComparatorFunction | cmp.ComparatorFunctor\n\n---offset: Entries with smaller offset will be ranked higher.\n---@type cmp.ComparatorFunction\ncompare.offset = function(entry1, entry2)\n  local diff = entry1.offset - entry2.offset\n  if diff < 0 then\n    return true\n  elseif diff > 0 then\n    return false\n  end\n  return nil\nend\n\n---exact: Entries with exact == true will be ranked higher.\n---@type cmp.ComparatorFunction\ncompare.exact = function(entry1, entry2)\n  if entry1.exact ~= entry2.exact then\n    return entry1.exact\n  end\n  return nil\nend\n\n---score: Entries with higher score will be ranked higher.\n---@type cmp.ComparatorFunction\ncompare.score = function(entry1, entry2)\n  local diff = entry2.score - entry1.score\n  if diff < 0 then\n    return true\n  elseif diff > 0 then\n    return false\n  end\n  return nil\nend\n\n---recently_used: Entries that are used recently will be ranked higher.\n---@type cmp.ComparatorFunctor\ncompare.recently_used = setmetatable({\n  records = {},\n  add_entry = function(self, e)\n    self.records[e.completion_item.label] = vim.loop.now()\n  end,\n}, {\n  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil\n  __call = function(self, entry1, entry2)\n    local t1 = self.records[entry1.completion_item.label] or -1\n    local t2 = self.records[entry2.completion_item.label] or -1\n    if t1 ~= t2 then\n      return t1 > t2\n    end\n    return nil\n  end,\n})\n\n---kind: Entries with smaller ordinal value of 'kind' will be ranked higher.\n---(see lsp.CompletionItemKind enum).\n---Exceptions are that Text(1) will be ranked the lowest, and snippets be the highest.\n---@type cmp.ComparatorFunction\ncompare.kind = function(entry1, entry2)\n  local kind1 = entry1:get_kind() --- @type lsp.CompletionItemKind | number\n  local kind2 = entry2:get_kind() --- @type lsp.CompletionItemKind | number\n  kind1 = kind1 == types.lsp.CompletionItemKind.Text and 100 or kind1\n  kind2 = kind2 == types.lsp.CompletionItemKind.Text and 100 or kind2\n  if kind1 ~= kind2 then\n    if kind1 == types.lsp.CompletionItemKind.Snippet then\n      return true\n    end\n    if kind2 == types.lsp.CompletionItemKind.Snippet then\n      return false\n    end\n    local diff = kind1 - kind2\n    if diff < 0 then\n      return true\n    elseif diff > 0 then\n      return false\n    end\n  end\n  return nil\nend\n\n---sort_text: Entries will be ranked according to the lexicographical order of sortText.\n---@type cmp.ComparatorFunction\ncompare.sort_text = function(entry1, entry2)\n  if entry1.completion_item.sortText and entry2.completion_item.sortText then\n    local diff = vim.stricmp(entry1.completion_item.sortText, entry2.completion_item.sortText)\n    if diff < 0 then\n      return true\n    elseif diff > 0 then\n      return false\n    end\n  end\n  return nil\nend\n\n---length: Entries with shorter label length will be ranked higher.\n---@type cmp.ComparatorFunction\ncompare.length = function(entry1, entry2)\n  local diff = #entry1.completion_item.label - #entry2.completion_item.label\n  if diff < 0 then\n    return true\n  elseif diff > 0 then\n    return false\n  end\n  return nil\nend\n\n----order: Entries with smaller id will be ranked higher.\n---@type fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil\ncompare.order = function(entry1, entry2)\n  local diff = entry1.id - entry2.id\n  if diff < 0 then\n    return true\n  elseif diff > 0 then\n    return false\n  end\n  return nil\nend\n\n---locality: Entries with higher locality (i.e., words that are closer to the cursor)\n---will be ranked higher. See GH-183 for more details.\n---@type cmp.ComparatorFunctor\ncompare.locality = setmetatable({\n  lines_count = 10,\n  lines_cache = cache.new(),\n  locality_map = {},\n  update = function(self)\n    local config = require('cmp').get_config()\n    if not vim.tbl_contains(config.sorting.comparators, compare.locality) then\n      return\n    end\n\n    local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf()\n    local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1\n    local max = vim.api.nvim_buf_line_count(buf)\n\n    if self.lines_cache:get('buf') ~= buf then\n      self.lines_cache:clear()\n      self.lines_cache:set('buf', buf)\n    end\n\n    self.locality_map = {}\n    for i = math.max(0, cursor_row - self.lines_count), math.min(max, cursor_row + self.lines_count) do\n      local is_above = i < cursor_row\n      local buffer = vim.api.nvim_buf_get_lines(buf, i, i + 1, false)[1] or ''\n      local locality_map = self.lines_cache:ensure({ 'line', buffer }, function()\n        local locality_map = {}\n        local regexp = vim.regex(config.completion.keyword_pattern)\n        -- the buffer length check is to avoid performance issues on very long lines, #1841\n        while buffer ~= '' and #buffer < 5000 do\n          local s, e = regexp:match_str(buffer)\n          if s and e then\n            local w = string.sub(buffer, s + 1, e)\n            local d = math.abs(i - cursor_row) - (is_above and 1 or 0)\n            locality_map[w] = math.min(locality_map[w] or math.huge, d)\n            buffer = string.sub(buffer, e + 1)\n          else\n            break\n          end\n        end\n        return locality_map\n      end)\n      for w, d in pairs(locality_map) do\n        self.locality_map[w] = math.min(self.locality_map[w] or d, math.abs(i - cursor_row))\n      end\n    end\n  end,\n}, {\n  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil\n  __call = function(self, entry1, entry2)\n    local local1 = self.locality_map[entry1.word]\n    local local2 = self.locality_map[entry2.word]\n    if local1 ~= local2 then\n      if local1 == nil then\n        return false\n      end\n      if local2 == nil then\n        return true\n      end\n      return local1 < local2\n    end\n    return nil\n  end,\n})\n\n---scopes: Entries defined in a closer scope will be ranked higher (e.g., prefer local variables to globals).\n---@type cmp.ComparatorFunctor\ncompare.scopes = setmetatable({\n  scopes_map = {},\n  update = function(self)\n    local config = require('cmp').get_config()\n    if not vim.tbl_contains(config.sorting.comparators, compare.scopes) then\n      return\n    end\n\n    local ok, locals = pcall(require, 'nvim-treesitter.locals')\n    if ok then\n      local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf()\n      local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1\n\n      -- Cursor scope.\n      local cursor_scope = nil\n      -- Prioritize the older get_scopes method from nvim-treesitter `master` over get from `main`\n      local scopes = locals.get_scopes and locals.get_scopes(buf) or select(3, locals.get(buf))\n      for _, scope in ipairs(scopes) do\n        if scope:start() <= cursor_row and cursor_row <= scope:end_() then\n          if not cursor_scope then\n            cursor_scope = scope\n          else\n            if cursor_scope:start() <= scope:start() and scope:end_() <= cursor_scope:end_() then\n              cursor_scope = scope\n            end\n          end\n        elseif cursor_scope and cursor_scope:end_() <= scope:start() then\n          break\n        end\n      end\n\n      -- Definitions.\n      local definitions = locals.get_definitions_lookup_table(buf)\n\n      -- Narrow definitions.\n      local depth = 0\n      for scope in locals.iter_scope_tree(cursor_scope, buf) do\n        local s, e = scope:start(), scope:end_()\n\n        -- Check scope's direct child.\n        for _, definition in pairs(definitions) do\n          if s <= definition.node:start() and definition.node:end_() <= e then\n            if scope:id() == locals.containing_scope(definition.node, buf):id() then\n              local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text\n              local text = get_node_text(definition.node, buf) or ''\n              if not self.scopes_map[text] then\n                self.scopes_map[text] = depth\n              end\n            end\n          end\n        end\n        depth = depth + 1\n      end\n    end\n  end,\n}, {\n  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil\n  __call = function(self, entry1, entry2)\n    local local1 = self.scopes_map[entry1.word]\n    local local2 = self.scopes_map[entry2.word]\n    if local1 ~= local2 then\n      if local1 == nil then\n        return false\n      end\n      if local2 == nil then\n        return true\n      end\n      return local1 < local2\n    end\n  end,\n})\n\nreturn compare\n"
  },
  {
    "path": "lua/cmp/config/context.lua",
    "content": "local api = require('cmp.utils.api')\n\nlocal context = {}\n\n---Check if cursor is in syntax group\n---@param group string | []string\n---@return boolean\ncontext.in_syntax_group = function(group)\n  local row, col = unpack(vim.api.nvim_win_get_cursor(0))\n  if not api.is_insert_mode() then\n    col = col + 1\n  end\n\n  for _, syn_id in ipairs(vim.fn.synstack(row, col)) do\n    syn_id = vim.fn.synIDtrans(syn_id) -- Resolve :highlight links\n    local g = vim.fn.synIDattr(syn_id, 'name')\n    if type(group) == 'string' and g == group then\n      return true\n    elseif type(group) == 'table' and vim.tbl_contains(group, g) then\n      return true\n    end\n  end\n\n  return false\nend\n\n---Check if cursor is in treesitter capture\n---@param capture string | []string\n---@return boolean\ncontext.in_treesitter_capture = function(capture)\n  local buf = vim.api.nvim_get_current_buf()\n  local row, col = unpack(vim.api.nvim_win_get_cursor(0))\n  row = row - 1\n  if vim.api.nvim_get_mode().mode == 'i' then\n    col = col - 1\n  end\n\n  local get_captures_at_pos = -- See neovim/neovim#20331\n    require('vim.treesitter').get_captures_at_pos -- for neovim >= 0.8 or require('vim.treesitter').get_captures_at_position -- for neovim < 0.8\n\n  local captures_at_cursor = vim.tbl_map(function(x)\n    return x.capture\n  end, get_captures_at_pos(buf, row, col))\n\n  if vim.tbl_isempty(captures_at_cursor) then\n    return false\n  elseif type(capture) == 'string' and vim.tbl_contains(captures_at_cursor, capture) then\n    return true\n  elseif type(capture) == 'table' then\n    for _, v in ipairs(capture) do\n      if vim.tbl_contains(captures_at_cursor, v) then\n        return true\n      end\n    end\n  end\n\n  return false\nend\n\nreturn context\n"
  },
  {
    "path": "lua/cmp/config/default.lua",
    "content": "local compare = require('cmp.config.compare')\nlocal types = require('cmp.types')\nlocal window = require('cmp.config.window')\n\nlocal WIDE_HEIGHT = 40\n\n---@return cmp.ConfigSchema\nreturn function()\n  ---@type cmp.ConfigSchema\n  local config = {\n    enabled = function()\n      local disabled = false\n      disabled = disabled or (vim.api.nvim_get_option_value('buftype', { buf = 0 }) == 'prompt')\n      disabled = disabled or (vim.fn.reg_recording() ~= '')\n      disabled = disabled or (vim.fn.reg_executing() ~= '')\n      return not disabled\n    end,\n\n    performance = {\n      debounce = 60,\n      throttle = 30,\n      fetching_timeout = 500,\n      filtering_context_budget = 3,\n      confirm_resolve_timeout = 80,\n      async_budget = 1,\n      max_view_entries = 200,\n    },\n\n    preselect = types.cmp.PreselectMode.Item,\n\n    mapping = {},\n\n    snippet = {\n      expand = vim.fn.has('nvim-0.10') == 1 and function(args)\n        vim.snippet.expand(args.body)\n      end or function(_)\n        error('snippet engine is not configured.')\n      end,\n    },\n\n    completion = {\n      autocomplete = {\n        types.cmp.TriggerEvent.TextChanged,\n      },\n      completeopt = 'menu,menuone,noselect',\n      keyword_pattern = [[\\%(-\\?\\d\\+\\%(\\.\\d\\+\\)\\?\\|\\h\\w*\\%(-\\w*\\)*\\)]],\n      keyword_length = 1,\n    },\n\n    formatting = {\n      expandable_indicator = true,\n      fields = { 'abbr', 'icon', 'kind', 'menu' },\n      format = function(_, vim_item)\n        return vim_item\n      end,\n    },\n\n    matching = {\n      disallow_fuzzy_matching = false,\n      disallow_fullfuzzy_matching = false,\n      disallow_partial_fuzzy_matching = true,\n      disallow_partial_matching = false,\n      disallow_prefix_unmatching = false,\n      disallow_symbol_nonprefix_matching = true,\n    },\n\n    sorting = {\n      priority_weight = 2,\n      comparators = {\n        compare.offset,\n        compare.exact,\n        -- compare.scopes,\n        compare.score,\n        compare.recently_used,\n        compare.locality,\n        compare.kind,\n        compare.sort_text,\n        compare.length,\n        compare.order,\n      },\n    },\n\n    sources = {},\n\n    confirmation = {\n      default_behavior = types.cmp.ConfirmBehavior.Insert,\n      get_commit_characters = function(commit_characters)\n        return commit_characters\n      end,\n    },\n\n    event = {},\n\n    experimental = {\n      ghost_text = false,\n    },\n\n    view = {\n      entries = {\n        name = 'custom',\n        selection_order = 'top_down',\n        vertical_positioning = 'below',\n        follow_cursor = false,\n      },\n      docs = {\n        auto_open = true,\n      },\n    },\n\n    window = {\n      completion = {\n        border = window.get_border(),\n        winhighlight = 'Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None',\n        winblend = vim.o.pumblend,\n        scrolloff = 0,\n        col_offset = 0,\n        side_padding = 1,\n        scrollbar = true,\n      },\n      documentation = {\n        max_height = math.floor(WIDE_HEIGHT * (WIDE_HEIGHT / vim.o.lines)),\n        max_width = math.floor((WIDE_HEIGHT * 2) * (vim.o.columns / (WIDE_HEIGHT * 2 * 16 / 9))),\n        border = window.get_border(),\n        winhighlight = 'FloatBorder:NormalFloat',\n        winblend = vim.o.pumblend,\n        col_offset = 0,\n      },\n    },\n  }\n  return config\nend\n"
  },
  {
    "path": "lua/cmp/config/mapping.lua",
    "content": "local types = require('cmp.types')\nlocal misc = require('cmp.utils.misc')\nlocal keymap = require('cmp.utils.keymap')\n\nlocal function merge_keymaps(base, override)\n  local normalized_base = {}\n  for k, v in pairs(base) do\n    normalized_base[keymap.normalize(k)] = v\n  end\n\n  local normalized_override = {}\n  for k, v in pairs(override) do\n    normalized_override[keymap.normalize(k)] = v\n  end\n\n  return misc.merge(normalized_base, normalized_override)\nend\n\nlocal mapping = setmetatable({}, {\n  __call = function(_, invoke, modes)\n    if type(invoke) == 'function' then\n      local map = {}\n      for _, mode in ipairs(modes or { 'i' }) do\n        map[mode] = invoke\n      end\n      return map\n    end\n    return invoke\n  end,\n})\n\n---Mapping preset configuration.\nmapping.preset = {}\n\n---Mapping preset insert-mode configuration.\nmapping.preset.insert = function(override)\n  return merge_keymaps(override or {}, {\n    ['<Down>'] = {\n      i = mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Select }),\n    },\n    ['<Up>'] = {\n      i = mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Select }),\n    },\n    ['<C-n>'] = {\n      i = function()\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_next_item({ behavior = types.cmp.SelectBehavior.Insert })\n        else\n          cmp.complete()\n        end\n      end,\n    },\n    ['<C-p>'] = {\n      i = function()\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_prev_item({ behavior = types.cmp.SelectBehavior.Insert })\n        else\n          cmp.complete()\n        end\n      end,\n    },\n    ['<C-y>'] = {\n      i = mapping.confirm({ select = false }),\n    },\n    ['<C-e>'] = {\n      i = mapping.abort(),\n    },\n  })\nend\n\n---Mapping preset cmdline-mode configuration.\nmapping.preset.cmdline = function(override)\n  return merge_keymaps(override or {}, {\n    ['<C-z>'] = {\n      c = function()\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_next_item()\n        else\n          cmp.complete()\n        end\n      end,\n    },\n    ['<Tab>'] = {\n      c = function()\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_next_item()\n        else\n          cmp.complete()\n        end\n      end,\n    },\n    ['<S-Tab>'] = {\n      c = function()\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_prev_item()\n        else\n          cmp.complete()\n        end\n      end,\n    },\n    ['<C-n>'] = {\n      c = function(fallback)\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_next_item()\n        else\n          fallback()\n        end\n      end,\n    },\n    ['<C-p>'] = {\n      c = function(fallback)\n        local cmp = require('cmp')\n        if cmp.visible() then\n          cmp.select_prev_item()\n        else\n          fallback()\n        end\n      end,\n    },\n    ['<C-e>'] = {\n      c = mapping.abort(),\n    },\n    ['<C-y>'] = {\n      c = mapping.confirm({ select = false }),\n    },\n  })\nend\n\n---Invoke completion\n---@param option? cmp.CompleteParams\nmapping.complete = function(option)\n  return function(fallback)\n    if not require('cmp').complete(option) then\n      fallback()\n    end\n  end\nend\n\n---Complete common string.\nmapping.complete_common_string = function()\n  return function(fallback)\n    if not require('cmp').complete_common_string() then\n      fallback()\n    end\n  end\nend\n\n---Close current completion menu if it displayed.\nmapping.close = function()\n  return function(fallback)\n    if not require('cmp').close() then\n      fallback()\n    end\n  end\nend\n\n---Abort current completion menu if it displayed.\nmapping.abort = function()\n  return function(fallback)\n    if not require('cmp').abort() then\n      fallback()\n    end\n  end\nend\n\n---Scroll documentation window.\nmapping.scroll_docs = function(delta)\n  return function(fallback)\n    if not require('cmp').scroll_docs(delta) then\n      fallback()\n    end\n  end\nend\n\n--- Opens the documentation window.\nmapping.open_docs = function()\n  return function(fallback)\n    if not require('cmp').open_docs() then\n      fallback()\n    end\n  end\nend\n\n--- Close the documentation window.\nmapping.close_docs = function()\n  return function(fallback)\n    if not require('cmp').close_docs() then\n      fallback()\n    end\n  end\nend\n\n---Select next completion item.\nmapping.select_next_item = function(option)\n  return function(fallback)\n    if not require('cmp').select_next_item(option) then\n      local release = require('cmp').core:suspend()\n      fallback()\n      vim.schedule(release)\n    end\n  end\nend\n\n---Select prev completion item.\nmapping.select_prev_item = function(option)\n  return function(fallback)\n    if not require('cmp').select_prev_item(option) then\n      local release = require('cmp').core:suspend()\n      fallback()\n      vim.schedule(release)\n    end\n  end\nend\n\n---Confirm selection\nmapping.confirm = function(option)\n  return function(fallback)\n    if not require('cmp').confirm(option) then\n      fallback()\n    end\n  end\nend\n\nreturn mapping\n"
  },
  {
    "path": "lua/cmp/config/sources.lua",
    "content": "return function(...)\n  local sources = {}\n  for i, group in ipairs({ ... }) do\n    for _, source in ipairs(group) do\n      source.group_index = i\n      table.insert(sources, source)\n    end\n  end\n  return sources\nend\n"
  },
  {
    "path": "lua/cmp/config/window.lua",
    "content": "local window = {}\n\nwindow.bordered = function(opts)\n  opts = opts or {}\n  return {\n    border = opts.border or window.get_border(),\n    winhighlight = opts.winhighlight or 'Normal:Normal,FloatBorder:FloatBorder,CursorLine:Visual,Search:None',\n    zindex = opts.zindex or 1001,\n    scrolloff = opts.scrolloff or 0,\n    col_offset = opts.col_offset or 0,\n    side_padding = opts.side_padding or 1,\n    scrollbar = opts.scrollbar == nil or opts.scrollbar,\n    max_height = opts.max_height or nil,\n  }\nend\n\nwindow.get_border = function()\n  -- On neovim 0.11+, use the vim.o.winborder option by default\n  local has_winborder, winborder = pcall(function()\n    return vim.o.winborder\n  end)\n  if has_winborder and winborder ~= '' then\n    return winborder\n  end\n\n  -- On lower versions return the default\n  return 'none'\nend\n\nreturn window\n"
  },
  {
    "path": "lua/cmp/config.lua",
    "content": "local mapping = require('cmp.config.mapping')\nlocal cache = require('cmp.utils.cache')\nlocal keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\nlocal api = require('cmp.utils.api')\n\n---@class cmp.Config\n---@field public g cmp.ConfigSchema\nlocal config = {}\n\n---@type cmp.Cache\nconfig.cache = cache.new()\n\n---@type cmp.ConfigSchema\nconfig.global = require('cmp.config.default')()\n\n---@type table<integer, cmp.ConfigSchema>\nconfig.buffers = {}\n\n---@type table<string, cmp.ConfigSchema>\nconfig.filetypes = {}\n\n---@type table<string, cmp.ConfigSchema>\nconfig.cmdline = {}\n\n---@type cmp.ConfigSchema\nconfig.onetime = {}\n\n---Set configuration for global.\n---@param c cmp.ConfigSchema\nconfig.set_global = function(c)\n  config.global = config.normalize(misc.merge(c, config.global))\n  config.global.revision = config.global.revision or 1\n  config.global.revision = config.global.revision + 1\nend\n\n---Set configuration for buffer\n---@param c cmp.ConfigSchema\n---@param bufnr integer\nconfig.set_buffer = function(c, bufnr)\n  local revision = (config.buffers[bufnr] or {}).revision or 1\n  config.buffers[bufnr] = c or {}\n  config.buffers[bufnr].revision = revision + 1\nend\n\n---Set configuration for filetype\n---@param c cmp.ConfigSchema\n---@param filetypes string[]|string\nconfig.set_filetype = function(c, filetypes)\n  for _, filetype in ipairs(type(filetypes) == 'table' and filetypes or { filetypes }) do\n    local revision = (config.filetypes[filetype] or {}).revision or 1\n    config.filetypes[filetype] = c or {}\n    config.filetypes[filetype].revision = revision + 1\n  end\nend\n\n---Set configuration for cmdline\n---@param c cmp.ConfigSchema\n---@param cmdtypes string|string[]\nconfig.set_cmdline = function(c, cmdtypes)\n  for _, cmdtype in ipairs(type(cmdtypes) == 'table' and cmdtypes or { cmdtypes }) do\n    local revision = (config.cmdline[cmdtype] or {}).revision or 1\n    config.cmdline[cmdtype] = c or {}\n    config.cmdline[cmdtype].revision = revision + 1\n  end\nend\n\n---Set configuration as oneshot completion.\n---@param c cmp.ConfigSchema\nconfig.set_onetime = function(c)\n  local revision = (config.onetime or {}).revision or 1\n  config.onetime = c or {}\n  config.onetime.revision = revision + 1\nend\n\n---@return cmp.ConfigSchema\nconfig.get = function()\n  local global_config = config.global\n\n  -- The config object already has `revision` key.\n  if #vim.tbl_keys(config.onetime) > 1 then\n    local onetime_config = config.onetime\n    return config.cache:ensure({\n      'get',\n      'onetime',\n      global_config.revision or 0,\n      onetime_config.revision or 0,\n    }, function()\n      local c = {}\n      c = misc.merge(c, config.normalize(onetime_config))\n      c = misc.merge(c, config.normalize(global_config))\n      return c\n    end)\n  elseif api.is_cmdline_mode() then\n    local cmdtype = vim.fn.getcmdtype()\n    local cmdline_config = config.cmdline[cmdtype] or { revision = 1, sources = {} }\n    return config.cache:ensure({\n      'get',\n      'cmdline',\n      global_config.revision or 0,\n      cmdtype,\n      cmdline_config.revision or 0,\n    }, function()\n      local c = {}\n      c = misc.merge(c, config.normalize(cmdline_config))\n      c = misc.merge(c, config.normalize(global_config))\n      return c\n    end)\n  else\n    local bufnr = vim.api.nvim_get_current_buf()\n    local filetype = vim.api.nvim_get_option_value('filetype', { buf = bufnr })\n    local buffer_config = config.buffers[bufnr] or { revision = 1 }\n    local filetype_config = config.filetypes[filetype] or { revision = 1 }\n    return config.cache:ensure({\n      'get',\n      'default',\n      global_config.revision or 0,\n      filetype,\n      filetype_config.revision or 0,\n      bufnr,\n      buffer_config.revision or 0,\n    }, function()\n      local c = {}\n      c = misc.merge(config.normalize(c), config.normalize(buffer_config))\n      c = misc.merge(config.normalize(c), config.normalize(filetype_config))\n      c = misc.merge(config.normalize(c), config.normalize(global_config))\n      return c\n    end)\n  end\nend\n\n---Return cmp is enabled or not.\nconfig.enabled = function()\n  local enabled = config.get().enabled\n  if type(enabled) == 'function' then\n    enabled = enabled()\n  end\n  return enabled and api.is_suitable_mode()\nend\n\n---Return source config\n---@param name string\n---@return cmp.SourceConfig\nconfig.get_source_config = function(name)\n  local c = config.get()\n  for _, s in ipairs(c.sources) do\n    if s.name == name then\n      return s\n    end\n  end\n  return nil\nend\n\n---Return the current menu is native or not.\nconfig.is_native_menu = function()\n  local c = config.get()\n  if c.view and c.view.entries then\n    return c.view.entries == 'native' or c.view.entries.name == 'native'\n  end\n  return false\nend\n\n---Normalize mapping key\n---@param c any\n---@return cmp.ConfigSchema\nconfig.normalize = function(c)\n  -- make sure c is not 'nil'\n  ---@type any\n  c = c == nil and {} or c\n\n  -- Normalize mapping.\n  if c.mapping then\n    local normalized = {}\n    for k, v in pairs(c.mapping) do\n      normalized[keymap.normalize(k)] = mapping(v, { 'i' })\n    end\n    c.mapping = normalized\n  end\n\n  -- Notice experimental.native_menu.\n  if c.experimental and c.experimental.native_menu then\n    vim.api.nvim_echo({\n      { '[nvim-cmp] ', 'Normal' },\n      { 'experimental.native_menu', 'WarningMsg' },\n      { ' is deprecated.\\n', 'Normal' },\n      { '[nvim-cmp] Please use ', 'Normal' },\n      { 'view.entries = \"native\"', 'WarningMsg' },\n      { ' instead.', 'Normal' },\n    }, true, {})\n\n    c.view = c.view or {}\n    c.view.entries = c.view.entries or 'native'\n  end\n\n  -- Notice documentation.\n  if c.documentation ~= nil then\n    vim.api.nvim_echo({\n      { '[nvim-cmp] ', 'Normal' },\n      { 'documentation', 'WarningMsg' },\n      { ' is deprecated.\\n', 'Normal' },\n      { '[nvim-cmp] Please use ', 'Normal' },\n      { 'window.documentation = cmp.config.window.bordered()', 'WarningMsg' },\n      { ' instead.', 'Normal' },\n    }, true, {})\n    c.window = c.window or {}\n    c.window.documentation = c.documentation\n  end\n\n  -- Notice sources.[n].opts\n  if c.sources then\n    for _, s in ipairs(c.sources) do\n      if s.opts and not s.option then\n        s.option = s.opts\n        s.opts = nil\n        vim.api.nvim_echo({\n          { '[nvim-cmp] ', 'Normal' },\n          { 'sources[number].opts', 'WarningMsg' },\n          { ' is deprecated.\\n', 'Normal' },\n          { '[nvim-cmp] Please use ', 'Normal' },\n          { 'sources[number].option', 'WarningMsg' },\n          { ' instead.', 'Normal' },\n        }, true, {})\n      end\n      s.option = s.option or {}\n    end\n  end\n\n  return c\nend\n\nreturn config\n"
  },
  {
    "path": "lua/cmp/context.lua",
    "content": "local misc = require('cmp.utils.misc')\nlocal pattern = require('cmp.utils.pattern')\nlocal types = require('cmp.types')\nlocal cache = require('cmp.utils.cache')\nlocal api = require('cmp.utils.api')\n\n---@class cmp.Context\n---@field public id string\n---@field public cache cmp.Cache\n---@field public prev_context cmp.Context\n---@field public option cmp.ContextOption\n---@field public filetype string\n---@field public time integer\n---@field public bufnr integer\n---@field public cursor vim.Position|lsp.Position\n---@field public cursor_line string\n---@field public cursor_after_line string\n---@field public cursor_before_line string\n---@field public aborted boolean\nlocal context = {}\n\n---Create new empty context\n---@return cmp.Context\ncontext.empty = function()\n  local ctx = context.new({}) -- dirty hack to prevent recursive call `context.empty`.\n  ctx.bufnr = -1\n  ctx.input = ''\n  ctx.cursor = {}\n  ctx.cursor.row = -1\n  ctx.cursor.col = -1\n  return ctx\nend\n\n---Create new context\n---@param prev_context? cmp.Context\n---@param option? cmp.ContextOption\n---@return cmp.Context\ncontext.new = function(prev_context, option)\n  option = option or {}\n\n  local self = setmetatable({}, { __index = context })\n  self.id = misc.id('cmp.context.new')\n  self.cache = cache.new()\n  self.prev_context = prev_context or context.empty()\n  self.option = option or { reason = types.cmp.ContextReason.None }\n  self.filetype = vim.api.nvim_get_option_value('filetype', { buf = 0 })\n  self.time = vim.loop.now()\n  self.bufnr = vim.api.nvim_get_current_buf()\n\n  local cursor = api.get_cursor()\n  self.cursor_line = api.get_current_line()\n  self.cursor = {}\n  self.cursor.row = cursor[1]\n  self.cursor.col = cursor[2] + 1\n  self.cursor.line = self.cursor.row - 1\n  self.cursor.character = misc.to_utfindex(self.cursor_line, self.cursor.col)\n  self.cursor_before_line = string.sub(self.cursor_line, 1, self.cursor.col - 1)\n  self.cursor_after_line = string.sub(self.cursor_line, self.cursor.col)\n  self.aborted = false\n  return self\nend\n\ncontext.abort = function(self)\n  self.aborted = true\nend\n\n---Return context creation reason.\n---@return cmp.ContextReason\ncontext.get_reason = function(self)\n  return self.option.reason\nend\n\n---Get keyword pattern offset\n---@return integer\ncontext.get_offset = function(self, keyword_pattern)\n  return self.cache:ensure({ 'get_offset', keyword_pattern, self.cursor_before_line }, function()\n    return pattern.offset([[\\%(]] .. keyword_pattern .. [[\\)\\m$]], self.cursor_before_line) or self.cursor.col\n  end)\nend\n\n---Return if this context is changed from previous context or not.\n---@return boolean\ncontext.changed = function(self, ctx)\n  local curr = self\n\n  if curr.bufnr ~= ctx.bufnr then\n    return true\n  end\n  if curr.cursor.row ~= ctx.cursor.row then\n    return true\n  end\n  if curr.cursor.col ~= ctx.cursor.col then\n    return true\n  end\n  if curr:get_reason() == types.cmp.ContextReason.Manual then\n    return true\n  end\n\n  return false\nend\n\n---Shallow clone\ncontext.clone = function(self)\n  local cloned = {}\n  for k, v in pairs(self) do\n    cloned[k] = v\n  end\n  return cloned\nend\n\nreturn context\n"
  },
  {
    "path": "lua/cmp/context_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\n\nlocal context = require('cmp.context')\n\ndescribe('context', function()\n  before_each(spec.before)\n\n  describe('new', function()\n    it('middle of text', function()\n      vim.fn.setline('1', 'function! s:name() abort')\n      vim.bo.filetype = 'vim'\n      vim.fn.execute('normal! fm')\n      local ctx = context.new()\n      assert.are.equal(ctx.filetype, 'vim')\n      assert.are.equal(ctx.cursor.row, 1)\n      assert.are.equal(ctx.cursor.col, 15)\n      assert.are.equal(ctx.cursor_line, 'function! s:name() abort')\n    end)\n\n    it('tab indent', function()\n      vim.fn.setline('1', '\\t\\tab')\n      vim.bo.filetype = 'vim'\n      vim.fn.execute('normal! fb')\n      local ctx = context.new()\n      assert.are.equal(ctx.filetype, 'vim')\n      assert.are.equal(ctx.cursor.row, 1)\n      assert.are.equal(ctx.cursor.col, 4)\n      assert.are.equal(ctx.cursor_line, '\\t\\tab')\n    end)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/core.lua",
    "content": "local debug = require('cmp.utils.debug')\nlocal str = require('cmp.utils.str')\nlocal char = require('cmp.utils.char')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal async = require('cmp.utils.async')\nlocal keymap = require('cmp.utils.keymap')\nlocal context = require('cmp.context')\nlocal source = require('cmp.source')\nlocal view = require('cmp.view')\nlocal misc = require('cmp.utils.misc')\nlocal config = require('cmp.config')\nlocal types = require('cmp.types')\nlocal api = require('cmp.utils.api')\nlocal event = require('cmp.utils.event')\n\n---@class cmp.Core\n---@field public suspending boolean\n---@field public view cmp.View\n---@field public sources cmp.Source[]\n---@field public context cmp.Context\n---@field public event cmp.Event\nlocal core = {}\n\ncore.new = function()\n  local self = setmetatable({}, { __index = core })\n  self.suspending = false\n  self.sources = {}\n  self.context = context.new()\n  self.event = event.new()\n  self.view = view.new()\n  self.view.event:on('keymap', function(...)\n    self:on_keymap(...)\n  end)\n  for _, event_name in ipairs({ 'complete_done', 'menu_opened', 'menu_closed' }) do\n    self.view.event:on(event_name, function(evt)\n      self.event:emit(event_name, evt)\n    end)\n  end\n  return self\nend\n\n---Register source\n---@param s cmp.Source\ncore.register_source = function(self, s)\n  self.sources[s.id] = s\nend\n\n---Unregister source\n---@param source_id integer\n---@return cmp.Source?\ncore.unregister_source = function(self, source_id)\n  local s = self.sources[source_id]\n  self.sources[source_id] = nil\n  return s\nend\n\n---Get new context\n---@param option? cmp.ContextOption\n---@return cmp.Context\ncore.get_context = function(self, option)\n  self.context:abort()\n  local prev = self.context:clone()\n  prev.prev_context = nil\n  prev.cache = nil\n  local ctx = context.new(prev, option)\n  self:set_context(ctx)\n  return self.context\nend\n\n---Set new context\n---@param ctx cmp.Context\ncore.set_context = function(self, ctx)\n  self.context = ctx\nend\n\n---Suspend completion\ncore.suspend = function(self)\n  self.suspending = true\n  -- It's needed to avoid conflicting with autocmd debouncing.\n  return vim.schedule_wrap(function()\n    self.suspending = false\n  end)\nend\n\n---Get sources that sorted by priority\n---@param filter? cmp.SourceStatus[]|fun(s: cmp.Source): boolean\n---@return cmp.Source[]\ncore.get_sources = function(self, filter)\n  local f = function(s)\n    if type(filter) == 'table' then\n      return vim.tbl_contains(filter, s.status)\n    elseif type(filter) == 'function' then\n      return filter(s)\n    end\n    return true\n  end\n\n  local sources = {}\n  for _, c in pairs(config.get().sources) do\n    for _, s in pairs(self.sources) do\n      if c.name == s.name then\n        if s:is_available() and f(s) then\n          table.insert(sources, s)\n        end\n      end\n    end\n  end\n  return sources\nend\n\n---Return registered sources.\n---@return cmp.Source[]\ncore.get_registered_sources = function(self)\n  return self.sources\nend\n\n---Keypress handler\ncore.on_keymap = function(self, keys, fallback)\n  local mode = api.get_mode()\n  for key, mapping in pairs(config.get().mapping) do\n    if keymap.equals(key, keys) and mapping[mode] then\n      return mapping[mode](fallback)\n    end\n  end\n\n  --Commit character. NOTE: This has a lot of cmp specific implementation to make more user-friendly.\n  local chars = keymap.t(keys)\n  local e = self.view:get_active_entry()\n  if e and vim.tbl_contains(config.get().confirmation.get_commit_characters(e:get_commit_characters()), chars) then\n    local is_printable = char.is_printable(string.byte(chars, 1))\n    self:confirm(e, {\n      behavior = is_printable and 'insert' or 'replace',\n      commit_character = chars,\n    }, function()\n      local ctx = self:get_context()\n      local word = e.word\n      if string.sub(ctx.cursor_before_line, -#word, ctx.cursor.col - 1) == word and is_printable then\n        fallback()\n      else\n        self:reset()\n      end\n    end)\n    return\n  end\n\n  fallback()\nend\n\n---Prepare completion\ncore.prepare = function(self)\n  for keys, mapping in pairs(config.get().mapping) do\n    for mode in pairs(mapping) do\n      keymap.listen(mode, keys, function(...)\n        self:on_keymap(...)\n      end)\n    end\n  end\nend\n\n---Check auto-completion\ncore.on_change = function(self, trigger_event)\n  local ignore = false\n  ignore = ignore or self.suspending\n  ignore = ignore or (vim.fn.pumvisible() == 1 and (vim.v.completed_item).word)\n  ignore = ignore or not self.view:ready()\n  if ignore then\n    self:get_context({ reason = types.cmp.ContextReason.Auto })\n    return\n  end\n  self:autoindent(trigger_event, function()\n    local ctx = self:get_context({ reason = types.cmp.ContextReason.Auto })\n    debug.log(('ctx: `%s`'):format(ctx.cursor_before_line))\n    if ctx:changed(ctx.prev_context) then\n      self.view:on_change()\n      debug.log('changed')\n\n      if vim.tbl_contains(config.get().completion.autocomplete or {}, trigger_event) then\n        self:complete(ctx)\n      else\n        self.filter.timeout = self.view:visible() and config.get().performance.throttle or 0\n        self:filter()\n      end\n    else\n      debug.log('unchanged')\n    end\n  end)\nend\n\n---Cursor moved.\ncore.on_moved = function(self)\n  local ignore = false\n  ignore = ignore or self.suspending\n  ignore = ignore or (vim.fn.pumvisible() == 1 and (vim.v.completed_item).word)\n  ignore = ignore or not self.view:visible()\n  if ignore then\n    return\n  end\n  self:filter()\nend\n\n---Returns the suffix of the specified `line`.\n---\n---Contains `%s`: returns everything after the last `%s` in `line`\n---Else:          returns `line` unmodified\n---@param line string\n---@return string suffix\nlocal function find_line_suffix(line)\n  return line:match('%S*$') --[[@as string]]\nend\n\n---Check autoindent\n---@param trigger_event cmp.TriggerEvent\n---@param callback function\ncore.autoindent = function(self, trigger_event, callback)\n  if trigger_event ~= types.cmp.TriggerEvent.TextChanged then\n    return callback()\n  end\n  if not api.is_insert_mode() then\n    return callback()\n  end\n\n  -- Check prefix\n  local cursor_before_line = api.get_cursor_before_line()\n  local prefix = find_line_suffix(cursor_before_line) or ''\n  if #prefix == 0 then\n    return callback()\n  end\n\n  -- Reset current completion if indentkeys matched.\n  for _, key in ipairs(vim.split(vim.bo.indentkeys, ',')) do\n    if vim.tbl_contains({ '=' .. prefix, '0=' .. prefix }, key) then\n      self:reset()\n      self:set_context(context.empty())\n      break\n    end\n  end\n\n  callback()\nend\n\n---Complete common string for current completed entries.\ncore.complete_common_string = function(self)\n  if not self.view:visible() or self.view:get_selected_entry() then\n    return false\n  end\n\n  config.set_onetime({\n    sources = config.get().sources,\n    matching = {\n      disallow_prefix_unmatching = true,\n      disallow_partial_matching = true,\n      disallow_fuzzy_matching = true,\n    },\n  })\n\n  self:filter()\n  self.filter:sync(1000)\n\n  config.set_onetime({})\n\n  local cursor = api.get_cursor()\n  local offset = self.view:get_offset() or cursor[2]\n  local common_string\n  for _, e in ipairs(self.view:get_entries()) do\n    local vim_item = e:get_vim_item(offset)\n    if not common_string then\n      common_string = vim_item.word\n    else\n      common_string = str.get_common_string(common_string, vim_item.word)\n    end\n  end\n  local cursor_before_line = api.get_cursor_before_line()\n  local pretext = cursor_before_line:sub(offset)\n  if common_string and #common_string > #pretext then\n    feedkeys.call(keymap.backspace(pretext) .. common_string, 'n')\n    return true\n  end\n  return false\nend\n\n---Invoke completion\n---@param ctx cmp.Context\ncore.complete = function(self, ctx)\n  if not api.is_suitable_mode() then\n    return\n  end\n\n  self:set_context(ctx)\n\n  -- Invoke completion sources.\n  local sources = self:get_sources()\n  for _, s in ipairs(sources) do\n    local callback\n    callback = (function(s_)\n      return function()\n        local new = context.new(ctx)\n        if s_.incomplete and new:changed(s_.context) then\n          s_:complete(new, callback)\n        else\n          if not self.view:get_active_entry() then\n            self.filter.stop()\n            self.filter.timeout = config.get().performance.debounce\n            self:filter()\n          end\n        end\n      end\n    end)(s)\n    s:complete(ctx, callback)\n  end\n\n  if not self.view:get_active_entry() then\n    self.filter.timeout = self.view:visible() and config.get().performance.throttle or 1\n    self:filter()\n  end\nend\n\n---Update completion menu\nlocal async_filter = async.wrap(function(self)\n  self.filter.timeout = config.get().performance.throttle\n\n  -- Check invalid condition.\n  local ignore = false\n  ignore = ignore or not api.is_suitable_mode()\n  if ignore then\n    return\n  end\n\n  -- Check fetching sources.\n  local sources = {}\n  for _, s in ipairs(self:get_sources({ source.SourceStatus.FETCHING, source.SourceStatus.COMPLETED })) do\n    -- Reserve filter call for timeout.\n    if not s.incomplete and config.get().performance.fetching_timeout > s:get_fetching_time() then\n      self.filter.timeout = config.get().performance.fetching_timeout - s:get_fetching_time()\n      self:filter()\n      if #sources == 0 then\n        return\n      end\n    end\n    table.insert(sources, s)\n  end\n\n  local ctx = self:get_context()\n\n  -- Display completion results.\n  local did_open = self.view:open(ctx, sources)\n  local fetching = #self:get_sources(function(s)\n    return s.status == source.SourceStatus.FETCHING\n  end)\n\n  -- Check onetime config.\n  if not did_open and fetching == 0 then\n    config.set_onetime({})\n  end\nend)\ncore.filter = async.throttle(async_filter, config.get().performance.throttle)\n\n---Confirm completion.\n---@param e cmp.Entry\n---@param option cmp.ConfirmOption\n---@param callback function\ncore.confirm = function(self, e, option, callback)\n  if not (e and not e.confirmed) then\n    if callback then\n      callback()\n    end\n    return\n  end\n  e.confirmed = true\n\n  debug.log('entry.confirm', e.completion_item)\n\n  async.sync(function(done)\n    e:resolve(done)\n  end, config.get().performance.confirm_resolve_timeout)\n\n  local release = self:suspend()\n\n  -- Close menus.\n  self.view:close()\n\n  feedkeys.call(keymap.indentkeys(), 'n')\n  feedkeys.call('', 'n', function()\n    -- Emulate `<C-y>` behavior to save `.` register.\n    local ctx = context.new()\n    local keys = {}\n    table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))\n    table.insert(keys, e.word)\n    table.insert(keys, keymap.undobreak())\n    feedkeys.call(table.concat(keys, ''), 'in')\n  end)\n  feedkeys.call('', 'n', function()\n    -- Restore the line at the time of request.\n    local ctx = context.new()\n    if api.is_cmdline_mode() then\n      local keys = {}\n      table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))\n      table.insert(keys, string.sub(e.context.cursor_before_line, e.offset))\n      feedkeys.call(table.concat(keys, ''), 'in')\n    else\n      vim.cmd([[silent! undojoin]])\n      -- This logic must be used nvim_buf_set_text.\n      -- If not used, the snippet engine's placeholder will be broken.\n      vim.api.nvim_buf_set_text(0, e.context.cursor.row - 1, e.offset - 1, ctx.cursor.row - 1, ctx.cursor.col - 1, {\n        e.context.cursor_before_line:sub(e.offset),\n      })\n      vim.api.nvim_win_set_cursor(0, { e.context.cursor.row, e.context.cursor.col - 1 })\n    end\n  end)\n  feedkeys.call('', 'n', function()\n    -- Apply additionalTextEdits.\n    local ctx = context.new()\n    if #(e.completion_item.additionalTextEdits or {}) == 0 then\n      e:resolve(function()\n        local new = context.new()\n        local text_edits = e.completion_item.additionalTextEdits or {}\n        if #text_edits == 0 then\n          return\n        end\n\n        local has_cursor_line_text_edit = (function()\n          local minrow = math.min(ctx.cursor.row, new.cursor.row)\n          local maxrow = math.max(ctx.cursor.row, new.cursor.row)\n          for _, te in ipairs(text_edits) do\n            local srow = te.range.start.line + 1\n            local erow = te.range['end'].line + 1\n            if srow <= minrow and maxrow <= erow then\n              return true\n            end\n          end\n          return false\n        end)()\n        if has_cursor_line_text_edit then\n          return\n        end\n        vim.cmd([[silent! undojoin]])\n        api.apply_text_edits(text_edits, ctx.bufnr, e.source:get_position_encoding_kind())\n      end)\n    else\n      vim.cmd([[silent! undojoin]])\n      api.apply_text_edits(e.completion_item.additionalTextEdits, ctx.bufnr, e.source:get_position_encoding_kind())\n    end\n  end)\n  feedkeys.call('', 'n', function()\n    local ctx = context.new()\n    local completion_item = misc.copy(e.completion_item)\n    if not completion_item.textEdit then\n      completion_item.textEdit = {}\n      local insertText = completion_item.insertText\n      if misc.empty(insertText) then\n        insertText = nil\n      end\n      completion_item.textEdit.newText = insertText or completion_item.word or completion_item.label\n    end\n    local behavior = option.behavior or config.get().confirmation.default_behavior\n    if behavior == types.cmp.ConfirmBehavior.Replace then\n      completion_item.textEdit.range = e.replace_range\n    else\n      completion_item.textEdit.range = e.insert_range\n    end\n\n    local diff_before = math.max(0, e.context.cursor.col - (completion_item.textEdit.range.start.character + 1))\n    local diff_after = math.max(0, (completion_item.textEdit.range['end'].character + 1) - e.context.cursor.col)\n    local new_text = completion_item.textEdit.newText\n    completion_item.textEdit.range.start.line = ctx.cursor.line\n    completion_item.textEdit.range.start.character = (ctx.cursor.col - 1) - diff_before\n    completion_item.textEdit.range['end'].line = ctx.cursor.line\n    completion_item.textEdit.range['end'].character = (ctx.cursor.col - 1) + diff_after\n    if api.is_insert_mode() then\n      if false then\n        --To use complex expansion debug.\n        vim.print({ -- luacheck: ignore\n          item = e.completion_item,\n          diff_before = diff_before,\n          diff_after = diff_after,\n          new_text = new_text,\n          text_edit_new_text = completion_item.textEdit.newText,\n          range_start = completion_item.textEdit.range.start.character,\n          range_end = completion_item.textEdit.range['end'].character,\n          original_range_start = e.completion_item.textEdit.range.start.character,\n          original_range_end = e.completion_item.textEdit.range['end'].character,\n          cursor_line = ctx.cursor_line,\n          cursor_col0 = ctx.cursor.col - 1,\n        })\n      end\n      local is_snippet = completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet\n      if is_snippet then\n        completion_item.textEdit.newText = ''\n      end\n      api.apply_text_edits({ completion_item.textEdit }, ctx.bufnr, 'utf-8')\n\n      local texts = vim.split(completion_item.textEdit.newText, '\\n')\n      vim.api.nvim_win_set_cursor(0, {\n        completion_item.textEdit.range.start.line + #texts,\n        (#texts == 1 and (completion_item.textEdit.range.start.character + #texts[1]) or #texts[#texts]),\n      })\n      if is_snippet then\n        config.get().snippet.expand({\n          body = new_text,\n          insert_text_mode = completion_item.insertTextMode,\n        })\n      end\n    else\n      local keys = {}\n      table.insert(keys, keymap.backspace(ctx.cursor_line:sub(completion_item.textEdit.range.start.character + 1, ctx.cursor.col - 1)))\n      table.insert(keys, keymap.delete(ctx.cursor_line:sub(ctx.cursor.col, completion_item.textEdit.range['end'].character)))\n      table.insert(keys, new_text)\n      feedkeys.call(table.concat(keys, ''), 'in')\n    end\n  end)\n  feedkeys.call(keymap.indentkeys(vim.bo.indentkeys), 'n')\n  feedkeys.call('', 'n', function()\n    e:execute(vim.schedule_wrap(function()\n      release()\n      self.event:emit('confirm_done', {\n        entry = e,\n        commit_character = option.commit_character,\n      })\n      if callback then\n        callback()\n      end\n    end))\n  end)\nend\n\n---Reset current completion state\ncore.reset = function(self)\n  for _, s in pairs(self.sources) do\n    s:reset()\n  end\n  self.context = context.empty()\nend\n\nreturn core\n"
  },
  {
    "path": "lua/cmp/core_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal types = require('cmp.types')\nlocal core = require('cmp.core')\nlocal source = require('cmp.source')\nlocal keymap = require('cmp.utils.keymap')\nlocal api = require('cmp.utils.api')\n\ndescribe('cmp.core', function()\n  describe('confirm', function()\n    ---@param request string\n    ---@param filter string\n    ---@param completion_item lsp.CompletionItem\n    ---@param option? { position_encoding_kind: lsp.PositionEncodingKind }\n    ---@return table\n    local confirm = function(request, filter, completion_item, option)\n      option = option or {}\n\n      local c = core.new()\n      local s = source.new('spec', {\n        get_position_encoding_kind = function()\n          return option.position_encoding_kind or types.lsp.PositionEncodingKind.UTF16\n        end,\n        complete = function(_, _, callback)\n          callback({ completion_item })\n        end,\n      })\n      c:register_source(s)\n      feedkeys.call(request, 'n', function()\n        c:complete(c:get_context({ reason = types.cmp.ContextReason.Manual }))\n        vim.wait(5000, function()\n          return #c.sources[s.id].entries > 0\n        end)\n      end)\n      feedkeys.call(filter, 'n', function()\n        c:confirm(c.sources[s.id].entries[1], {}, function() end)\n      end)\n      local state = {}\n      feedkeys.call('', 'x', function()\n        feedkeys.call('', 'n', function()\n          if api.is_cmdline_mode() then\n            state.buffer = { api.get_current_line() }\n          else\n            state.buffer = vim.api.nvim_buf_get_lines(0, 0, -1, false)\n          end\n          state.cursor = api.get_cursor()\n        end)\n      end)\n      return state\n    end\n\n    describe('insert-mode', function()\n      before_each(spec.before)\n\n      it('label', function()\n        local state = confirm('iA', 'IU', {\n          label = 'AIUEO',\n        })\n        assert.are.same(state.buffer, { 'AIUEO' })\n        assert.are.same(state.cursor, { 1, 5 })\n      end)\n\n      it('insertText', function()\n        local state = confirm('iA', 'IU', {\n          label = 'AIUEO',\n          insertText = '_AIUEO_',\n        })\n        assert.are.same(state.buffer, { '_AIUEO_' })\n        assert.are.same(state.cursor, { 1, 7 })\n      end)\n\n      it('textEdit', function()\n        local state = confirm(keymap.t('i***AEO***<Left><Left><Left><Left><Left>'), 'IU', {\n          label = 'AIUEO',\n          textEdit = {\n            range = {\n              start = {\n                line = 0,\n                character = 3,\n              },\n              ['end'] = {\n                line = 0,\n                character = 6,\n              },\n            },\n            newText = 'foo\\nbar\\nbaz',\n          },\n        })\n        assert.are.same(state.buffer, { '***foo', 'bar', 'baz***' })\n        assert.are.same(state.cursor, { 3, 3 })\n      end)\n\n      it('#1552', function()\n        local state = confirm(keymap.t('ios.'), '', {\n          filterText = 'IsPermission',\n          insertTextFormat = 2,\n          label = 'IsPermission',\n          textEdit = {\n            newText = 'IsPermission($0)',\n            range = {\n              ['end'] = {\n                character = 3,\n                line = 0,\n              },\n              start = {\n                character = 3,\n                line = 0,\n              },\n            },\n          },\n        })\n        assert.are.same(state.buffer, { 'os.IsPermission()' })\n        assert.are.same(state.cursor, { 1, 16 })\n      end)\n\n      it('insertText & snippet', function()\n        local state = confirm('iA', 'IU', {\n          label = 'AIUEO',\n          insertText = 'AIUEO($0)',\n          insertTextFormat = types.lsp.InsertTextFormat.Snippet,\n        })\n        assert.are.same(state.buffer, { 'AIUEO()' })\n        assert.are.same(state.cursor, { 1, 6 })\n      end)\n\n      it('textEdit & snippet', function()\n        local state = confirm(keymap.t('i***AEO***<Left><Left><Left><Left><Left>'), 'IU', {\n          label = 'AIUEO',\n          insertTextFormat = types.lsp.InsertTextFormat.Snippet,\n          textEdit = {\n            range = {\n              start = {\n                line = 0,\n                character = 3,\n              },\n              ['end'] = {\n                line = 0,\n                character = 6,\n              },\n            },\n            newText = 'foo\\nba$0r\\nbaz',\n          },\n        })\n        assert.are.same(state.buffer, { '***foo', 'bar', 'baz***' })\n        assert.are.same(state.cursor, { 2, 2 })\n      end)\n\n      local char = '🗿'\n      for _, case in ipairs({\n        {\n          encoding = types.lsp.PositionEncodingKind.UTF8,\n          char_size = #char,\n        },\n        {\n          encoding = types.lsp.PositionEncodingKind.UTF16,\n          char_size = select(2, vim.str_utfindex(char)),\n        },\n        {\n          encoding = types.lsp.PositionEncodingKind.UTF32,\n          char_size = select(1, vim.str_utfindex(char)),\n        },\n      }) do\n        it('textEdit & multibyte: ' .. case.encoding, function()\n          local state = confirm(keymap.t('i%s:%s%s:%s<Left><Left><Left>'):format(char, char, char, char), char, {\n            label = char .. char .. char,\n            textEdit = {\n              range = {\n                start = {\n                  line = 0,\n                  character = case.char_size + #':',\n                },\n                ['end'] = {\n                  line = 0,\n                  character = case.char_size + #':' + case.char_size + case.char_size,\n                },\n              },\n              newText = char .. char .. char .. char .. char,\n            },\n          }, {\n            position_encoding_kind = case.encoding,\n          })\n          vim.print({ state = state, case = case })\n          assert.are.same(state.buffer, { ('%s:%s%s%s%s%s:%s'):format(char, char, char, char, char, char, char) })\n          assert.are.same(state.cursor, { 1, #('%s:%s%s%s%s%s'):format(char, char, char, char, char, char) })\n        end)\n      end\n    end)\n\n    describe('cmdline-mode', function()\n      before_each(spec.before)\n\n      it('label', function()\n        local state = confirm(':A', 'IU', {\n          label = 'AIUEO',\n        })\n        assert.are.same(state.buffer, { 'AIUEO' })\n        assert.are.same(state.cursor[2], 5)\n      end)\n\n      it('insertText', function()\n        local state = confirm(':A', 'IU', {\n          label = 'AIUEO',\n          insertText = '_AIUEO_',\n        })\n        assert.are.same(state.buffer, { '_AIUEO_' })\n        assert.are.same(state.cursor[2], 7)\n      end)\n\n      it('textEdit', function()\n        local state = confirm(keymap.t(':***AEO***<Left><Left><Left><Left><Left>'), 'IU', {\n          label = 'AIUEO',\n          textEdit = {\n            range = {\n              start = {\n                line = 0,\n                character = 3,\n              },\n              ['end'] = {\n                line = 0,\n                character = 6,\n              },\n            },\n            newText = 'AIUEO',\n          },\n        })\n        assert.are.same(state.buffer, { '***AIUEO***' })\n        assert.are.same(state.cursor[2], 6)\n      end)\n    end)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/entry.lua",
    "content": "local cache = require('cmp.utils.cache')\nlocal char = require('cmp.utils.char')\nlocal misc = require('cmp.utils.misc')\nlocal str = require('cmp.utils.str')\nlocal snippet = require('cmp.utils.snippet')\nlocal config = require('cmp.config')\nlocal types = require('cmp.types')\nlocal matcher = require('cmp.matcher')\nlocal ok, lspkind = pcall(require, 'lspkind')\n\nlocal function get_icon(kind)\n  if ok then\n    local icon = lspkind.symbol_map[kind]\n    return icon\n  end\n\n  return ''\nend\n\n---@class cmp.Entry\n---@field public id integer\n---@field public cache cmp.Cache\n---@field public match_cache cmp.Cache\n---@field public score integer\n---@field public exact boolean\n---@field public matches table\n---@field public context cmp.Context\n---@field public source cmp.Source\n---@field public source_offset integer\n---@field public source_insert_range lsp.Range\n---@field public source_replace_range lsp.Range\n---@field public completion_item lsp.CompletionItem\n---@field public item_defaults? lsp.internal.CompletionItemDefaults\n---@field public resolved_completion_item lsp.CompletionItem|nil\n---@field public resolved_callbacks fun()[]\n---@field public resolving boolean\n---@field public confirmed boolean\n---@field public insert_range lsp.Range\n---@field public replace_range lsp.Range\n---@field public offset integer\n---@field public word string\n---@field public filter_text string\n---@field private match_view_args_ret {input:string, word:string, option:cmp.MatchingConfig, matches:table[]}\nlocal entry = {}\nentry.__index = entry\n\n---Create new entry\n---@param ctx cmp.Context\n---@param source cmp.Source\n---@param completion_item lsp.CompletionItem\n---@param item_defaults? lsp.internal.CompletionItemDefaults\n---@return cmp.Entry\nentry.new = function(ctx, source, completion_item, item_defaults)\n  local self = setmetatable({}, entry)\n  self.id = misc.id('entry.new')\n  self.cache = cache.new()\n  self.match_cache = cache.new()\n  self.score = 0\n  self.exact = false\n  self.matches = {}\n  self.context = ctx\n  self.source = source\n  self.offset = source.request_offset\n  self.source_offset = source.request_offset\n  self.source_insert_range = source.default_insert_range\n  self.source_replace_range = source.default_replace_range\n  self.item_defaults = item_defaults\n  self.resolved_completion_item = nil\n  self.resolved_callbacks = {}\n  self.resolving = false\n  self.confirmed = false\n  self:_set_completion_item(completion_item)\n  return self\nend\n\n---@package\nentry._set_completion_item = function(self, completion_item)\n  if not self.completion_item then\n    self.completion_item = self:fill_defaults(completion_item, self.item_defaults)\n  else\n    -- @see https://github.com/microsoft/vscode/blob/85eea4a9b2ccc99615e970bf2181edbc1781d0f9/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts#L588\n    -- @see https://github.com/microsoft/vscode/blob/85eea4a9b2ccc99615e970bf2181edbc1781d0f9/src/vs/base/common/objects.ts#L89\n    -- @see https://github.com/microsoft/vscode/blob/a00f2e64f4fa9a1f774875562e1e9697d7138ed3/src/vs/editor/contrib/suggest/browser/suggest.ts#L147\n    for k, v in pairs(completion_item) do\n      self.completion_item[k] = v or self.completion_item[k]\n    end\n  end\n\n  local item = self.completion_item\n\n  ---Create filter text\n  self.filter_text = item.filterText or str.trim(item.label)\n\n  -- TODO: the order below is important\n  if item.textEdit then\n    self.insert_range = self:convert_range_encoding(item.textEdit.insert or item.textEdit.range)\n    self.replace_range = self:convert_range_encoding(item.textEdit.replace or item.textEdit.range)\n  end\n\n  self.word = self:_get_word()\n  self.offset = self:_get_offset()\n\n  if not self.insert_range then\n    self.insert_range = {\n      start = {\n        line = self.context.cursor.row - 1,\n        character = self.offset - 1,\n      },\n      ['end'] = self.source_insert_range['end'],\n    }\n  end\n\n  if not self.replace_range or ((self.context.cursor.col - 1) == self.replace_range['end'].character) then\n    self.replace_range = {\n      start = {\n        line = self.source_replace_range.start.line,\n        character = self.offset - 1,\n      },\n      ['end'] = self.source_replace_range['end'],\n    }\n  end\nend\n\n---@deprecated use entry.offset instead\nentry.get_offset = function(self)\n  return self.offset\nend\n\n---Make offset value\n---@package\n---@return integer\nentry._get_offset = function(self)\n  local offset = self.source_offset\n  if self.completion_item.textEdit then\n    local range = self.insert_range\n    if range then\n      local start = math.min(range.start.character + 1, offset)\n      for idx = start, self.source_offset do\n        local byte = string.byte(self.context.cursor_line, idx)\n        if byte == nil or not char.is_white(byte) then\n          return idx\n        end\n      end\n      return offset\n    end\n  else\n    -- NOTE\n    -- The VSCode does not implement this but it's useful if the server does not care about word patterns.\n    -- We should care about this performance.\n    local word = self.word\n    for idx = self.source_offset - 1, self.source_offset - #word, -1 do\n      if char.is_semantic_index(self.context.cursor_line, idx) then\n        local c = string.byte(self.context.cursor_line, idx)\n        if char.is_white(c) then\n          break\n        end\n        local match = true\n        for i = 1, self.source_offset - idx do\n          local c1 = string.byte(word, i)\n          local c2 = string.byte(self.context.cursor_line, idx + i - 1)\n          if not c1 or not c2 or c1 ~= c2 then\n            match = false\n            break\n          end\n        end\n        if match then\n          offset = math.min(offset, idx)\n        end\n      end\n    end\n  end\n  return offset\nend\n\n---@deprecated use entry.word instead\nentry.get_word = function(self)\n  return self.word\nend\n\n---Create word for vim.CompletedItem\n---NOTE: This method doesn't clear the cache after completionItem/resolve.\n---@package\n---@return string\nentry._get_word = function(self)\n  --NOTE: This is nvim-cmp specific implementation.\n  local completion_item = self.completion_item\n  if completion_item.word then\n    return completion_item.word\n  end\n\n  local word\n  if completion_item.textEdit and not misc.empty(completion_item.textEdit.newText) then\n    word = str.trim(completion_item.textEdit.newText)\n    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n      word = tostring(snippet.parse(word))\n    end\n    local overwrite = self:get_overwrite()\n    if 0 < overwrite[2] or completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n      word = str.get_word(word, string.byte(self.context.cursor_after_line, 1), overwrite[1] or 0)\n    end\n  elseif not misc.empty(completion_item.insertText) then\n    word = str.trim(completion_item.insertText)\n    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n      word = str.get_word(tostring(snippet.parse(word)))\n    end\n  else\n    word = str.trim(completion_item.label)\n  end\n  return str.oneline(word)\nend\n\n---Get overwrite information\n---@return integer[]\nentry.get_overwrite = function(self)\n  return self.cache:ensure('get_overwrite', entry._get_overwrite, self)\nend\n\n---@package\nentry._get_overwrite = function(self)\n  if self.completion_item.textEdit then\n    local range = self.insert_range\n    if range then\n      local vim_start = range.start.character + 1\n      local vim_end = range['end'].character + 1\n      local before = self.context.cursor.col - vim_start\n      local after = vim_end - self.context.cursor.col\n      return { before, after }\n    end\n  end\n  return { 0, 0 }\nend\n\n---@package\nentry.get_filter_text = function(self)\n  return self.filter_text\nend\n\n---Get LSP's insert text\n---@return string\nentry.get_insert_text = function(self)\n  return self.cache:ensure('get_insert_text', entry._get_insert_text, self)\nend\n\n---@package\nentry._get_insert_text = function(self)\n  local completion_item = self.completion_item\n  local word\n  if completion_item.textEdit then\n    word = str.trim(completion_item.textEdit.newText)\n    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n      word = str.remove_suffix(str.remove_suffix(word, '$0'), '${0}')\n    end\n  elseif completion_item.insertText then\n    word = str.trim(completion_item.insertText)\n    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n      word = str.remove_suffix(str.remove_suffix(word, '$0'), '${0}')\n    end\n  else\n    word = str.trim(completion_item.label)\n  end\n  return word\nend\n\n---Return the item is deprecated or not.\n---@return boolean\nentry.is_deprecated = function(self)\n  return self.completion_item.deprecated or vim.tbl_contains(self.completion_item.tags or {}, types.lsp.CompletionItemTag.Deprecated)\nend\n\n---Return view information.\n---@param suggest_offset integer\n---@param entries_buf integer The buffer this entry will be rendered into.\n---@return { abbr: { text: string, bytes: integer, width: integer, hl_group: string|table }, icon: { text: string, bytes: integer, width: integer, hl_group: string|table }, kind: { text: string, bytes: integer, width: integer, hl_group: string|table }, menu: { text: string, bytes: integer, width: integer, hl_group: string|table } }\nentry.get_view = function(self, suggest_offset, entries_buf)\n  local item = self:get_vim_item(suggest_offset)\n  return self.cache:ensure('get_view:' .. tostring(entries_buf), entry._get_view, self, item, entries_buf)\nend\n\n---@package\nentry._get_view = function(self, item, entries_buf)\n  local view = {}\n  -- The result of vim.fn.strdisplaywidth depends on which buffer it was\n  -- called in because it reads the values of the option 'tabstop' when\n  -- rendering <Tab> characters.\n  vim.api.nvim_buf_call(entries_buf, function()\n    view.abbr = {}\n    view.abbr.text = item.abbr or ''\n    view.abbr.bytes = #view.abbr.text\n    view.abbr.width = vim.fn.strdisplaywidth(view.abbr.text)\n    view.abbr.hl_group = item.abbr_hl_group or (self:is_deprecated() and 'CmpItemAbbrDeprecated' or 'CmpItemAbbr')\n    view.icon = {}\n    view.icon.text = item.icon or get_icon(types.lsp.CompletionItemKind[self:get_kind()])\n    view.icon.bytes = #view.icon.text\n    view.icon.width = vim.fn.strdisplaywidth(view.icon.text)\n    view.icon.hl_group = item.icon_hl_group or (('CmpItemKind' .. (types.lsp.CompletionItemKind[self:get_kind()] or '') .. 'Icon') or 'CmpItemKind')\n    view.kind = {}\n    view.kind.text = item.kind or ''\n    view.kind.bytes = #view.kind.text\n    view.kind.width = vim.fn.strdisplaywidth(view.kind.text)\n    view.kind.hl_group = item.kind_hl_group or ('CmpItemKind' .. (types.lsp.CompletionItemKind[self:get_kind()] or ''))\n    view.menu = {}\n    view.menu.text = item.menu or ''\n    view.menu.bytes = #view.menu.text\n    view.menu.width = vim.fn.strdisplaywidth(view.menu.text)\n    view.menu.hl_group = item.menu_hl_group or 'CmpItemMenu'\n    view.dup = item.dup\n  end)\n  return view\nend\n\n---Make vim.CompletedItem\n---@param suggest_offset integer\n---@return vim.CompletedItem\nentry.get_vim_item = function(self, suggest_offset)\n  return self.cache:ensure('get_vim_item:' .. tostring(suggest_offset), entry._get_vim_item, self, suggest_offset)\nend\n\n---@package\nentry._get_vim_item = function(self, suggest_offset)\n  local completion_item = self.completion_item\n  local word = self.word\n  local abbr = str.oneline(completion_item.label)\n\n  -- ~ indicator\n  local is_expandable = false\n  local expandable_indicator = config.get().formatting.expandable_indicator\n  if #(completion_item.additionalTextEdits or {}) > 0 then\n    is_expandable = true\n  elseif completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n    is_expandable = self:get_insert_text() ~= word\n  elseif completion_item.kind == types.lsp.CompletionItemKind.Snippet then\n    is_expandable = true\n  end\n  if expandable_indicator and is_expandable then\n    abbr = abbr .. '~'\n  end\n\n  -- append delta text\n  if suggest_offset < self.offset then\n    word = string.sub(self.context.cursor_before_line, suggest_offset, self.offset - 1) .. word\n  end\n\n  -- labelDetails.\n  local menu = nil\n  if completion_item.labelDetails then\n    menu = ''\n    if completion_item.labelDetails.detail then\n      menu = menu .. completion_item.labelDetails.detail\n    end\n    if completion_item.labelDetails.description then\n      menu = menu .. completion_item.labelDetails.description\n    end\n  end\n\n  -- remove duplicated string.\n  if self.offset ~= self.context.cursor.col then\n    for i = 1, #word do\n      if str.has_prefix(self.context.cursor_after_line, string.sub(word, i, #word)) then\n        word = string.sub(word, 1, i - 1)\n        break\n      end\n    end\n  end\n\n  local cmp_opts = completion_item.cmp or {}\n\n  local vim_item = {\n    word = word,\n    abbr = abbr,\n    icon = cmp_opts.icon or get_icon(types.lsp.CompletionItemKind[self:get_kind()]),\n    icon_hl_group = cmp_opts.icon_hl_group,\n    kind = cmp_opts.kind_text or types.lsp.CompletionItemKind[self:get_kind()] or types.lsp.CompletionItemKind[1],\n    kind_hl_group = cmp_opts.kind_hl_group,\n    menu = menu,\n    dup = completion_item.dup or 1,\n  }\n  if config.get().formatting.format then\n    vim_item = config.get().formatting.format(self, vim_item)\n  end\n  vim_item.word = str.oneline(vim_item.word or '')\n  vim_item.abbr = str.oneline(vim_item.abbr or '')\n  vim_item.icon = str.oneline(vim_item.icon or '')\n  vim_item.kind = str.oneline(vim_item.kind or '')\n  vim_item.menu = str.oneline(vim_item.menu or '')\n  vim_item.equal = 1\n  vim_item.empty = 1\n\n  return vim_item\nend\n\n---Get commit characters\n---@return string[]\nentry.get_commit_characters = function(self)\n  return self.completion_item.commitCharacters or {}\nend\n\n---@deprecated use entry.insert_range instead\nentry.get_insert_range = function(self)\n  return self.insert_range\nend\n\n---@deprecated use entry.replace_range instead\nentry.get_replace_range = function(self)\n  return self.replace_range\nend\n\n---Match line.\n---@param input string\n---@param matching_config cmp.MatchingConfig\n---@return { score: integer, matches: table[] }\nentry.match = function(self, input, matching_config)\n  -- https://www.lua.org/pil/11.6.html\n  -- do not use '..' to allocate multiple strings\n  local cache_key = string.format('%s:%d:%d:%d:%d:%d:%d', input, self.resolved_completion_item and 1 or 0, matching_config.disallow_fuzzy_matching and 1 or 0, matching_config.disallow_partial_matching and 1 or 0, matching_config.disallow_prefix_unmatching and 1 or 0, matching_config.disallow_partial_fuzzy_matching and 1 or 0, matching_config.disallow_symbol_nonprefix_matching and 1 or 0)\n  local matched = self.match_cache:get(cache_key)\n  if matched then\n    if self.match_view_args_ret and self.match_view_args_ret.input ~= input then\n      self.match_view_args_ret.input = input\n      self.match_view_args_ret.word = matched._word\n      self.match_view_args_ret.matches = matched.matches\n    end\n    return matched\n  end\n  matched = self:_match(input, matching_config)\n  self.match_cache:set(cache_key, matched)\n  return matched\nend\n\n---@package\nentry._match = function(self, input, matching_config)\n  local completion_item = self.completion_item\n  local option = {\n    disallow_fuzzy_matching = matching_config.disallow_fuzzy_matching,\n    disallow_partial_fuzzy_matching = matching_config.disallow_partial_fuzzy_matching,\n    disallow_partial_matching = matching_config.disallow_partial_matching,\n    disallow_prefix_unmatching = matching_config.disallow_prefix_unmatching,\n    disallow_symbol_nonprefix_matching = matching_config.disallow_symbol_nonprefix_matching,\n    synonyms = {\n      self.word,\n      self.completion_item.label,\n    },\n  }\n\n  local score, matches, filter_text\n  local checked = {} ---@type table<string, boolean>\n\n  filter_text = self.filter_text\n  checked[filter_text] = true\n  score, matches = matcher.match(input, filter_text, option)\n\n  -- Support the language server that doesn't respect VSCode's behaviors.\n  if score == 0 then\n    if completion_item.textEdit and not misc.empty(completion_item.textEdit.newText) then\n      local diff = self.source_offset - self.offset\n      if diff > 0 then\n        local prefix = string.sub(self.context.cursor_line, self.offset, self.offset + diff)\n        local accept = nil\n        accept = accept or string.match(prefix, '^[^%a]+$')\n        accept = accept or string.find(completion_item.textEdit.newText, prefix, 1, true)\n        if accept then\n          filter_text = prefix .. filter_text\n          if not checked[filter_text] then\n            checked[filter_text] = true\n            score, matches = matcher.match(input, filter_text, option)\n          end\n        end\n      end\n    end\n  end\n\n  -- Fix highlight if filterText is not the same to vim_item.abbr.\n  if score > 0 then\n    self.match_view_args_ret = {\n      input = input,\n      word = filter_text,\n      option = option,\n      matches = matches,\n    }\n  end\n\n  return { score = score, matches = matches, _word = filter_text }\nend\n\n---@param view string\nentry.get_view_matches = function(self, view)\n  if self.match_view_args_ret then\n    if self.match_view_args_ret.word == view then\n      return self.match_view_args_ret.matches\n    end\n    self.match_view_args_ret.word = view\n    local input = self.match_view_args_ret.input\n    local diff = self.source_offset - self.offset\n    if diff > 0 then\n      input = input:sub(1 + diff)\n    end\n    local _, matches = matcher.match(input, view, self.match_view_args_ret.option)\n    self.match_view_args_ret.matches = matches\n    return matches\n  end\nend\n\n---@deprecated use entry.completion_item instead\nentry.get_completion_item = function(self)\n  return self.completion_item\nend\n\n---Create documentation\n---@return string[]\nentry.get_documentation = function(self)\n  local item = self.completion_item\n\n  local documents = {}\n\n  -- detail\n  if item.detail and item.detail ~= '' then\n    local ft = self.context.filetype\n    local dot_index = string.find(ft, '%.')\n    if dot_index ~= nil then\n      ft = string.sub(ft, 0, dot_index - 1)\n    end\n    table.insert(documents, {\n      kind = types.lsp.MarkupKind.Markdown,\n      value = ('```%s\\n%s\\n```'):format(ft, str.trim(item.detail)),\n    })\n  end\n\n  local documentation = item.documentation\n  if type(documentation) == 'string' and documentation ~= '' then\n    local value = str.trim(documentation)\n    if value ~= '' then\n      table.insert(documents, {\n        kind = types.lsp.MarkupKind.PlainText,\n        value = value,\n      })\n    end\n  elseif type(documentation) == 'table' and not misc.empty(documentation.value) then\n    local value = str.trim(documentation.value)\n    if value ~= '' then\n      table.insert(documents, {\n        kind = documentation.kind,\n        value = value,\n      })\n    end\n  end\n\n  return vim.lsp.util.convert_input_to_markdown_lines(documents)\nend\n\n---Get completion item kind\n---@return lsp.CompletionItemKind\nentry.get_kind = function(self)\n  return self.completion_item.kind or types.lsp.CompletionItemKind.Text\nend\n\n---Execute completion item's command.\n---@param callback fun()\nentry.execute = function(self, callback)\n  self.source:execute(self.completion_item, callback)\nend\n\n---Resolve completion item.\n---@param callback fun()\nentry.resolve = function(self, callback)\n  if self.resolved_completion_item then\n    return callback()\n  end\n  table.insert(self.resolved_callbacks, callback)\n\n  if not self.resolving then\n    self.resolving = true\n    self.source:resolve(self.completion_item, function(completion_item)\n      self.resolving = false\n      if not completion_item then\n        return\n      end\n      self:_set_completion_item(completion_item)\n      self.resolved_completion_item = self.completion_item\n      self.cache:clear()\n      for _, c in ipairs(self.resolved_callbacks) do\n        c()\n      end\n    end)\n  end\nend\n\n---@param completion_item lsp.CompletionItem\n---@param defaults? lsp.internal.CompletionItemDefaults\n---@return lsp.CompletionItem\nentry.fill_defaults = function(_, completion_item, defaults)\n  defaults = defaults or {}\n\n  if defaults.data then\n    completion_item.data = completion_item.data or defaults.data\n  end\n\n  if defaults.commitCharacters then\n    completion_item.commitCharacters = completion_item.commitCharacters or defaults.commitCharacters\n  end\n\n  if defaults.insertTextFormat then\n    completion_item.insertTextFormat = completion_item.insertTextFormat or defaults.insertTextFormat\n  end\n\n  if defaults.insertTextMode then\n    completion_item.insertTextMode = completion_item.insertTextMode or defaults.insertTextMode\n  end\n\n  if defaults.editRange then\n    if not completion_item.textEdit then\n      if defaults.editRange.insert then\n        completion_item.textEdit = {\n          insert = defaults.editRange.insert,\n          replace = defaults.editRange.replace,\n          newText = completion_item.textEditText or completion_item.label,\n        }\n      else\n        completion_item.textEdit = {\n          range = defaults.editRange, --[[@as lsp.Range]]\n          newText = completion_item.textEditText or completion_item.label,\n        }\n      end\n    end\n  end\n\n  return completion_item\nend\n\n---Convert the oneline range encoding.\nentry.convert_range_encoding = function(self, range)\n  local from_encoding = self.source.position_encoding\n  local cache_key = string.format('entry.convert_range_encoding:%d:%d:%s', range.start.character, range['end'].character, from_encoding)\n  local res = self.context.cache:get(cache_key)\n  if res then\n    return res\n  end\n  res = {\n    start = types.lsp.Position.to_utf8(self.context.cursor_line, range.start, from_encoding),\n    ['end'] = types.lsp.Position.to_utf8(self.context.cursor_line, range['end'], from_encoding),\n  }\n  self.context.cache:set(cache_key, res)\n  return res\nend\n\n---Return true if the entry is invalid.\nentry.is_invalid = function(self)\n  local is_invalid = false\n  is_invalid = is_invalid or misc.empty(self.completion_item.label)\n  if self.completion_item.textEdit then\n    local range = self.completion_item.textEdit.range or self.completion_item.textEdit.insert\n    is_invalid = is_invalid or range.start.line ~= range['end'].line or range.start.line ~= self.context.cursor.line\n  end\n  return is_invalid\nend\n\nreturn entry\n"
  },
  {
    "path": "lua/cmp/entry_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\n\nlocal entry = require('cmp.entry')\n\ndescribe('entry', function()\n  before_each(spec.before)\n\n  it('one char', function()\n    local state = spec.state('@.', 1, 3)\n    state.input('@')\n    local e = entry.new(state.manual(), state.source(), {\n      label = '@',\n    })\n    assert.are.equal(e.offset, 3)\n    assert.are.equal(e:get_vim_item(e.offset).word, '@')\n  end)\n\n  it('word length (no fix)', function()\n    local state = spec.state('a.b', 1, 4)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = 'b',\n    })\n    assert.are.equal(e.offset, 5)\n    assert.are.equal(e:get_vim_item(e.offset).word, 'b')\n  end)\n\n  it('word length (fix)', function()\n    local state = spec.state('a.b', 1, 4)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = 'b.',\n    })\n    assert.are.equal(e.offset, 3)\n    assert.are.equal(e:get_vim_item(e.offset).word, 'b.')\n  end)\n\n  it('semantic index (no fix)', function()\n    local state = spec.state('a.bc', 1, 5)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = 'c.',\n    })\n    assert.are.equal(e.offset, 6)\n    assert.are.equal(e:get_vim_item(e.offset).word, 'c.')\n  end)\n\n  it('semantic index (fix)', function()\n    local state = spec.state('a.bc', 1, 5)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = 'bc.',\n    })\n    assert.are.equal(e.offset, 3)\n    assert.are.equal(e:get_vim_item(e.offset).word, 'bc.')\n  end)\n\n  it('[vscode-html-language-server] 1', function()\n    local state = spec.state('    </>', 1, 7)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = '/div',\n      textEdit = {\n        range = {\n          start = {\n            line = 0,\n            character = 0,\n          },\n          ['end'] = {\n            line = 0,\n            character = 6,\n          },\n        },\n        newText = '  </div',\n      },\n    })\n    assert.are.equal(e.offset, 5)\n    assert.are.equal(e:get_vim_item(e.offset).word, '</div')\n  end)\n\n  it('[clangd] 1', function()\n    --NOTE: clangd does not return `.foo` as filterText but we should care about it.\n    --nvim-cmp does care it by special handling in entry.lua.\n    local state = spec.state('foo', 1, 4)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      insertText = '->foo',\n      label = ' foo',\n      textEdit = {\n        newText = '->foo',\n        range = {\n          start = {\n            character = 3,\n            line = 1,\n          },\n          ['end'] = {\n            character = 4,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(4).word, '->foo')\n    assert.are.equal(e.filter_text, 'foo')\n  end)\n\n  it('[typescript-language-server] 1', function()\n    local state = spec.state('Promise.resolve()', 1, 18)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      label = 'catch',\n    })\n    -- The offset will be 18 in this situation because the server returns `[Symbol]` as candidate.\n    assert.are.equal(e:get_vim_item(18).word, '.catch')\n    assert.are.equal(e.filter_text, 'catch')\n  end)\n\n  it('[typescript-language-server] 2', function()\n    local state = spec.state('Promise.resolve()', 1, 18)\n    state.input('.')\n    local e = entry.new(state.manual(), state.source(), {\n      filterText = '.Symbol',\n      label = 'Symbol',\n      textEdit = {\n        newText = '[Symbol]',\n        range = {\n          ['end'] = {\n            character = 18,\n            line = 0,\n          },\n          start = {\n            character = 17,\n            line = 0,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(18).word, '[Symbol]')\n    assert.are.equal(e.filter_text, '.Symbol')\n  end)\n\n  it('[lua-language-server] 1', function()\n    local state = spec.state(\"local m = require'cmp.confi\", 1, 28)\n    local e\n\n    -- press g\n    state.input('g')\n    e = entry.new(state.manual(), state.source(), {\n      insertTextFormat = 2,\n      label = 'cmp.config',\n      textEdit = {\n        newText = 'cmp.config',\n        range = {\n          ['end'] = {\n            character = 27,\n            line = 1,\n          },\n          start = {\n            character = 18,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(19).word, 'cmp.config')\n    assert.are.equal(e.filter_text, 'cmp.config')\n\n    -- press '\n    state.input(\"'\")\n    e = entry.new(state.manual(), state.source(), {\n      insertTextFormat = 2,\n      label = 'cmp.config',\n      textEdit = {\n        newText = 'cmp.config',\n        range = {\n          ['end'] = {\n            character = 27,\n            line = 1,\n          },\n          start = {\n            character = 18,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(19).word, 'cmp.config')\n    assert.are.equal(e.filter_text, 'cmp.config')\n  end)\n\n  it('[lua-language-server] 2', function()\n    local state = spec.state(\"local m = require'cmp.confi\", 1, 28)\n    local e\n\n    -- press g\n    state.input('g')\n    e = entry.new(state.manual(), state.source(), {\n      insertTextFormat = 2,\n      label = 'lua.cmp.config',\n      textEdit = {\n        newText = 'lua.cmp.config',\n        range = {\n          ['end'] = {\n            character = 27,\n            line = 1,\n          },\n          start = {\n            character = 18,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(19).word, 'lua.cmp.config')\n    assert.are.equal(e.filter_text, 'lua.cmp.config')\n\n    -- press '\n    state.input(\"'\")\n    e = entry.new(state.manual(), state.source(), {\n      insertTextFormat = 2,\n      label = 'lua.cmp.config',\n      textEdit = {\n        newText = 'lua.cmp.config',\n        range = {\n          ['end'] = {\n            character = 27,\n            line = 1,\n          },\n          start = {\n            character = 18,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(19).word, 'lua.cmp.config')\n    assert.are.equal(e.filter_text, 'lua.cmp.config')\n  end)\n\n  it('[intelephense] 1', function()\n    local state = spec.state('\\t\\t', 1, 4)\n\n    -- press g\n    state.input('$')\n    local e = entry.new(state.manual(), state.source(), {\n      kind = 6,\n      label = '$this',\n      sortText = '$this',\n      textEdit = {\n        newText = '$this',\n        range = {\n          ['end'] = {\n            character = 3,\n            line = 1,\n          },\n          start = {\n            character = 2,\n            line = 1,\n          },\n        },\n      },\n    })\n    assert.are.equal(e:get_vim_item(e.offset).word, '$this')\n    assert.are.equal(e.filter_text, '$this')\n  end)\n\n  it('[odin-language-server] 1', function()\n    local state = spec.state('\\t\\t', 1, 4)\n\n    -- press g\n    state.input('s')\n    local e = entry.new(state.manual(), state.source(), {\n      additionalTextEdits = {},\n      command = {\n        arguments = {},\n        command = '',\n        title = '',\n      },\n      deprecated = false,\n      detail = 'string',\n      documentation = '',\n      insertText = '',\n      insertTextFormat = 1,\n      kind = 14,\n      label = 'string',\n      tags = {},\n    })\n    assert.are.equal(e:get_vim_item(e.offset).word, 'string')\n  end)\n\n  it('[#47] word should not contain \\\\n character', function()\n    local state = spec.state('', 1, 1)\n\n    -- press g\n    state.input('_')\n    local e = entry.new(state.manual(), state.source(), {\n      kind = 6,\n      label = '__init__',\n      insertTextFormat = 1,\n      insertText = '__init__(self) -> None:\\n  pass',\n    })\n    assert.are.equal(e:get_vim_item(e.offset).word, '__init__(self) -> None:')\n    assert.are.equal(e.filter_text, '__init__')\n  end)\n\n  -- I can't understand this test case...\n  -- it('[#1533] keyword pattern that include whitespace', function()\n  --   local state = spec.state(' ', 1, 2)\n  --   local state_source = state.source()\n\n  --   state_source.get_keyword_pattern = function(_)\n  --     return '.'\n  --   end\n\n  --   state.input(' ')\n  --   local e = entry.new(state.manual(), state_source, {\n  --     filterText = \"constructor() {\\n     ... st = 'test';\\n  \",\n  --     kind = 1,\n  --     label = \"constructor() {\\n     ... st = 'test';\\n  }\",\n  --     textEdit = {\n  --       newText = \"constructor() {\\n    this.test = 'test';\\n  }\",\n  --       range = {\n  --         ['end'] = {\n  --           character = 2,\n  --           line = 2,\n  --         },\n  --         start = {\n  --           character = 0,\n  --           line = 2,\n  --         },\n  --       },\n  --     },\n  --   })\n  --   assert.are.equal(e:get_offset(), 2)\n  --   assert.are.equal(e:get_vim_item(e:get_offset()).word, 'constructor() {')\n  -- end)\n\n  it('[#1533] clang regression test', function()\n    local state = spec.state('jsonReader', 3, 11)\n    local state_source = state.source()\n\n    state.input('.')\n    local e = entry.new(state.manual(), state_source, {\n      filterText = 'getPath()',\n      kind = 1,\n      label = 'getPath()',\n      textEdit = {\n        newText = 'getPath()',\n        range = {\n          ['end'] = {\n            character = 11,\n            col = 12,\n            line = 2,\n            row = 3,\n          },\n          start = {\n            character = 11,\n            line = 2,\n          },\n        },\n      },\n    })\n    assert.are.equal(e.offset, 12)\n    assert.are.equal(e:get_vim_item(e.offset).word, 'getPath()')\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/init.lua",
    "content": "local core = require('cmp.core')\nlocal source = require('cmp.source')\nlocal config = require('cmp.config')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\nlocal async = require('cmp.utils.async')\n\nlocal cmp = {}\n\ncmp.core = core.new()\n\n---Expose types\nfor k, v in pairs(require('cmp.types.cmp')) do\n  cmp[k] = v\nend\ncmp.lsp = require('cmp.types.lsp')\ncmp.vim = require('cmp.types.vim')\n\n---Expose event\ncmp.event = cmp.core.event\n\n---Export mapping for special case\ncmp.mapping = require('cmp.config.mapping')\n\n---Export default config presets\ncmp.config = {}\ncmp.config.disable = misc.none\ncmp.config.compare = require('cmp.config.compare')\ncmp.config.sources = require('cmp.config.sources')\ncmp.config.mapping = require('cmp.config.mapping')\ncmp.config.window = require('cmp.config.window')\n\n---Sync asynchronous process.\ncmp.sync = function(callback)\n  return function(...)\n    cmp.core.filter:sync(1000)\n    if callback then\n      return callback(...)\n    end\n  end\nend\n\n---Suspend completion.\ncmp.suspend = function()\n  return cmp.core:suspend()\nend\n\n---Register completion sources\n---@param name string\n---@param s cmp.Source\n---@return integer\ncmp.register_source = function(name, s)\n  local src = source.new(name, s)\n  cmp.core:register_source(src)\n  vim.api.nvim_exec_autocmds('User', {\n    pattern = 'CmpRegisterSource',\n    data = {\n      source_id = src.id,\n    },\n  })\n  return src.id\nend\n\n---Unregister completion source\n---@param id integer\ncmp.unregister_source = function(id)\n  local s = cmp.core:unregister_source(id)\n  if s then\n    vim.api.nvim_exec_autocmds('User', {\n      pattern = 'CmpUnregisterSource',\n      data = {\n        source_id = id,\n      },\n    })\n  end\nend\n\n---Get registered sources.\n---@return cmp.Source[]\ncmp.get_registered_sources = function()\n  return cmp.core:get_registered_sources()\nend\n\n---Get current configuration.\n---@return cmp.ConfigSchema\ncmp.get_config = function()\n  return require('cmp.config').get()\nend\n\n---Invoke completion manually\n---@param option cmp.CompleteParams\ncmp.complete = cmp.sync(function(option)\n  option = option or {}\n  config.set_onetime(option.config)\n  cmp.core:complete(cmp.core:get_context({ reason = option.reason or cmp.ContextReason.Manual }))\n  return true\nend)\n\n---Complete common string in current entries.\ncmp.complete_common_string = cmp.sync(function()\n  return cmp.core:complete_common_string()\nend)\n\n---Return view is visible or not.\ncmp.visible = cmp.sync(function()\n  return cmp.core.view:visible() or vim.fn.pumvisible() == 1\nend)\n\n---Get what number candidates are currently selected.\n---If not selected, nil is returned.\ncmp.get_selected_index = cmp.sync(function()\n  return cmp.core.view:get_selected_index()\nend)\n\n---Get current selected entry or nil\ncmp.get_selected_entry = cmp.sync(function()\n  return cmp.core.view:get_selected_entry()\nend)\n\n---Get current active entry or nil\ncmp.get_active_entry = cmp.sync(function()\n  return cmp.core.view:get_active_entry()\nend)\n\n---Get current all entries\ncmp.get_entries = cmp.sync(function()\n  return cmp.core.view:get_entries()\nend)\n\n---Close current completion\ncmp.close = cmp.sync(function()\n  if cmp.core.view:visible() then\n    local release = cmp.core:suspend()\n    cmp.core.view:close()\n    cmp.core:reset()\n    vim.schedule(release)\n    return true\n  else\n    return false\n  end\nend)\n\n---Abort current completion\ncmp.abort = cmp.sync(function()\n  if cmp.core.view:visible() then\n    local release = cmp.core:suspend()\n    cmp.core.view:abort()\n    cmp.core:reset()\n    vim.schedule(release)\n    return true\n  else\n    return false\n  end\nend)\n\n---Select next item if possible\ncmp.select_next_item = cmp.sync(function(option)\n  option = option or {}\n  option.behavior = option.behavior or cmp.SelectBehavior.Insert\n  option.count = option.count or 1\n\n  if cmp.core.view:visible() then\n    local release = cmp.core:suspend()\n    cmp.core.view:select_next_item(option)\n    vim.schedule(release)\n    return true\n  elseif vim.fn.pumvisible() == 1 then\n    if option.behavior == cmp.SelectBehavior.Insert then\n      feedkeys.call(keymap.t(string.rep('<C-n>', option.count)), 'in')\n    else\n      feedkeys.call(keymap.t(string.rep('<Down>', option.count)), 'in')\n    end\n    return true\n  end\n  return false\nend)\n\n---Select prev item if possible\ncmp.select_prev_item = cmp.sync(function(option)\n  option = option or {}\n  option.behavior = option.behavior or cmp.SelectBehavior.Insert\n  option.count = option.count or 1\n\n  if cmp.core.view:visible() then\n    local release = cmp.core:suspend()\n    cmp.core.view:select_prev_item(option)\n    vim.schedule(release)\n    return true\n  elseif vim.fn.pumvisible() == 1 then\n    if option.behavior == cmp.SelectBehavior.Insert then\n      feedkeys.call(keymap.t(string.rep('<C-p>', option.count)), 'in')\n    else\n      feedkeys.call(keymap.t(string.rep('<Up>', option.count)), 'in')\n    end\n    return true\n  end\n  return false\nend)\n\n---Scrolling documentation window if possible\ncmp.scroll_docs = cmp.sync(function(delta)\n  if cmp.core.view.docs_view:visible() then\n    cmp.core.view:scroll_docs(delta)\n    return true\n  else\n    return false\n  end\nend)\n\n---Whether the documentation window is visible or not.\ncmp.visible_docs = cmp.sync(function()\n  return cmp.core.view.docs_view:visible()\nend)\n\n---Opens the documentation window.\ncmp.open_docs = cmp.sync(function()\n  if not cmp.visible_docs() then\n    cmp.core.view:open_docs()\n    return true\n  else\n    return false\n  end\nend)\n\n---Closes the documentation window.\ncmp.close_docs = cmp.sync(function()\n  if cmp.visible_docs() then\n    cmp.core.view:close_docs()\n    return true\n  else\n    return false\n  end\nend)\n\n---Confirm completion\ncmp.confirm = cmp.sync(function(option, callback)\n  option = option or {}\n  option.select = option.select or false\n  option.behavior = option.behavior or cmp.get_config().confirmation.default_behavior or cmp.ConfirmBehavior.Insert\n  callback = callback or function() end\n\n  if cmp.core.view:visible() then\n    local e = cmp.core.view:get_selected_entry()\n    if not e and option.select then\n      e = cmp.core.view:get_first_entry()\n    end\n    if e then\n      cmp.core:confirm(e, {\n        behavior = option.behavior,\n      }, function()\n        callback()\n        cmp.core:complete(cmp.core:get_context({ reason = cmp.ContextReason.TriggerOnly }))\n      end)\n      return true\n    end\n  elseif vim.fn.pumvisible() == 1 then\n    local index = vim.fn.complete_info({ 'selected' }).selected\n    if index == -1 and option.select then\n      index = 0\n    end\n    if index ~= -1 then\n      vim.api.nvim_select_popupmenu_item(index, true, true, {})\n      return true\n    end\n  end\n  return false\nend)\n\n---Show status\ncmp.status = function()\n  local kinds = {}\n  kinds.available = {}\n  kinds.unavailable = {}\n  kinds.installed = {}\n  kinds.invalid = {}\n  local names = {}\n  for _, s in pairs(cmp.core.sources) do\n    names[s.name] = true\n\n    if config.get_source_config(s.name) then\n      if s:is_available() then\n        table.insert(kinds.available, s:get_debug_name())\n      else\n        table.insert(kinds.unavailable, s:get_debug_name())\n      end\n    else\n      table.insert(kinds.installed, s:get_debug_name())\n    end\n  end\n  for _, s in ipairs(config.get().sources) do\n    if not names[s.name] then\n      table.insert(kinds.invalid, s.name)\n    end\n  end\n\n  if #kinds.available > 0 then\n    vim.api.nvim_echo({ { '\\n', 'Normal' } }, false, {})\n    vim.api.nvim_echo({ { '# ready source names\\n', 'Special' } }, false, {})\n    for _, name in ipairs(kinds.available) do\n      vim.api.nvim_echo({ { ('- %s\\n'):format(name), 'Normal' } }, false, {})\n    end\n  end\n\n  if #kinds.unavailable > 0 then\n    vim.api.nvim_echo({ { '\\n', 'Normal' } }, false, {})\n    vim.api.nvim_echo({ { '# unavailable source names\\n', 'Comment' } }, false, {})\n    for _, name in ipairs(kinds.unavailable) do\n      vim.api.nvim_echo({ { ('- %s\\n'):format(name), 'Normal' } }, false, {})\n    end\n  end\n\n  if #kinds.installed > 0 then\n    vim.api.nvim_echo({ { '\\n', 'Normal' } }, false, {})\n    vim.api.nvim_echo({ { '# unused source names\\n', 'WarningMsg' } }, false, {})\n    for _, name in ipairs(kinds.installed) do\n      vim.api.nvim_echo({ { ('- %s\\n'):format(name), 'Normal' } }, false, {})\n    end\n  end\n\n  if #kinds.invalid > 0 then\n    vim.api.nvim_echo({ { '\\n', 'Normal' } }, false, {})\n    vim.api.nvim_echo({ { '# unknown source names\\n', 'ErrorMsg' } }, false, {})\n    for _, name in ipairs(kinds.invalid) do\n      vim.api.nvim_echo({ { ('- %s\\n'):format(name), 'Normal' } }, false, {})\n    end\n  end\nend\n\n---Ensures that cmp is the last receiver of the events specified.\n---@param events string[]\ncmp.resubscribe = function(events)\n  autocmd.resubscribe(events)\nend\n\n---@type cmp.Setup\ncmp.setup = setmetatable({\n  global = function(c)\n    config.set_global(c)\n  end,\n  filetype = function(filetype, c)\n    config.set_filetype(c, filetype)\n  end,\n  buffer = function(c)\n    config.set_buffer(c, vim.api.nvim_get_current_buf())\n  end,\n  cmdline = function(type, c)\n    config.set_cmdline(c, type)\n  end,\n}, {\n  __call = function(self, c)\n    self.global(c)\n  end,\n})\n\n-- In InsertEnter autocmd, vim will detects mode=normal unexpectedly.\nlocal on_insert_enter = function()\n  if config.enabled() then\n    cmp.config.compare.scopes:update()\n    cmp.config.compare.locality:update()\n    cmp.core:prepare()\n    cmp.core:on_change('InsertEnter')\n  end\nend\nautocmd.subscribe({ 'CmdlineEnter' }, async.debounce_next_tick(on_insert_enter))\nautocmd.subscribe({ 'InsertEnter' }, async.debounce_next_tick_by_keymap(on_insert_enter))\n\n-- async.throttle is needed for performance. The mapping `:<C-u>...<CR>` will fire `CmdlineChanged` for each character.\nlocal on_text_changed = function()\n  if config.enabled() then\n    cmp.core:on_change('TextChanged')\n  end\nend\nautocmd.subscribe({ 'TextChangedI', 'TextChangedP' }, on_text_changed)\nautocmd.subscribe('CmdlineChanged', async.debounce_next_tick(on_text_changed))\n\nautocmd.subscribe('CursorMovedI', function()\n  if config.enabled() then\n    cmp.core:on_moved()\n  else\n    cmp.core:reset()\n    cmp.core.view:close()\n  end\nend)\n\n-- If make this asynchronous, the completion menu will not close when the command output is displayed.\nautocmd.subscribe({ 'InsertLeave', 'CmdlineLeave', 'CmdwinEnter' }, function()\n  cmp.core:reset()\n  cmp.core.view:close()\nend)\n\ncmp.event:on('complete_done', function(evt)\n  if evt.entry then\n    cmp.config.compare.recently_used:add_entry(evt.entry)\n  end\n  cmp.config.compare.scopes:update()\n  cmp.config.compare.locality:update()\nend)\n\ncmp.event:on('confirm_done', function(evt)\n  if evt.entry then\n    cmp.config.compare.recently_used:add_entry(evt.entry)\n  end\nend)\n\nreturn cmp\n"
  },
  {
    "path": "lua/cmp/matcher.lua",
    "content": "local char = require('cmp.utils.char')\n\nlocal matcher = {}\n\nmatcher.WORD_BOUNDALY_ORDER_FACTOR = 10\n\nmatcher.PREFIX_FACTOR = 8\nmatcher.NOT_FUZZY_FACTOR = 6\n\n---@type function\nmatcher.debug = function(...)\n  return ...\nend\n\n--- score\n--\n-- ### The score\n--\n--   The `score` is `matched char count` generally.\n--\n--   But cmp will fix the score with some of the below points so the actual score is not `matched char count`.\n--\n--   1. Word boundary order\n--\n--     cmp prefers the match that near by word-beggining.\n--\n--   2. Strict case\n--\n--     cmp prefers strict match than ignorecase match.\n--\n--\n-- ### Matching specs.\n--\n--   1. Prefix matching per word boundary\n--\n--     `bora`         -> `border-radius` # imaginary score: 4\n--      ^^~~              ^^     ~~\n--\n--   2. Try sequential match first\n--\n--     `woroff`       -> `word_offset`   # imaginary score: 6\n--      ^^^~~~            ^^^  ~~~\n--\n--     * The `woroff`'s second `o` should not match `word_offset`'s first `o`\n--\n--   3. Prefer early word boundary\n--\n--     `call`         -> `call`          # imaginary score: 4.1\n--      ^^^^              ^^^^\n--     `call`         -> `condition_all` # imaginary score: 4\n--      ^~~~              ^         ~~~\n--\n--   4. Prefer strict match\n--\n--     `Buffer`       -> `Buffer`        # imaginary score: 6.1\n--      ^^^^^^            ^^^^^^\n--     `buffer`       -> `Buffer`        # imaginary score: 6\n--      ^^^^^^            ^^^^^^\n--\n--   5. Use remaining characters for substring match\n--\n--     `fmodify`        -> `fnamemodify`   # imaginary score: 1\n--      ^~~~~~~             ^    ~~~~~~\n--\n--   6. Avoid unexpected match detection\n--\n--     `candlesingle` -> candle#accept#single\n--      ^^^^^^~~~~~~     ^^^^^^        ~~~~~~\n--      * The `accept`'s `a` should not match to `candle`'s `a`\n--\n--   7. Avoid false positive matching\n--\n--     `,` -> print,\n--                 ~\n--      * Typically, the middle match with symbol characters only is false positive. should be ignored.\n--        This doesn't work for command line completions like \":b foo_\" which we like to match\n--        \"lib/foo_bar.txt\". The option disallow_symbol_nonprefix_matching controls this and defaults\n--        to preventing matches like these. The documentation recommends it for command line completion.\n--\n--\n---Match entry\n---@param input string\n---@param word string\n---@param option { synonyms: string[], disallow_fullfuzzy_matching: boolean, disallow_fuzzy_matching: boolean, disallow_partial_fuzzy_matching: boolean, disallow_partial_matching: boolean, disallow_prefix_unmatching: boolean, disallow_symbol_nonprefix_matching: boolean }\n---@return integer, table\nmatcher.match = function(input, word, option)\n  option = option or {}\n\n  -- Empty input\n  if #input == 0 then\n    return matcher.PREFIX_FACTOR + matcher.NOT_FUZZY_FACTOR, {}\n  end\n\n  -- Ignore if input is long than word\n  if #input > #word then\n    return 0, {}\n  end\n\n  -- Check prefix matching.\n  if option.disallow_prefix_unmatching then\n    if not char.match(string.byte(input, 1), string.byte(word, 1)) then\n      return 0, {}\n    end\n  end\n\n  -- Gather matched regions\n  local matches = {}\n  local input_start_index = 1\n  local input_end_index = 1\n  local word_index = 1\n  local word_bound_index = 1\n  local no_symbol_match = false\n  while input_end_index <= #input and word_index <= #word do\n    local m = matcher.find_match_region(input, input_start_index, input_end_index, word, word_index)\n    if m and input_end_index <= m.input_match_end then\n      m.index = word_bound_index\n      input_start_index = m.input_match_start + 1\n      input_end_index = m.input_match_end + 1\n      no_symbol_match = no_symbol_match or m.no_symbol_match\n      word_index = char.get_next_semantic_index(word, m.word_match_end)\n      table.insert(matches, m)\n    else\n      word_index = char.get_next_semantic_index(word, word_index)\n    end\n    word_bound_index = word_bound_index + 1\n  end\n\n  -- Check partial matching.\n  if option.disallow_partial_matching and #matches > 1 then\n    return 0, {}\n  end\n\n  if #matches == 0 then\n    if not option.disallow_fuzzy_matching and not option.disallow_prefix_unmatching and not option.disallow_partial_fuzzy_matching then\n      if matcher.fuzzy(input, word, matches, option) then\n        return 1, matches\n      end\n    end\n    return 0, {}\n  end\n\n  matcher.debug(word, matches)\n\n  -- Add prefix bonus\n  local prefix = false\n  if matches[1].input_match_start == 1 and matches[1].word_match_start == 1 then\n    prefix = true\n  else\n    for _, synonym in ipairs(option.synonyms or {}) do\n      prefix = true\n      local o = 1\n      for i = matches[1].input_match_start, matches[1].input_match_end do\n        if not char.match(string.byte(synonym, o), string.byte(input, i)) then\n          prefix = false\n          break\n        end\n        o = o + 1\n      end\n      if prefix then\n        break\n      end\n    end\n  end\n\n  if no_symbol_match and not prefix then\n    if option.disallow_symbol_nonprefix_matching then\n      return 0, {}\n    end\n  end\n\n  -- Compute prefix match score\n  local score = prefix and matcher.PREFIX_FACTOR or 0\n  local offset = prefix and matches[1].index - 1 or 0\n  local idx = 1\n  for _, m in ipairs(matches) do\n    local s = 0\n    for i = math.max(idx, m.input_match_start), m.input_match_end do\n      s = s + 1\n      idx = i\n    end\n    idx = idx + 1\n    if s > 0 then\n      s = s * (1 + m.strict_ratio)\n      s = s * (1 + math.max(0, matcher.WORD_BOUNDALY_ORDER_FACTOR - (m.index - offset)) / matcher.WORD_BOUNDALY_ORDER_FACTOR)\n      score = score + s\n    end\n  end\n\n  -- Check remaining input as fuzzy\n  if matches[#matches].input_match_end < #input then\n    if not option.disallow_fuzzy_matching then\n      if not option.disallow_partial_fuzzy_matching or prefix then\n        if matcher.fuzzy(input, word, matches, option) then\n          return score, matches\n        end\n      end\n    end\n    return 0, {}\n  end\n\n  return score + matcher.NOT_FUZZY_FACTOR, matches\nend\n\n--- fuzzy\nmatcher.fuzzy = function(input, word, matches, option)\n  local input_index = matches[#matches] and (matches[#matches].input_match_end + 1) or 1\n\n  -- Lately specified middle of text.\n  for i = 1, #matches - 1 do\n    local curr_match = matches[i]\n    local next_match = matches[i + 1]\n    local word_offset = 0\n    local word_index = char.get_next_semantic_index(word, curr_match.word_match_end)\n    while word_offset + word_index < next_match.word_match_start and input_index <= #input do\n      if char.match(string.byte(word, word_index + word_offset), string.byte(input, input_index)) then\n        input_index = input_index + 1\n        word_offset = word_offset + 1\n      else\n        word_index = char.get_next_semantic_index(word, word_index + word_offset)\n        word_offset = 0\n      end\n    end\n  end\n\n  -- Remaining text fuzzy match.\n  local matched = false\n  local word_offset = 0\n  local word_index = matches[#matches] and (matches[#matches].word_match_end + 1) or 1\n  local input_match_start = -1\n  local input_match_end = -1\n  local word_match_start = -1\n  local strict_count = 0\n  local match_count = 0\n  while word_offset + word_index <= #word and input_index <= #input do\n    local c1, c2 = string.byte(word, word_index + word_offset), string.byte(input, input_index)\n    if char.match(c1, c2) then\n      if not matched then\n        input_match_start = input_index\n        word_match_start = word_index + word_offset\n      end\n      matched = true\n      input_index = input_index + 1\n      strict_count = strict_count + (c1 == c2 and 1 or 0)\n      match_count = match_count + 1\n    else\n      if option.disallow_fullfuzzy_matching then\n        break\n      else\n        if matched then\n          table.insert(matches, {\n            input_match_start = input_match_start,\n            input_match_end = input_index - 1,\n            word_match_start = word_match_start,\n            word_match_end = word_index + word_offset - 1,\n            strict_ratio = strict_count / match_count,\n            fuzzy = true,\n          })\n        end\n      end\n      matched = false\n    end\n    word_offset = word_offset + 1\n  end\n\n  if matched and input_index > #input then\n    table.insert(matches, {\n      input_match_start = input_match_start,\n      input_match_end = input_match_end,\n      word_match_start = word_match_start,\n      word_match_end = word_index + word_offset - 1,\n      strict_ratio = strict_count / match_count,\n      fuzzy = true,\n    })\n    return true\n  end\n  return false\nend\n\n--- find_match_region\nmatcher.find_match_region = function(input, input_start_index, input_end_index, word, word_index)\n  -- determine input position ( woroff -> word_offset )\n  while input_start_index < input_end_index do\n    if char.match(string.byte(input, input_end_index), string.byte(word, word_index)) then\n      break\n    end\n    input_end_index = input_end_index - 1\n  end\n\n  -- Can't determine input position\n  if input_end_index < input_start_index then\n    return nil\n  end\n\n  local input_match_start = -1\n  local input_index = input_end_index\n  local word_offset = 0\n  local strict_count = 0\n  local match_count = 0\n  local no_symbol_match = false\n  while input_index <= #input and word_index + word_offset <= #word do\n    local c1 = string.byte(input, input_index)\n    local c2 = string.byte(word, word_index + word_offset)\n    if char.match(c1, c2) then\n      -- Match start.\n      if input_match_start == -1 then\n        input_match_start = input_index\n      end\n\n      strict_count = strict_count + (c1 == c2 and 1 or 0)\n      match_count = match_count + 1\n      word_offset = word_offset + 1\n      no_symbol_match = no_symbol_match or char.is_symbol(c1)\n    else\n      -- Match end (partial region)\n      if input_match_start ~= -1 then\n        return {\n          input_match_start = input_match_start,\n          input_match_end = input_index - 1,\n          word_match_start = word_index,\n          word_match_end = word_index + word_offset - 1,\n          strict_ratio = strict_count / match_count,\n          no_symbol_match = no_symbol_match,\n          fuzzy = false,\n        }\n      else\n        return nil\n      end\n    end\n    input_index = input_index + 1\n  end\n\n  -- Match end (whole region)\n  if input_match_start ~= -1 then\n    return {\n      input_match_start = input_match_start,\n      input_match_end = input_index - 1,\n      word_match_start = word_index,\n      word_match_end = word_index + word_offset - 1,\n      strict_ratio = strict_count / match_count,\n      no_symbol_match = no_symbol_match,\n      fuzzy = false,\n    }\n  end\n\n  return nil\nend\n\nreturn matcher\n"
  },
  {
    "path": "lua/cmp/matcher_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal default_config = require('cmp.config.default')\n\nlocal matcher = require('cmp.matcher')\n\ndescribe('matcher', function()\n  before_each(spec.before)\n\n  it('match', function()\n    local config = default_config()\n    assert.is.truthy(matcher.match('', 'a', config.matching) >= 1)\n    assert.is.truthy(matcher.match('a', 'a', config.matching) >= 1)\n    assert.is.truthy(matcher.match('ab', 'a', config.matching) == 0)\n    assert.is.truthy(matcher.match('ab', 'ab', config.matching) > matcher.match('ab', 'a_b', config.matching))\n    assert.is.truthy(matcher.match('ab', 'a_b_c', config.matching) > matcher.match('ac', 'a_b_c', config.matching))\n\n    assert.is.truthy(matcher.match('bora', 'border-radius', config.matching) >= 1)\n    assert.is.truthy(matcher.match('woroff', 'word_offset', config.matching) >= 1)\n    assert.is.truthy(matcher.match('call', 'call', config.matching) > matcher.match('call', 'condition_all', config.matching))\n    assert.is.truthy(matcher.match('Buffer', 'Buffer', config.matching) > matcher.match('Buffer', 'buffer', config.matching))\n    assert.is.truthy(matcher.match('luacon', 'lua_context', config.matching) > matcher.match('luacon', 'LuaContext', config.matching))\n    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', config.matching) >= 1)\n    assert.is.truthy(matcher.match('candlesingle', 'candle#accept#single', config.matching) >= 1)\n\n    assert.is.truthy(matcher.match('vi', 'void#', config.matching) >= 1)\n    assert.is.truthy(matcher.match('vo', 'void#', config.matching) >= 1)\n    assert.is.truthy(matcher.match('var_', 'var_dump', config.matching) >= 1)\n    assert.is.truthy(matcher.match('conso', 'console', config.matching) > matcher.match('conso', 'ConstantSourceNode', config.matching))\n    assert.is.truthy(matcher.match('usela', 'useLayoutEffect', config.matching) > matcher.match('usela', 'useDataLayer', config.matching))\n    assert.is.truthy(matcher.match('my_', 'my_awesome_variable', config.matching) > matcher.match('my_', 'completion_matching_strategy_list', config.matching))\n    assert.is.truthy(matcher.match('2', '[[2021', config.matching) >= 1)\n\n    assert.is.truthy(matcher.match(',', 'pri,', config.matching) == 0)\n    assert.is.truthy(matcher.match('/', '/**', config.matching) >= 1)\n\n    assert.is.truthy(matcher.match('true', 'v:true', { synonyms = { 'true' } }, config.matching) == matcher.match('true', 'true', config.matching))\n    assert.is.truthy(matcher.match('g', 'get', { synonyms = { 'get' } }, config.matching) > matcher.match('g', 'dein#get', { 'dein#get' }, config.matching))\n\n    assert.is.truthy(matcher.match('Unit', 'net.UnixListener', { disallow_partial_fuzzy_matching = true }, config.matching) == 0)\n    assert.is.truthy(matcher.match('Unit', 'net.UnixListener', { disallow_partial_fuzzy_matching = false }, config.matching) >= 1)\n\n    assert.is.truthy(matcher.match('emg', 'error_msg', config.matching) >= 1)\n    assert.is.truthy(matcher.match('sasr', 'saved_splitright', config.matching) >= 1)\n\n    -- TODO: #1420 test-case\n    -- assert.is.truthy(matcher.match('asset_', '????') >= 0)\n\n    local score, matches\n    score, matches = matcher.match('tail', 'HCDetails', {\n      disallow_fuzzy_matching = false,\n      disallow_partial_matching = false,\n      disallow_prefix_unmatching = false,\n      disallow_partial_fuzzy_matching = false,\n      disallow_symbol_nonprefix_matching = true,\n    })\n    assert.is.truthy(score >= 1)\n    assert.equals(matches[1].word_match_start, 5)\n\n    score = matcher.match('tail', 'HCDetails', {\n      disallow_fuzzy_matching = false,\n      disallow_partial_matching = false,\n      disallow_prefix_unmatching = false,\n      disallow_partial_fuzzy_matching = true,\n      disallow_symbol_nonprefix_matching = true,\n    })\n    assert.is.truthy(score == 0)\n  end)\n\n  it('disallow_fuzzy_matching', function()\n    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = true }) == 0)\n    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = false }) >= 1)\n  end)\n\n  it('disallow_fullfuzzy_matching', function()\n    assert.is.truthy(matcher.match('svd', 'saved_splitright', { disallow_fullfuzzy_matching = true }) == 0)\n    assert.is.truthy(matcher.match('svd', 'saved_splitright', { disallow_fullfuzzy_matching = false }) >= 1)\n  end)\n\n  it('disallow_partial_matching', function()\n    assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = true }) == 0)\n    assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = false }) >= 1)\n    assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = true }) >= 1)\n    assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = false }) >= 1)\n  end)\n\n  it('disallow_prefix_unmatching', function()\n    assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = true }) == 0)\n    assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = false }) >= 1)\n  end)\n\n  it('disallow_symbol_nonprefix_matching', function()\n    assert.is.truthy(matcher.match('foo_', 'b foo_bar', { disallow_symbol_nonprefix_matching = true }) == 0)\n    assert.is.truthy(matcher.match('foo_', 'b foo_bar', { disallow_symbol_nonprefix_matching = false }) >= 1)\n  end)\n\n  it('debug', function()\n    matcher.debug = function(...)\n      print(vim.inspect({ ... }))\n    end\n    -- print(vim.inspect({\n    --   a = matcher.match('true', 'v:true', { 'true' }),\n    --   b = matcher.match('true', 'true'),\n    -- }))\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/source.lua",
    "content": "local context = require('cmp.context')\nlocal config = require('cmp.config')\nlocal entry = require('cmp.entry')\nlocal debug = require('cmp.utils.debug')\nlocal misc = require('cmp.utils.misc')\nlocal cache = require('cmp.utils.cache')\nlocal types = require('cmp.types')\nlocal async = require('cmp.utils.async')\nlocal pattern = require('cmp.utils.pattern')\nlocal char = require('cmp.utils.char')\n\n---@class cmp.Source\n---@field public id integer\n---@field public name string\n---@field public source any\n---@field public cache cmp.Cache\n---@field public revision integer\n---@field public response? lsp.CompletionResponse|nil\n---@field public incomplete boolean\n---@field public is_triggered_by_symbol boolean\n---@field public entries cmp.Entry[]\n---@field public offset integer\n---@field public request_offset integer\n---@field public context cmp.Context\n---@field public completion_context lsp.CompletionContext|nil\n---@field public status cmp.SourceStatus\n---@field public complete_dedup function\n---@field public default_replace_range lsp.Range\n---@field public default_insert_range lsp.Range\n---@field public position_encoding lsp.PositionEncodingKind\nlocal source = {}\n\n---@alias cmp.SourceStatus 1 | 2 | 3\nsource.SourceStatus = {}\nsource.SourceStatus.WAITING = 1\nsource.SourceStatus.FETCHING = 2\nsource.SourceStatus.COMPLETED = 3\n\n---@return cmp.Source\nsource.new = function(name, s)\n  local self = setmetatable({}, { __index = source })\n  self.id = misc.id('cmp.source.new')\n  self.name = name\n  self.source = s\n  self.cache = cache.new()\n  self.complete_dedup = async.dedup()\n  self.revision = 0\n  self.position_encoding = self:get_position_encoding_kind()\n  self:reset()\n  return self\nend\n\n---Reset current completion state\nsource.reset = function(self)\n  self.cache:clear()\n  self.revision = self.revision + 1\n  self.context = context.empty()\n  self.is_triggered_by_symbol = false\n  self.incomplete = false\n  self.entries = {}\n  self.offset = -1\n  self.request_offset = -1\n  self.completion_context = nil\n  self.status = source.SourceStatus.WAITING\n  self.complete_dedup(function() end)\nend\n\n---Return source config\n---@return cmp.SourceConfig\nsource.get_source_config = function(self)\n  return config.get_source_config(self.name) or {}\nend\n\n---Return matching config\n---@return cmp.MatchingConfig\nsource.get_matching_config = function()\n  return config.get().matching\nend\n\n---Get fetching time\nsource.get_fetching_time = function(self)\n  if self.status == source.SourceStatus.FETCHING then\n    return vim.loop.now() - self.context.time\n  end\n  return 100 * 1000 -- return pseudo time if source isn't fetching.\nend\n\n---Return filtered entries\n---@param ctx cmp.Context\n---@return cmp.Entry[]\nsource.get_entries = function(self, ctx)\n  if self.offset == -1 then\n    return {}\n  end\n\n  local target_entries = self.entries\n\n  if not self.incomplete then\n    local prev = self.cache:get({ 'get_entries', tostring(self.revision) })\n    if prev and ctx.cursor.row == prev.ctx.cursor.row and self.offset == prev.offset then\n      -- only use prev entries when cursor is moved forward.\n      -- and the pattern offset is the same.\n      if prev.ctx.cursor.col <= ctx.cursor.col then\n        target_entries = prev.entries\n      end\n    end\n  end\n\n  local entry_filter = self:get_entry_filter()\n\n  local inputs = {}\n  ---@type cmp.Entry[]\n  local entries = {}\n  local matching_config = self:get_matching_config()\n  local filtering_context_budget = config.get().performance.filtering_context_budget / 1000\n\n  local stime = (vim.uv or vim.loop).hrtime() / 1000000\n  for _, e in ipairs(target_entries) do\n    local o = e.offset\n    if not inputs[o] then\n      inputs[o] = string.sub(ctx.cursor_before_line, o)\n    end\n\n    local match = e:match(inputs[o], matching_config)\n    e.score = match.score\n    e.exact = false\n    if e.score >= 1 then\n      e.matches = match.matches\n      e.exact = e.filter_text == inputs[o] or e.word == inputs[o]\n\n      if entry_filter(e, ctx) then\n        entries[#entries + 1] = e\n      end\n    end\n\n    local etime = (vim.uv or vim.loop).hrtime() / 1000000\n    if etime - stime > filtering_context_budget then\n      async.yield()\n      if ctx.aborted then\n        async.abort()\n      end\n      stime = etime\n    end\n  end\n\n  if not self.incomplete then\n    self.cache:set({ 'get_entries', tostring(self.revision) }, { entries = entries, ctx = ctx, offset = self.offset })\n  end\n\n  return entries\nend\n\n---Get default insert range (UTF8 byte index).\n---@package\n---@return lsp.Range\nsource._get_default_insert_range = function(self)\n  return {\n    start = {\n      line = self.context.cursor.row - 1,\n      character = self.offset - 1,\n    },\n    ['end'] = {\n      line = self.context.cursor.row - 1,\n      character = self.context.cursor.col - 1,\n    },\n  }\nend\n\n---Get default replace range (UTF8 byte index).\n---@package\n---@return lsp.Range\nsource._get_default_replace_range = function(self)\n  local _, e = pattern.offset('^' .. '\\\\%(' .. self:get_keyword_pattern() .. '\\\\)', string.sub(self.context.cursor_line, self.offset))\n  return {\n    start = {\n      line = self.context.cursor.row - 1,\n      character = self.offset,\n    },\n    ['end'] = {\n      line = self.context.cursor.row - 1,\n      character = (e and self.offset + e - 2 or self.context.cursor.col - 1),\n    },\n  }\nend\n\n---@deprecated use source.default_insert_range instead\nsource.get_default_insert_range = function(self)\n  return self.default_insert_range\nend\n\n---@deprecated use source.default_replace_range instead\nsource.get_default_replae_range = function(self)\n  return self.default_replace_range\nend\n\n---Return source name.\nsource.get_debug_name = function(self)\n  local name = self.name\n  if self.source.get_debug_name then\n    name = self.source:get_debug_name()\n  end\n  return name\nend\n\n---Return the source is available or not.\nsource.is_available = function(self)\n  if self.source.is_available then\n    return self.source:is_available()\n  end\n  return true\nend\n\n---Get trigger_characters\n---@return string[]\nsource.get_trigger_characters = function(self)\n  local c = self:get_source_config()\n  if c.trigger_characters then\n    return c.trigger_characters\n  end\n\n  local trigger_characters = {}\n  if self.source.get_trigger_characters then\n    trigger_characters = self.source:get_trigger_characters(misc.copy(c)) or {}\n  end\n  if config.get().completion.get_trigger_characters then\n    return config.get().completion.get_trigger_characters(trigger_characters)\n  end\n  return trigger_characters\nend\n\n---Get keyword_pattern\n---@return string\nsource.get_keyword_pattern = function(self)\n  local c = self:get_source_config()\n  if c.keyword_pattern then\n    return c.keyword_pattern\n  end\n  if self.source.get_keyword_pattern then\n    local keyword_pattern = self.source:get_keyword_pattern(misc.copy(c))\n    if keyword_pattern then\n      return keyword_pattern\n    end\n  end\n  return config.get().completion.keyword_pattern\nend\n\n---Get keyword_length\n---@return integer\nsource.get_keyword_length = function(self)\n  local c = self:get_source_config()\n  if c.keyword_length then\n    return c.keyword_length\n  end\n  return config.get().completion.keyword_length or 1\nend\n\n---Get filter\n--@return fun(entry: cmp.Entry, context: cmp.Context): boolean\nsource.get_entry_filter = function(self)\n  local c = self:get_source_config()\n  if c.entry_filter then\n    return c.entry_filter --[[@as fun(entry: cmp.Entry, context: cmp.Context): boolean]]\n  end\n  return function(_, _)\n    return true\n  end\nend\n\n---Get lsp.PositionEncodingKind\n---@return lsp.PositionEncodingKind\nsource.get_position_encoding_kind = function(self)\n  if self.source.get_position_encoding_kind then\n    return self.source:get_position_encoding_kind()\n  end\n  return types.lsp.PositionEncodingKind.UTF16\nend\n\n---Invoke completion\n---@param ctx cmp.Context\n---@param callback function\n---@return boolean? Return true if not trigger completion.\nsource.complete = function(self, ctx, callback)\n  local offset = ctx:get_offset(self:get_keyword_pattern())\n\n  -- NOTE: This implementation is nvim-cmp specific.\n  -- We trigger new completion after core.confirm but we check only the symbol trigger_character in this case.\n  local before_char = string.sub(ctx.cursor_before_line, -1)\n  if ctx:get_reason() == types.cmp.ContextReason.TriggerOnly then\n    before_char = string.match(ctx.cursor_before_line, '(.)%s*$')\n    if not before_char or not char.is_symbol(string.byte(before_char)) then\n      before_char = ''\n    end\n  end\n\n  local completion_context\n  if ctx:get_reason() == types.cmp.ContextReason.Manual then\n    completion_context = {\n      triggerKind = types.lsp.CompletionTriggerKind.Invoked,\n    }\n  elseif vim.tbl_contains(self:get_trigger_characters(), before_char) then\n    completion_context = {\n      triggerKind = types.lsp.CompletionTriggerKind.TriggerCharacter,\n      triggerCharacter = before_char,\n    }\n  elseif ctx:get_reason() ~= types.cmp.ContextReason.TriggerOnly then\n    if offset < ctx.cursor.col and self:get_keyword_length() <= (ctx.cursor.col - offset) then\n      if self.incomplete and self.context.cursor.col ~= ctx.cursor.col and self.status ~= source.SourceStatus.FETCHING then\n        completion_context = {\n          triggerKind = types.lsp.CompletionTriggerKind.TriggerForIncompleteCompletions,\n        }\n      elseif not vim.tbl_contains({ self.request_offset, self.offset }, offset) then\n        completion_context = {\n          triggerKind = types.lsp.CompletionTriggerKind.Invoked,\n        }\n      end\n    else\n      self:reset() -- Should clear current completion if the TriggerKind isn't TriggerCharacter or Manual and keyword length does not enough.\n    end\n  else\n    self:reset() -- Should clear current completion if ContextReason is TriggerOnly and the triggerCharacter isn't matched\n  end\n\n  -- Does not perform completions.\n  if not completion_context then\n    return\n  end\n\n  if completion_context.triggerKind == types.lsp.CompletionTriggerKind.TriggerCharacter then\n    self.is_triggered_by_symbol = char.is_symbol(string.byte(completion_context.triggerCharacter))\n  end\n\n  debug.log(self:get_debug_name(), 'request', offset, vim.inspect(completion_context))\n  local prev_status = self.status\n  self.status = source.SourceStatus.FETCHING\n  self.offset = offset\n  self.request_offset = offset\n  self.context = ctx\n  self.default_replace_range = self:_get_default_replace_range()\n  self.default_insert_range = self:_get_default_insert_range()\n  self.position_encoding = self:get_position_encoding_kind()\n  self.completion_context = completion_context\n  self.source:complete(\n    vim.tbl_extend('keep', misc.copy(self:get_source_config()), {\n      offset = self.offset,\n      context = ctx,\n      completion_context = completion_context,\n    }),\n    self.complete_dedup(vim.schedule_wrap(function(response)\n      if self.context ~= ctx then\n        return\n      end\n      ---@type lsp.CompletionResponse\n      response = response or {}\n      self.response = response\n\n      self.incomplete = response.isIncomplete or false\n\n      if #(response.items or response) > 0 then\n        debug.log(self:get_debug_name(), 'retrieve', #(response.items or response))\n        local old_offset = self.offset\n        local old_entries = self.entries\n\n        self.status = source.SourceStatus.COMPLETED\n        self.entries = {}\n        for _, item in ipairs(response.items or response) do\n          if item.label then\n            local e = entry.new(ctx, self, item, response.itemDefaults)\n            if not e:is_invalid() then\n              table.insert(self.entries, e)\n              self.offset = math.min(self.offset, e.offset)\n            end\n          end\n        end\n        self.revision = self.revision + 1\n        if #self.entries == 0 then\n          self.offset = old_offset\n          self.entries = old_entries\n          self.revision = self.revision + 1\n        end\n      else\n        -- The completion will be invoked when pressing <CR> if the trigger characters contain the <Space>.\n        -- If the server returns an empty response in such a case, should invoke the keyword completion on the next keypress.\n        if offset == ctx.cursor.col then\n          self:reset()\n        end\n        self.status = prev_status\n      end\n      callback()\n    end))\n  )\n  return true\nend\n\n---Resolve CompletionItem\n---@param item lsp.CompletionItem\n---@param callback fun(item: lsp.CompletionItem)\nsource.resolve = function(self, item, callback)\n  if not self.source.resolve then\n    return callback(item)\n  end\n  self.source:resolve(item, function(resolved_item)\n    callback(resolved_item or item)\n  end)\nend\n\n---Execute command\n---@param item lsp.CompletionItem\n---@param callback fun()\nsource.execute = function(self, item, callback)\n  if not self.source.execute then\n    return callback()\n  end\n  self.source:execute(item, function()\n    callback()\n  end)\nend\n\nreturn source\n"
  },
  {
    "path": "lua/cmp/source_spec.lua",
    "content": "local config = require('cmp.config')\nlocal spec = require('cmp.utils.spec')\n\nlocal source = require('cmp.source')\n\ndescribe('source', function()\n  before_each(spec.before)\n\n  describe('keyword length', function()\n    it('not enough', function()\n      config.set_buffer({\n        completion = {\n          keyword_length = 3,\n        },\n      }, vim.api.nvim_get_current_buf())\n\n      local state = spec.state('', 1, 1)\n      local s = source.new('spec', {\n        complete = function(_, _, callback)\n          callback({ { label = 'spec' } })\n        end,\n      })\n      assert.is.truthy(not s:complete(state.input('a'), function() end))\n    end)\n\n    it('enough', function()\n      config.set_buffer({\n        completion = {\n          keyword_length = 3,\n        },\n      }, vim.api.nvim_get_current_buf())\n\n      local state = spec.state('', 1, 1)\n      local s = source.new('spec', {\n        complete = function(_, _, callback)\n          callback({ { label = 'spec' } })\n        end,\n      })\n      assert.is.truthy(s:complete(state.input('aiu'), function() end))\n    end)\n\n    it('enough -> not enough', function()\n      config.set_buffer({\n        completion = {\n          keyword_length = 3,\n        },\n      }, vim.api.nvim_get_current_buf())\n\n      local state = spec.state('', 1, 1)\n      local s = source.new('spec', {\n        complete = function(_, _, callback)\n          callback({ { label = 'spec' } })\n        end,\n      })\n      assert.is.truthy(s:complete(state.input('aiu'), function() end))\n      assert.is.truthy(not s:complete(state.backspace(), function() end))\n    end)\n\n    it('continue', function()\n      config.set_buffer({\n        completion = {\n          keyword_length = 3,\n        },\n      }, vim.api.nvim_get_current_buf())\n\n      local state = spec.state('', 1, 1)\n      local s = source.new('spec', {\n        complete = function(_, _, callback)\n          callback({ { label = 'spec' } })\n        end,\n      })\n      assert.is.truthy(s:complete(state.input('aiu'), function() end))\n      assert.is.truthy(not s:complete(state.input('eo'), function() end))\n    end)\n  end)\n\n  describe('isIncomplete', function()\n    it('isIncomplete=true', function()\n      local state = spec.state('', 1, 1)\n      local s = source.new('spec', {\n        complete = function(_, _, callback)\n          callback({\n            items = { { label = 'spec' } },\n            isIncomplete = true,\n          })\n        end,\n      })\n      vim.wait(100, function()\n        return s.status == source.SourceStatus.COMPLETED\n      end, 100, false)\n      assert.is.truthy(s:complete(state.input('s'), function() end))\n      vim.wait(100, function()\n        return s.status == source.SourceStatus.COMPLETED\n      end, 100, false)\n      assert.is.truthy(s:complete(state.input('p'), function() end))\n      vim.wait(100, function()\n        return s.status == source.SourceStatus.COMPLETED\n      end, 100, false)\n      assert.is.truthy(s:complete(state.input('e'), function() end))\n      vim.wait(100, function()\n        return s.status == source.SourceStatus.COMPLETED\n      end, 100, false)\n      assert.is.truthy(s:complete(state.input('c'), function() end))\n      vim.wait(100, function()\n        return s.status == source.SourceStatus.COMPLETED\n      end, 100, false)\n    end)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/types/cmp.lua",
    "content": "local cmp = {}\n\n---@alias cmp.ConfirmBehavior 'insert' | 'replace'\ncmp.ConfirmBehavior = {\n  Insert = 'insert',\n  Replace = 'replace',\n}\n\n---@alias cmp.SelectBehavior 'insert' | 'select'\ncmp.SelectBehavior = {\n  Insert = 'insert',\n  Select = 'select',\n}\n\n---@alias cmp.ContextReason 'auto' | 'manual' | 'triggerOnly' | 'none'\ncmp.ContextReason = {\n  Auto = 'auto',\n  Manual = 'manual',\n  TriggerOnly = 'triggerOnly',\n  None = 'none',\n}\n\n---@alias cmp.TriggerEvent 'InsertEnter' | 'TextChanged'\ncmp.TriggerEvent = {\n  InsertEnter = 'InsertEnter',\n  TextChanged = 'TextChanged',\n}\n\n---@alias cmp.PreselectMode 'item' | 'None'\ncmp.PreselectMode = {\n  Item = 'item',\n  None = 'none',\n}\n\n---@alias cmp.ItemField 'abbr' | 'icon' | 'kind' | 'menu'\ncmp.ItemField = {\n  Abbr = 'abbr',\n  Icon = 'icon',\n  Kind = 'kind',\n  Menu = 'menu',\n}\n\n---@class cmp.ContextOption\n---@field public reason cmp.ContextReason|nil\n\n---@class cmp.ConfirmOption\n---@field public behavior cmp.ConfirmBehavior\n---@field public commit_character? string\n\n---@class cmp.SelectOption\n---@field public behavior cmp.SelectBehavior\n\n---@class cmp.SnippetExpansionParams\n---@field public body string\n---@field public insert_text_mode integer\n\n---@class cmp.CompleteParams\n---@field public reason? cmp.ContextReason\n---@field public config? cmp.ConfigSchema\n\n---@class cmp.SetupProperty\n---@field public buffer fun(c: cmp.ConfigSchema)\n---@field public global fun(c: cmp.ConfigSchema)\n---@field public cmdline fun(type: string|string[], c: cmp.ConfigSchema)\n---@field public filetype fun(type: string|string[], c: cmp.ConfigSchema)\n\n---@alias cmp.Setup cmp.SetupProperty | fun(c: cmp.ConfigSchema)\n\n---@class cmp.SourceApiParams: cmp.SourceConfig\n\n---@class cmp.SourceCompletionApiParams : cmp.SourceConfig\n---@field public offset integer\n---@field public context cmp.Context\n---@field public completion_context lsp.CompletionContext\n\n---@alias  cmp.MappingFunction fun(fallback: function): nil\n\n---@class cmp.MappingClass\n---@field public i nil|cmp.MappingFunction\n---@field public c nil|cmp.MappingFunction\n---@field public x nil|cmp.MappingFunction\n---@field public s nil|cmp.MappingFunction\n\n---@alias cmp.Mapping cmp.MappingFunction | cmp.MappingClass\n\n---@class cmp.ConfigSchema\n---@field private revision? integer\n---@field public enabled? boolean | fun(): boolean\n---@field public performance? cmp.PerformanceConfig\n---@field public preselect? cmp.PreselectMode\n---@field public completion? cmp.CompletionConfig\n---@field public window? cmp.WindowConfig|nil\n---@field public confirmation? cmp.ConfirmationConfig\n---@field public matching? cmp.MatchingConfig\n---@field public sorting? cmp.SortingConfig\n---@field public formatting? cmp.FormattingConfig\n---@field public snippet? cmp.SnippetConfig\n---@field public mapping? table<string, cmp.Mapping>\n---@field public sources? cmp.SourceConfig[]\n---@field public view? cmp.ViewConfig\n---@field public experimental? cmp.ExperimentalConfig\n\n---@class cmp.PerformanceConfig\n---@field public debounce integer\n---@field public throttle integer\n---@field public fetching_timeout integer\n---@field public filtering_context_budget integer\n---@field public confirm_resolve_timeout integer\n---@field public async_budget integer Maximum time (in ms) an async function is allowed to run during one step of the event loop.\n---@field public max_view_entries integer\n\n---@class cmp.CompletionConfig\n---@field public autocomplete? cmp.TriggerEvent[]|false\n---@field public completeopt? string\n---@field public get_trigger_characters? fun(trigger_characters: string[]): string[]\n---@field public keyword_length? integer\n---@field public keyword_pattern? string\n\n---@class cmp.WindowConfig\n---@field public completion? cmp.CompletionWindowOptions\n---@field public documentation? cmp.DocumentationWindowOptions|vim.NIL\n\n---@class cmp.WindowOptions\n---@field public border? string|string[]\n---@field public winhighlight? string\n---@field public winblend? number\n---@field public zindex? integer|nil\n\n---@class cmp.CompletionWindowOptions: cmp.WindowOptions\n---@field public scrolloff? integer|nil\n---@field public col_offset? integer|nil\n---@field public side_padding? integer|nil\n---@field public scrollbar? boolean|true\n---@field public max_height? integer|nil\n\n---@class cmp.DocumentationWindowOptions: cmp.WindowOptions\n---@field public max_height? integer|nil\n---@field public max_width? integer|nil\n---@field public scrolloff integer|nil\n---@field public scrollbar boolean|true\n---@field public col_offset integer|nil\n\n---@class cmp.ConfirmationConfig\n---@field public default_behavior cmp.ConfirmBehavior\n---@field public get_commit_characters fun(commit_characters: string[]): string[]\n\n---@class cmp.MatchingConfig\n---@field public disallow_fuzzy_matching boolean\n---@field public disallow_fullfuzzy_matching boolean\n---@field public disallow_partial_fuzzy_matching boolean\n---@field public disallow_partial_matching boolean\n---@field public disallow_prefix_unmatching boolean\n---@field public disallow_symbol_nonprefix_matching boolean\n\n---@class cmp.SortingConfig\n---@field public priority_weight integer\n---@field public comparators cmp.Comparator[]\n\n---@class cmp.FormattingConfig\n---@field public fields? cmp.ItemField[]\n---@field public expandable_indicator? boolean\n---@field public format? fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem\n\n---@class cmp.SnippetConfig\n---@field public expand fun(args: cmp.SnippetExpansionParams)\n\n---@class cmp.ExperimentalConfig\n---@field public ghost_text cmp.GhostTextConfig|boolean\n\n---@class cmp.GhostTextConfig\n---@field hl_group string\n\n---@class cmp.SourceConfig\n---@field public name string\n---@field public option table|nil\n---@field public priority integer|nil\n---@field public trigger_characters string[]|nil\n---@field public keyword_pattern string|nil\n---@field public keyword_length integer|nil\n---@field public max_item_count integer|nil\n---@field public group_index integer|nil\n---@field public entry_filter nil|function(entry: cmp.Entry, ctx: cmp.Context): boolean\n\n---@class cmp.ViewConfig\n---@field public entries? cmp.EntriesViewConfig\n---@field public docs? cmp.DocsViewConfig\n\n---@alias cmp.EntriesViewConfig cmp.CustomEntriesViewConfig|cmp.NativeEntriesViewConfig|cmp.WildmenuEntriesViewConfig|string\n\n---@class cmp.CustomEntriesViewConfig\n---@field name 'custom'\n---@field selection_order 'top_down'|'near_cursor'\n---@field vertical_positioning 'auto'|'above'|'below'\n---@field follow_cursor boolean\n\n---@class cmp.NativeEntriesViewConfig\n---@field name 'native'\n\n---@class cmp.WildmenuEntriesViewConfig\n---@field name 'wildmenu'\n---@field separator string|nil\n\n---@class cmp.DocsViewConfig\n---@field public auto_open boolean\n\nreturn cmp\n"
  },
  {
    "path": "lua/cmp/types/init.lua",
    "content": "local types = {}\n\ntypes.cmp = require('cmp.types.cmp')\ntypes.lsp = require('cmp.types.lsp')\ntypes.vim = require('cmp.types.vim')\n\nreturn types\n"
  },
  {
    "path": "lua/cmp/types/lsp.lua",
    "content": "local misc = require('cmp.utils.misc')\n\n---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/\n---@class lsp\nlocal lsp = {}\n\n---@enum lsp.PositionEncodingKind\nlsp.PositionEncodingKind = {\n  UTF8 = 'utf-8',\n  UTF16 = 'utf-16',\n  UTF32 = 'utf-32',\n}\n\nlsp.Position = {\n  ---Convert lsp.Position to vim.Position\n  ---@param buf integer\n  ---@param position lsp.Position\n  --\n  ---@return vim.Position\n  to_vim = function(buf, position)\n    if not vim.api.nvim_buf_is_loaded(buf) then\n      vim.fn.bufload(buf)\n    end\n    local lines = vim.api.nvim_buf_get_lines(buf, position.line, position.line + 1, false)\n    if #lines > 0 then\n      return {\n        row = position.line + 1,\n        col = misc.to_vimindex(lines[1], position.character),\n      }\n    end\n    return {\n      row = position.line + 1,\n      col = position.character + 1,\n    }\n  end,\n  ---Convert vim.Position to lsp.Position\n  ---@param buf integer\n  ---@param position vim.Position\n  ---@return lsp.Position\n  to_lsp = function(buf, position)\n    if not vim.api.nvim_buf_is_loaded(buf) then\n      vim.fn.bufload(buf)\n    end\n    local lines = vim.api.nvim_buf_get_lines(buf, position.row - 1, position.row, false)\n    if #lines > 0 then\n      return {\n        line = position.row - 1,\n        character = misc.to_utfindex(lines[1], position.col),\n      }\n    end\n    return {\n      line = position.row - 1,\n      character = position.col - 1,\n    }\n  end,\n\n  ---Convert position to utf8 from specified encoding.\n  ---@param text string\n  ---@param position lsp.Position\n  ---@param from_encoding? lsp.PositionEncodingKind\n  ---@return lsp.Position\n  to_utf8 = function(text, position, from_encoding)\n    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16\n    if from_encoding == lsp.PositionEncodingKind.UTF8 then\n      return position\n    end\n\n    local ok, byteindex = pcall(vim.str_byteindex, text, position.character, from_encoding == lsp.PositionEncodingKind.UTF16)\n    if not ok then\n      return position\n    end\n    return { line = position.line, character = byteindex }\n  end,\n\n  ---Convert position to utf16 from specified encoding.\n  ---@param text string\n  ---@param position lsp.Position\n  ---@param from_encoding? lsp.PositionEncodingKind\n  ---@return lsp.Position\n  to_utf16 = function(text, position, from_encoding)\n    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16\n    if from_encoding == lsp.PositionEncodingKind.UTF16 then\n      return position\n    end\n\n    local utf8 = lsp.Position.to_utf8(text, position, from_encoding)\n    for index = utf8.character, 0, -1 do\n      local ok, utf16index = pcall(function()\n        return select(2, vim.str_utfindex(text, index))\n      end)\n      if ok then\n        return { line = utf8.line, character = utf16index }\n      end\n    end\n    return position\n  end,\n\n  ---Convert position to utf32 from specified encoding.\n  ---@param text string\n  ---@param position lsp.Position\n  ---@param from_encoding? lsp.PositionEncodingKind\n  ---@return lsp.Position\n  to_utf32 = function(text, position, from_encoding)\n    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16\n    if from_encoding == lsp.PositionEncodingKind.UTF32 then\n      return position\n    end\n\n    local utf8 = lsp.Position.to_utf8(text, position, from_encoding)\n    for index = utf8.character, 0, -1 do\n      local ok, utf32index = pcall(function()\n        return select(1, vim.str_utfindex(text, index))\n      end)\n      if ok then\n        return { line = utf8.line, character = utf32index }\n      end\n    end\n    return position\n  end,\n}\n\nlsp.Range = {\n  ---Convert lsp.Range to vim.Range\n  ---@param buf integer\n  ---@param range lsp.Range\n  ---@return vim.Range\n  to_vim = function(buf, range)\n    return {\n      start = lsp.Position.to_vim(buf, range.start),\n      ['end'] = lsp.Position.to_vim(buf, range['end']),\n    }\n  end,\n\n  ---Convert vim.Range to lsp.Range\n  ---@param buf integer\n  ---@param range vim.Range\n  ---@return lsp.Range\n  to_lsp = function(buf, range)\n    return {\n      start = lsp.Position.to_lsp(buf, range.start),\n      ['end'] = lsp.Position.to_lsp(buf, range['end']),\n    }\n  end,\n}\n\n---@alias lsp.CompletionTriggerKind 1 | 2 | 3\nlsp.CompletionTriggerKind = {\n  Invoked = 1,\n  TriggerCharacter = 2,\n  TriggerForIncompleteCompletions = 3,\n}\n\n---@alias lsp.InsertTextFormat 1 | 2\nlsp.InsertTextFormat = {}\nlsp.InsertTextFormat.PlainText = 1\nlsp.InsertTextFormat.Snippet = 2\n\n---@alias lsp.InsertTextMode 1 | 2\nlsp.InsertTextMode = {\n  AsIs = 1,\n  AdjustIndentation = 2,\n}\n\n---@alias lsp.MarkupKind 'plaintext' | 'markdown'\nlsp.MarkupKind = {\n  PlainText = 'plaintext',\n  Markdown = 'markdown',\n}\n\n---@alias lsp.CompletionItemTag 1\nlsp.CompletionItemTag = {\n  Deprecated = 1,\n}\n\n---@alias lsp.CompletionItemKind 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25\nlsp.CompletionItemKind = {\n  Text = 1,\n  Method = 2,\n  Function = 3,\n  Constructor = 4,\n  Field = 5,\n  Variable = 6,\n  Class = 7,\n  Interface = 8,\n  Module = 9,\n  Property = 10,\n  Unit = 11,\n  Value = 12,\n  Enum = 13,\n  Keyword = 14,\n  Snippet = 15,\n  Color = 16,\n  File = 17,\n  Reference = 18,\n  Folder = 19,\n  EnumMember = 20,\n  Constant = 21,\n  Struct = 22,\n  Event = 23,\n  Operator = 24,\n  TypeParameter = 25,\n}\nfor _, k in ipairs(vim.tbl_keys(lsp.CompletionItemKind)) do\n  local v = lsp.CompletionItemKind[k]\n  lsp.CompletionItemKind[v] = k\nend\n\n---@class lsp.internal.CompletionItemDefaults\n---@field public commitCharacters? string[]\n---@field public editRange? lsp.Range | { insert: lsp.Range, replace: lsp.Range }\n---@field public insertTextFormat? lsp.InsertTextFormat\n---@field public insertTextMode? lsp.InsertTextMode\n---@field public data? any\n\n---@class lsp.CompletionContext\n---@field public triggerKind lsp.CompletionTriggerKind\n---@field public triggerCharacter string|nil\n\n---@class lsp.CompletionList\n---@field public isIncomplete boolean\n---@field public itemDefaults? lsp.internal.CompletionItemDefaults\n---@field public items lsp.CompletionItem[]\n\n---@alias lsp.CompletionResponse lsp.CompletionList|lsp.CompletionItem[]\n\n---@class lsp.MarkupContent\n---@field public kind lsp.MarkupKind\n---@field public value string\n\n---@class lsp.Position\n---@field public line integer\n---@field public character integer\n\n---@class lsp.Range\n---@field public start lsp.Position\n---@field public end lsp.Position\n\n---@class lsp.Command\n---@field public title string\n---@field public command string\n---@field public arguments any[]|nil\n\n---@class lsp.TextEdit\n---@field public range lsp.Range|nil\n---@field public newText string\n\n---@alias lsp.InsertReplaceTextEdit lsp.internal.InsertTextEdit|lsp.internal.ReplaceTextEdit\n\n---@class lsp.internal.InsertTextEdit\n---@field public insert lsp.Range\n---@field public newText string\n\n---@class lsp.internal.ReplaceTextEdit\n---@field public replace lsp.Range\n---@field public newText string\n\n---@class lsp.CompletionItemLabelDetails\n---@field public detail? string\n---@field public description? string\n\n---@class lsp.internal.CmpCompletionExtension\n---@field public icon string\n---@field public icon_hl_group string\n---@field public kind_text string\n---@field public kind_hl_group string\n\n---@class lsp.CompletionItem\n---@field public label string\n---@field public labelDetails? lsp.CompletionItemLabelDetails\n---@field public kind? lsp.CompletionItemKind\n---@field public tags? lsp.CompletionItemTag[]\n---@field public detail? string\n---@field public documentation? lsp.MarkupContent|string\n---@field public deprecated? boolean\n---@field public preselect? boolean\n---@field public sortText? string\n---@field public filterText? string\n---@field public insertText? string\n---@field public insertTextFormat? lsp.InsertTextFormat\n---@field public insertTextMode? lsp.InsertTextMode\n---@field public textEdit? lsp.TextEdit|lsp.InsertReplaceTextEdit\n---@field public textEditText? string\n---@field public additionalTextEdits? lsp.TextEdit[]\n---@field public commitCharacters? string[]\n---@field public command? lsp.Command\n---@field public data? any\n---@field public cmp? lsp.internal.CmpCompletionExtension\n---\n---TODO: Should send the issue for upstream?\n---@field public word string|nil\n---@field public dup boolean|nil\n\nreturn lsp\n"
  },
  {
    "path": "lua/cmp/types/lsp_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal lsp = require('cmp.types.lsp')\n\ndescribe('types.lsp', function()\n  before_each(spec.before)\n  describe('Position', function()\n    vim.fn.setline('1', {\n      'あいうえお',\n      'かきくけこ',\n      'さしすせそ',\n    })\n    local vim_position, lsp_position\n\n    local bufnr = vim.api.nvim_get_current_buf()\n    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 3 })\n    assert.are.equal(vim_position.row, 2)\n    assert.are.equal(vim_position.col, 10)\n    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)\n    assert.are.equal(lsp_position.line, 1)\n    assert.are.equal(lsp_position.character, 3)\n\n    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 0 })\n    assert.are.equal(vim_position.row, 2)\n    assert.are.equal(vim_position.col, 1)\n    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)\n    assert.are.equal(lsp_position.line, 1)\n    assert.are.equal(lsp_position.character, 0)\n\n    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 5 })\n    assert.are.equal(vim_position.row, 2)\n    assert.are.equal(vim_position.col, 16)\n    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)\n    assert.are.equal(lsp_position.line, 1)\n    assert.are.equal(lsp_position.character, 5)\n\n    -- overflow (lsp -> vim)\n    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 6 })\n    assert.are.equal(vim_position.row, 2)\n    assert.are.equal(vim_position.col, 16)\n\n    -- overflow(vim -> lsp)\n    vim_position.col = vim_position.col + 1\n    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)\n    assert.are.equal(lsp_position.line, 1)\n    assert.are.equal(lsp_position.character, 5)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/types/vim.lua",
    "content": "---@class vim.CompletedItem\n---@field public word string\n---@field public abbr string|nil\n---@field public icon string|nil\n---@field public kind string|nil\n---@field public menu string|nil\n---@field public equal 1|nil\n---@field public empty 1|nil\n---@field public dup 1|nil\n---@field public id any\n---@field public abbr_hl_group string|table|nil\n---@field public icon_hl_group string|table|nil\n---@field public kind_hl_group string|table|nil\n---@field public menu_hl_group string|table|nil\n\n---@class vim.Position 1-based index\n---@field public row integer\n---@field public col integer\n\n---@class vim.Range\n---@field public start vim.Position\n---@field public end vim.Position\n"
  },
  {
    "path": "lua/cmp/utils/api.lua",
    "content": "local api = {}\n\nlocal CTRL_V = vim.api.nvim_replace_termcodes('<C-v>', true, true, true)\nlocal CTRL_S = vim.api.nvim_replace_termcodes('<C-s>', true, true, true)\n\napi.get_mode = function()\n  local mode = vim.api.nvim_get_mode().mode:sub(1, 1)\n  if mode == 'i' then\n    return 'i' -- insert\n  elseif mode == 'v' or mode == 'V' or mode == CTRL_V then\n    return 'x' -- visual\n  elseif mode == 's' or mode == 'S' or mode == CTRL_S then\n    return 's' -- select\n  elseif mode == 'c' and vim.fn.getcmdtype() ~= '=' then\n    return 'c' -- cmdline\n  end\nend\n\napi.is_insert_mode = function()\n  return api.get_mode() == 'i'\nend\n\napi.is_cmdline_mode = function()\n  return api.get_mode() == 'c'\nend\n\napi.is_select_mode = function()\n  return api.get_mode() == 's'\nend\n\napi.is_visual_mode = function()\n  return api.get_mode() == 'x'\nend\n\napi.is_suitable_mode = function()\n  local mode = api.get_mode()\n  return mode == 'i' or mode == 'c'\nend\n\napi.get_current_line = function()\n  if api.is_cmdline_mode() then\n    return vim.fn.getcmdline()\n  end\n  return vim.api.nvim_get_current_line()\nend\n\n---@return { [1]: integer, [2]: integer }\napi.get_cursor = function()\n  if api.is_cmdline_mode() then\n    return { math.min(vim.o.lines, vim.o.lines - (vim.api.nvim_get_option_value('cmdheight', {}) - 1)), vim.fn.getcmdpos() - 1 }\n  end\n  return vim.api.nvim_win_get_cursor(0)\nend\n\napi.get_screen_cursor = function()\n  if api.is_cmdline_mode() then\n    local cursor = api.get_cursor()\n    return { cursor[1], vim.fn.strdisplaywidth(string.sub(vim.fn.getcmdline(), 1, cursor[2] + 1)) }\n  end\n  local cursor = api.get_cursor()\n  local pos = vim.fn.screenpos(0, cursor[1], cursor[2] + 1)\n  return { pos.row, pos.col - 1 }\nend\n\napi.get_cursor_before_line = function()\n  local cursor = api.get_cursor()\n  return string.sub(api.get_current_line(), 1, cursor[2])\nend\n\n--- Applies a list of text edits to a buffer. Preserves 'buflisted' state.\n---@param text_edits lsp.TextEdit[]\n---@param bufnr integer Buffer id\n---@param position_encoding 'utf-8'|'utf-16'|'utf-32'\napi.apply_text_edits = function(text_edits, bufnr, position_encoding)\n  -- preserve 'buflisted' state because vim.lsp.util.apply_text_edits forces it to true\n  local prev_buflisted = vim.bo[bufnr].buflisted\n  vim.lsp.util.apply_text_edits(text_edits, bufnr, position_encoding)\n  vim.bo[bufnr].buflisted = prev_buflisted\nend\n\nreturn api\n"
  },
  {
    "path": "lua/cmp/utils/api_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal keymap = require('cmp.utils.keymap')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal api = require('cmp.utils.api')\n\ndescribe('api', function()\n  before_each(spec.before)\n  describe('get_cursor', function()\n    it('insert-mode', function()\n      local cursor\n      feedkeys.call(keymap.t('i\\t1234567890'), 'nx', function()\n        cursor = api.get_cursor()\n      end)\n      assert.are.equal(cursor[2], 11)\n    end)\n    it('cmdline-mode', function()\n      local cursor\n      keymap.set_map(0, 'c', '<Plug>(cmp-spec-spy)', function()\n        cursor = api.get_cursor()\n      end, { expr = true, noremap = true })\n      feedkeys.call(keymap.t(':\\t1234567890'), 'n')\n      feedkeys.call(keymap.t('<Plug>(cmp-spec-spy)'), 'x')\n      assert.are.equal(cursor[2], 11)\n    end)\n  end)\n\n  describe('get_screen_cursor', function()\n    it('insert-mode', function()\n      local screen_cursor\n      feedkeys.call(keymap.t('iあいうえお'), 'nx', function()\n        screen_cursor = api.get_screen_cursor()\n      end)\n      assert.are.equal(10, screen_cursor[2])\n    end)\n    it('cmdline-mode', function()\n      local screen_cursor\n      keymap.set_map(0, 'c', '<Plug>(cmp-spec-spy)', function()\n        screen_cursor = api.get_screen_cursor()\n      end, { expr = true, noremap = true })\n      feedkeys.call(keymap.t(':あいうえお'), 'n')\n      feedkeys.call(keymap.t('<Plug>(cmp-spec-spy)'), 'x')\n      assert.are.equal(10, screen_cursor[2])\n    end)\n  end)\n\n  describe('get_cursor_before_line', function()\n    it('insert-mode', function()\n      local cursor_before_line\n      feedkeys.call(keymap.t('i\\t1234567890<Left><Left>'), 'nx', function()\n        cursor_before_line = api.get_cursor_before_line()\n      end)\n      assert.are.same(cursor_before_line, '\\t12345678')\n    end)\n    it('cmdline-mode', function()\n      local cursor_before_line\n      keymap.set_map(0, 'c', '<Plug>(cmp-spec-spy)', function()\n        cursor_before_line = api.get_cursor_before_line()\n      end, { expr = true, noremap = true })\n      feedkeys.call(keymap.t(':\\t1234567890<Left><Left>'), 'n')\n      feedkeys.call(keymap.t('<Plug>(cmp-spec-spy)'), 'x')\n      assert.are.same(cursor_before_line, '\\t12345678')\n    end)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/async.lua",
    "content": "local feedkeys = require('cmp.utils.feedkeys')\nlocal config = require('cmp.config')\n\nlocal async = {}\n\n---@class cmp.AsyncThrottle\n---@field public running boolean\n---@field public timeout integer\n---@field public sync function(self: cmp.AsyncThrottle, timeout: integer|nil)\n---@field public stop function\n---@field public __call function\n\n---@type uv_timer_t[]\nlocal timers = {}\n\nvim.api.nvim_create_autocmd('VimLeavePre', {\n  callback = function()\n    for _, timer in pairs(timers) do\n      if timer and not timer:is_closing() then\n        timer:stop()\n        timer:close()\n      end\n    end\n  end,\n})\n\n---@param fn function\n---@param timeout integer\n---@return cmp.AsyncThrottle\nasync.throttle = function(fn, timeout)\n  local time = nil\n  local timer = assert(vim.loop.new_timer())\n  local _async = nil ---@type Async?\n  timers[#timers + 1] = timer\n  local throttle\n  throttle = setmetatable({\n    running = false,\n    timeout = timeout,\n    sync = function(self, timeout_)\n      if not self.running then\n        return\n      end\n      vim.wait(timeout_ or 1000, function()\n        return not self.running\n      end, 10)\n    end,\n    stop = function(reset_time)\n      if reset_time ~= false then\n        time = nil\n      end\n      -- can't use self here unfortunately\n      throttle.running = false\n      timer:stop()\n      if _async then\n        _async:cancel()\n        _async = nil\n      end\n    end,\n  }, {\n    __call = function(self, ...)\n      local args = { ... }\n\n      if time == nil then\n        time = vim.loop.now()\n      end\n      self.stop(false)\n      self.running = true\n      timer:start(math.max(1, self.timeout - (vim.loop.now() - time)), 0, function()\n        vim.schedule(function()\n          time = nil\n          local ret = fn(unpack(args))\n          if async.is_async(ret) then\n            ---@cast ret Async\n            _async = ret\n            _async:await(function(_, error)\n              _async = nil\n              self.running = false\n              if error and error ~= 'abort' then\n                vim.notify(error, vim.log.levels.ERROR)\n              end\n            end)\n          else\n            self.running = false\n          end\n        end)\n      end)\n    end,\n  })\n  return throttle\nend\n\n---Control async tasks.\nasync.step = function(...)\n  local tasks = { ... }\n  local next\n  next = function(...)\n    if #tasks > 0 then\n      table.remove(tasks, 1)(next, ...)\n    end\n  end\n  table.remove(tasks, 1)(next)\nend\n\n---Timeout callback function\n---@param fn function\n---@param timeout integer\n---@return function\nasync.timeout = function(fn, timeout)\n  local timer\n  local done = false\n  local callback = function(...)\n    if not done then\n      done = true\n      timer:stop()\n      timer:close()\n      fn(...)\n    end\n  end\n  timer = vim.loop.new_timer()\n  timer:start(timeout, 0, function()\n    callback()\n  end)\n  return callback\nend\n\n---@alias cmp.AsyncDedup fun(callback: function): function\n\n---Create deduplicated callback\n---@return function\nasync.dedup = function()\n  local id = 0\n  return function(callback)\n    id = id + 1\n\n    local current = id\n    return function(...)\n      if current == id then\n        callback(...)\n      end\n    end\n  end\nend\n\n---Convert async process as sync\nasync.sync = function(runner, timeout)\n  local done = false\n  runner(function()\n    done = true\n  end)\n  vim.wait(timeout, function()\n    return done\n  end, 10, false)\nend\n\n---Wait and callback for next safe state.\nasync.debounce_next_tick = function(callback)\n  local running = false\n  return function()\n    if running then\n      return\n    end\n    running = true\n    vim.schedule(function()\n      running = false\n      callback()\n    end)\n  end\nend\n\n---Wait and callback for consuming next keymap.\nasync.debounce_next_tick_by_keymap = function(callback)\n  return function()\n    feedkeys.call('', '', callback)\n  end\nend\n\nlocal Scheduler = {}\nScheduler._queue = {}\nScheduler._executor = assert(vim.loop.new_check())\n\nfunction Scheduler.step()\n  local budget = config.get().performance.async_budget * 1e6\n  local start = vim.loop.hrtime()\n  while #Scheduler._queue > 0 and vim.loop.hrtime() - start < budget do\n    local a = table.remove(Scheduler._queue, 1)\n    a:_step()\n    if a.running then\n      table.insert(Scheduler._queue, a)\n    end\n  end\n  if #Scheduler._queue == 0 then\n    return Scheduler._executor:stop()\n  end\nend\n\n---@param a Async\nfunction Scheduler.add(a)\n  table.insert(Scheduler._queue, a)\n  if not Scheduler._executor:is_active() then\n    Scheduler._executor:start(vim.schedule_wrap(Scheduler.step))\n  end\nend\n\n--- @alias AsyncCallback fun(result?:any, error?:string)\n\n--- @class Async\n--- @field running boolean\n--- @field result? any\n--- @field error? string\n--- @field callbacks AsyncCallback[]\n--- @field thread thread\nlocal Async = {}\nAsync.__index = Async\n\nfunction Async.new(fn)\n  local self = setmetatable({}, Async)\n  self.callbacks = {}\n  self.running = true\n  self.thread = coroutine.create(fn)\n  Scheduler.add(self)\n  return self\nend\n\n---@param result? any\n---@param error? string\nfunction Async:_done(result, error)\n  if self.running then\n    self.running = false\n    self.result = result\n    self.error = error\n  end\n  for _, callback in ipairs(self.callbacks) do\n    callback(result, error)\n  end\n  -- only run each callback once.\n  -- _done can possibly be called multiple times.\n  -- so we need to clear callbacks after executing them.\n  self.callbacks = {}\nend\n\nfunction Async:_step()\n  local ok, res = coroutine.resume(self.thread)\n  if not ok then\n    return self:_done(nil, res)\n  elseif res == 'abort' then\n    return self:_done(nil, 'abort')\n  elseif coroutine.status(self.thread) == 'dead' then\n    return self:_done(res)\n  end\nend\n\nfunction Async:cancel()\n  self:_done(nil, 'abort')\nend\n\n---@param cb AsyncCallback\nfunction Async:await(cb)\n  if not cb then\n    error('callback is required')\n  end\n  if self.running then\n    table.insert(self.callbacks, cb)\n  else\n    cb(self.result, self.error)\n  end\nend\n\nfunction Async:sync()\n  while self.running do\n    vim.wait(10)\n  end\n  return self.error and error(self.error) or self.result\nend\n\n--- @return boolean\nfunction async.is_async(obj)\n  return obj and type(obj) == 'table' and getmetatable(obj) == Async\nend\n\n--- @return fun(...): Async\nfunction async.wrap(fn)\n  return function(...)\n    local args = { ... }\n    return Async.new(function()\n      return fn(unpack(args))\n    end)\n  end\nend\n\n-- This will yield when called from a coroutine\nfunction async.yield(...)\n  if coroutine.running() == nil then\n    error('Trying to yield from a non-yieldable context')\n    return ...\n  end\n  return coroutine.yield(...)\nend\n\nfunction async.abort()\n  return async.yield('abort')\nend\n\nreturn async\n"
  },
  {
    "path": "lua/cmp/utils/async_spec.lua",
    "content": "local async = require('cmp.utils.async')\n\ndescribe('utils.async', function()\n  it('throttle', function()\n    local count = 0\n    local now\n    local f = async.throttle(function()\n      count = count + 1\n    end, 100)\n\n    -- 1. delay for 100ms\n    now = vim.loop.now()\n    f.timeout = 100\n    f()\n    vim.wait(1000, function()\n      return count == 1\n    end)\n    assert.is.truthy(math.abs(f.timeout - (vim.loop.now() - now)) < 10)\n\n    -- 2. delay for 500ms\n    now = vim.loop.now()\n    f.timeout = 500\n    f()\n    vim.wait(1000, function()\n      return count == 2\n    end)\n    assert.is.truthy(math.abs(f.timeout - (vim.loop.now() - now)) < 10)\n\n    -- 4. delay for 500ms and wait 100ms (remain 400ms)\n    f.timeout = 500\n    f()\n    vim.wait(100) -- remain 400ms\n\n    -- 5. call immediately (100ms already elapsed from No.4)\n    now = vim.loop.now()\n    f.timeout = 100\n    f()\n    vim.wait(1000, function()\n      return count == 3\n    end)\n    assert.is.truthy(math.abs(vim.loop.now() - now) < 10)\n  end)\n  it('step', function()\n    local done = false\n    local step = {}\n    async.step(function(next)\n      vim.defer_fn(function()\n        table.insert(step, 1)\n        next()\n      end, 10)\n    end, function(next)\n      vim.defer_fn(function()\n        table.insert(step, 2)\n        next()\n      end, 10)\n    end, function(next)\n      vim.defer_fn(function()\n        table.insert(step, 3)\n        next()\n      end, 10)\n    end, function()\n      done = true\n    end)\n    vim.wait(1000, function()\n      return done\n    end)\n    assert.are.same(step, { 1, 2, 3 })\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/autocmd.lua",
    "content": "local debug = require('cmp.utils.debug')\n\nlocal autocmd = {}\n\nautocmd.group = vim.api.nvim_create_augroup('___cmp___', { clear = true })\n\nautocmd.events = {}\n\nlocal function create_autocmd(event)\n  vim.api.nvim_create_autocmd(event, {\n    desc = ('nvim-cmp: autocmd: %s'):format(event),\n    group = autocmd.group,\n    callback = function()\n      autocmd.emit(event)\n    end,\n  })\nend\n\n---Subscribe autocmd\n---@param events string|string[]\n---@param callback function\n---@return function\nautocmd.subscribe = function(events, callback)\n  events = type(events) == 'string' and { events } or events\n\n  for _, event in ipairs(events) do\n    if not autocmd.events[event] then\n      autocmd.events[event] = {}\n      create_autocmd(event)\n    end\n    table.insert(autocmd.events[event], callback)\n  end\n\n  return function()\n    for _, event in ipairs(events) do\n      for i, callback_ in ipairs(autocmd.events[event]) do\n        if callback_ == callback then\n          table.remove(autocmd.events[event], i)\n          break\n        end\n      end\n    end\n  end\nend\n\n---Emit autocmd\n---@param event string\nautocmd.emit = function(event)\n  debug.log(' ')\n  debug.log(string.format('>>> %s', event))\n  autocmd.events[event] = autocmd.events[event] or {}\n  for _, callback in ipairs(autocmd.events[event]) do\n    callback()\n  end\nend\n\n---Resubscribe to events\n---@param events string[]\nautocmd.resubscribe = function(events)\n  -- Delete the autocommands if present\n  local found = vim.api.nvim_get_autocmds({\n    group = autocmd.group,\n    event = events,\n  })\n  for _, to_delete in ipairs(found) do\n    vim.api.nvim_del_autocmd(to_delete.id)\n  end\n\n  -- Recreate if event is known\n  for _, event in ipairs(events) do\n    if autocmd.events[event] then\n      create_autocmd(event)\n    end\n  end\nend\n\nreturn autocmd\n"
  },
  {
    "path": "lua/cmp/utils/binary.lua",
    "content": "local binary = {}\n\n---Insert item to list to ordered index\n---@param list any[]\n---@param item any\n---@param func fun(a: any, b: any): 1|-1|0\nbinary.insort = function(list, item, func)\n  table.insert(list, binary.search(list, item, func), item)\nend\n\n---Search suitable index from list\n---@param list any[]\n---@param item any\n---@param func fun(a: any, b: any): 1|-1|0\n---@return integer\nbinary.search = function(list, item, func)\n  local s = 1\n  local e = #list\n  while s <= e do\n    local idx = math.floor((e + s) / 2)\n    local diff = func(item, list[idx])\n    if diff > 0 then\n      s = idx + 1\n    elseif diff < 0 then\n      e = idx - 1\n    else\n      return idx + 1\n    end\n  end\n  return s\nend\n\nreturn binary\n"
  },
  {
    "path": "lua/cmp/utils/binary_spec.lua",
    "content": "local binary = require('cmp.utils.binary')\n\ndescribe('utils.binary', function()\n  it('insort', function()\n    local func = function(a, b)\n      return a.score - b.score\n    end\n    local list = {}\n    binary.insort(list, { id = 'a', score = 1 }, func)\n    binary.insort(list, { id = 'b', score = 5 }, func)\n    binary.insort(list, { id = 'c', score = 2.5 }, func)\n    binary.insort(list, { id = 'd', score = 2 }, func)\n    binary.insort(list, { id = 'e', score = 8 }, func)\n    binary.insort(list, { id = 'g', score = 8 }, func)\n    binary.insort(list, { id = 'h', score = 7 }, func)\n    binary.insort(list, { id = 'i', score = 6 }, func)\n    binary.insort(list, { id = 'j', score = 4 }, func)\n    assert.are.equal(list[1].id, 'a')\n    assert.are.equal(list[2].id, 'd')\n    assert.are.equal(list[3].id, 'c')\n    assert.are.equal(list[4].id, 'j')\n    assert.are.equal(list[5].id, 'b')\n    assert.are.equal(list[6].id, 'i')\n    assert.are.equal(list[7].id, 'h')\n    assert.are.equal(list[8].id, 'e')\n    assert.are.equal(list[9].id, 'g')\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/buffer.lua",
    "content": "local buffer = {}\n\nbuffer.cache = {}\n\n---@return integer buf\nbuffer.get = function(name)\n  local buf = buffer.cache[name]\n  if buf and vim.api.nvim_buf_is_valid(buf) then\n    return buf\n  else\n    return nil\n  end\nend\n\n---@return integer buf\n---@return boolean created_new\nbuffer.ensure = function(name)\n  local created_new = false\n  local buf = buffer.get(name)\n  if not buf then\n    created_new = true\n    buf = vim.api.nvim_create_buf(false, true)\n    buffer.cache[name] = buf\n  end\n  return buf, created_new\nend\n\nreturn buffer\n"
  },
  {
    "path": "lua/cmp/utils/cache.lua",
    "content": "---@class cmp.Cache\n---@field public entries any\nlocal cache = {}\n\ncache.new = function()\n  local self = setmetatable({}, { __index = cache })\n  self.entries = {}\n  return self\nend\n\n---Get cache value\n---@param key string|string[]\n---@return any|nil\ncache.get = function(self, key)\n  key = self:key(key)\n  if self.entries[key] ~= nil then\n    return self.entries[key]\n  end\n  return nil\nend\n\n---Set cache value explicitly\n---@param key string|string[]\n---@vararg any\ncache.set = function(self, key, value)\n  key = self:key(key)\n  self.entries[key] = value\nend\n\n---Ensure value by callback\n---@generic T\n---@param key string|string[]\n---@param callback fun(...): T\n---@return T\ncache.ensure = function(self, key, callback, ...)\n  local value = self:get(key)\n  if value == nil then\n    local v = callback(...)\n    self:set(key, v)\n    return v\n  end\n  return value\nend\n\n---Clear all cache entries\ncache.clear = function(self)\n  self.entries = {}\nend\n\n---Create key\n---@param key string|string[]\n---@return string\ncache.key = function(_, key)\n  if type(key) == 'table' then\n    return table.concat(key, ':')\n  end\n  return key\nend\n\nreturn cache\n"
  },
  {
    "path": "lua/cmp/utils/char.lua",
    "content": "local _\n\nlocal alpha = {}\n_ = string.gsub('abcdefghijklmnopqrstuvwxyz', '.', function(char)\n  alpha[string.byte(char)] = true\nend)\n\nlocal ALPHA = {}\n_ = string.gsub('ABCDEFGHIJKLMNOPQRSTUVWXYZ', '.', function(char)\n  ALPHA[string.byte(char)] = true\nend)\n\nlocal digit = {}\n_ = string.gsub('1234567890', '.', function(char)\n  digit[string.byte(char)] = true\nend)\n\nlocal white = {}\n_ = string.gsub(' \\t\\n', '.', function(char)\n  white[string.byte(char)] = true\nend)\n\nlocal char = {}\n\n---@param byte integer\n---@return boolean\nchar.is_upper = function(byte)\n  return ALPHA[byte]\nend\n\n---@param byte integer\n---@return boolean\nchar.is_alpha = function(byte)\n  return alpha[byte] or ALPHA[byte]\nend\n\n---@param byte integer\n---@return boolean\nchar.is_digit = function(byte)\n  return digit[byte]\nend\n\n---@param byte integer\n---@return boolean\nchar.is_white = function(byte)\n  return white[byte]\nend\n\n---@param byte integer\n---@return boolean\nchar.is_symbol = function(byte)\n  return not (char.is_alnum(byte) or char.is_white(byte))\nend\n\n---@param byte integer\n---@return boolean\nchar.is_printable = function(byte)\n  return string.match(string.char(byte), '^%c$') == nil\nend\n\n---@param byte integer\n---@return boolean\nchar.is_alnum = function(byte)\n  return char.is_alpha(byte) or char.is_digit(byte)\nend\n\n---@param text string\n---@param index integer\n---@return boolean\nchar.is_semantic_index = function(text, index)\n  if index <= 1 then\n    return true\n  end\n\n  local prev = string.byte(text, index - 1)\n  local curr = string.byte(text, index)\n\n  if not char.is_upper(prev) and char.is_upper(curr) then\n    return true\n  end\n  if char.is_symbol(curr) or char.is_white(curr) then\n    return true\n  end\n  if not char.is_alpha(prev) and char.is_alpha(curr) then\n    return true\n  end\n  if not char.is_digit(prev) and char.is_digit(curr) then\n    return true\n  end\n  return false\nend\n\n---@param text string\n---@param current_index integer\n---@return integer\nchar.get_next_semantic_index = function(text, current_index)\n  for i = current_index + 1, #text do\n    if char.is_semantic_index(text, i) then\n      return i\n    end\n  end\n  return #text + 1\nend\n\n---Ignore case match\n---@param byte1 integer\n---@param byte2 integer\n---@return boolean\nchar.match = function(byte1, byte2)\n  if not char.is_alpha(byte1) or not char.is_alpha(byte2) then\n    return byte1 == byte2\n  end\n  local diff = byte1 - byte2\n  return diff == 0 or diff == 32 or diff == -32\nend\n\nreturn char\n"
  },
  {
    "path": "lua/cmp/utils/debug.lua",
    "content": "local debug = {}\n\ndebug.flag = false\n\n---Print log\n---@vararg any\ndebug.log = function(...)\n  if debug.flag then\n    local data = {}\n    for _, v in ipairs({ ... }) do\n      if not vim.tbl_contains({ 'string', 'number', 'boolean' }, type(v)) then\n        v = vim.inspect(v)\n      end\n      table.insert(data, v)\n    end\n    print(table.concat(data, '\\t'))\n  end\nend\n\nreturn debug\n"
  },
  {
    "path": "lua/cmp/utils/event.lua",
    "content": "---@class cmp.Event\n---@field private events table<string, function[]>\nlocal event = {}\n\n---Create vents\nevent.new = function()\n  local self = setmetatable({}, { __index = event })\n  self.events = {}\n  return self\nend\n\n---Add event listener\n---@param name string\n---@param callback function\n---@return function\nevent.on = function(self, name, callback)\n  if not self.events[name] then\n    self.events[name] = {}\n  end\n  table.insert(self.events[name], callback)\n  return function()\n    self:off(name, callback)\n  end\nend\n\n---Remove event listener\n---@param name string\n---@param callback function\nevent.off = function(self, name, callback)\n  for i, callback_ in ipairs(self.events[name] or {}) do\n    if callback_ == callback then\n      table.remove(self.events[name], i)\n      break\n    end\n  end\nend\n\n---Remove all events\nevent.clear = function(self)\n  self.events = {}\nend\n\n---Emit event\n---@param name string\nevent.emit = function(self, name, ...)\n  for _, callback in ipairs(self.events[name] or {}) do\n    if type(callback) == 'function' then\n      callback(...)\n    end\n  end\nend\n\nreturn event\n"
  },
  {
    "path": "lua/cmp/utils/feedkeys.lua",
    "content": "local keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\n\nlocal feedkeys = {}\n\nfeedkeys.call = setmetatable({\n  callbacks = {},\n}, {\n  __call = function(self, keys, mode, callback)\n    local is_insert = string.match(mode, 'i') ~= nil\n    local is_immediate = string.match(mode, 'x') ~= nil\n\n    local queue = {}\n    if #keys > 0 then\n      table.insert(queue, { keymap.t('<Cmd>setlocal lazyredraw<CR>'), 'n' })\n      table.insert(queue, { keymap.t('<Cmd>setlocal textwidth=0<CR>'), 'n' })\n      table.insert(queue, { keymap.t('<Cmd>setlocal backspace=nostop<CR>'), 'n' })\n      table.insert(queue, { keys, string.gsub(mode, '[itx]', ''), true })\n      table.insert(queue, { keymap.t('<Cmd>setlocal %slazyredraw<CR>'):format(vim.o.lazyredraw and '' or 'no'), 'n' })\n      table.insert(queue, { keymap.t('<Cmd>setlocal textwidth=%s<CR>'):format(vim.bo.textwidth or 0), 'n' })\n      table.insert(queue, { keymap.t('<Cmd>setlocal backspace=%s<CR>'):format(vim.go.backspace or 2), 'n' })\n    end\n\n    if callback then\n      local id = misc.id('cmp.utils.feedkeys.call')\n      self.callbacks[id] = callback\n      table.insert(queue, { keymap.t('<Cmd>lua require\"cmp.utils.feedkeys\".run(%s)<CR>'):format(id), 'n', true })\n    end\n\n    if is_insert then\n      for i = #queue, 1, -1 do\n        vim.api.nvim_feedkeys(queue[i][1], queue[i][2] .. 'i', queue[i][3])\n      end\n    else\n      for i = 1, #queue do\n        vim.api.nvim_feedkeys(queue[i][1], queue[i][2], queue[i][3])\n      end\n    end\n\n    if is_immediate then\n      vim.api.nvim_feedkeys('', 'x', true)\n    end\n  end,\n})\nfeedkeys.run = function(id)\n  if feedkeys.call.callbacks[id] then\n    local ok, err = pcall(feedkeys.call.callbacks[id])\n    if not ok then\n      vim.notify(err, vim.log.levels.ERROR)\n    end\n    feedkeys.call.callbacks[id] = nil\n  end\n  return ''\nend\n\nreturn feedkeys\n"
  },
  {
    "path": "lua/cmp/utils/feedkeys_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal keymap = require('cmp.utils.keymap')\n\nlocal feedkeys = require('cmp.utils.feedkeys')\n\ndescribe('feedkeys', function()\n  before_each(spec.before)\n\n  it('dot-repeat', function()\n    local reg\n    feedkeys.call(keymap.t('iaiueo<Esc>'), 'nx', function()\n      reg = vim.fn.getreg('.')\n    end)\n    assert.are.equal(reg, keymap.t('aiueo'))\n  end)\n\n  it('textwidth', function()\n    vim.cmd([[setlocal textwidth=6]])\n    feedkeys.call(keymap.t('iaiueo '), 'nx')\n    feedkeys.call(keymap.t('aaiueoaiueo'), 'nx')\n    assert.are.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), {\n      'aiueo aiueoaiueo',\n    })\n  end)\n\n  it('backspace', function()\n    vim.cmd([[setlocal backspace=\"\"]])\n    feedkeys.call(keymap.t('iaiueo'), 'nx')\n    feedkeys.call(keymap.t('a<BS><BS>'), 'nx')\n    assert.are.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), {\n      'aiu',\n    })\n  end)\n\n  it('testability', function()\n    feedkeys.call('i', 'n', function()\n      feedkeys.call('', 'n', function()\n        feedkeys.call('aiueo', 'in')\n      end)\n      feedkeys.call('', 'n', function()\n        feedkeys.call(keymap.t('<BS><BS><BS><BS><BS>'), 'in')\n      end)\n      feedkeys.call('', 'n', function()\n        feedkeys.call(keymap.t('abcde'), 'in')\n      end)\n      feedkeys.call('', 'n', function()\n        feedkeys.call(keymap.t('<BS><BS><BS><BS><BS>'), 'in')\n      end)\n      feedkeys.call('', 'n', function()\n        feedkeys.call(keymap.t('12345'), 'in')\n      end)\n    end)\n    feedkeys.call('', 'x')\n    assert.are.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { '12345' })\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/highlight.lua",
    "content": "local highlight = {}\n\nhighlight.keys = {\n  'fg',\n  'bg',\n  'bold',\n  'italic',\n  'reverse',\n  'standout',\n  'underline',\n  'undercurl',\n  'strikethrough',\n}\n\nhighlight.inherit = function(name, source, settings)\n  for _, key in ipairs(highlight.keys) do\n    if not settings[key] then\n      local v = vim.fn.synIDattr(vim.fn.hlID(source), key)\n      if key == 'fg' or key == 'bg' then\n        local n = tonumber(v, 10)\n        v = type(n) == 'number' and n or v\n      else\n        v = v == 1\n      end\n      settings[key] = v == '' and 'NONE' or v\n    end\n  end\n  vim.api.nvim_set_hl(0, name, settings)\nend\n\nreturn highlight\n"
  },
  {
    "path": "lua/cmp/utils/keymap.lua",
    "content": "local misc = require('cmp.utils.misc')\nlocal buffer = require('cmp.utils.buffer')\nlocal api = require('cmp.utils.api')\n\nlocal keymap = {}\n\n---Shortcut for nvim_replace_termcodes\n---@param keys string\n---@return string\nkeymap.t = function(keys)\n  return (string.gsub(keys, \"(<[A-Za-z0-9\\\\%-%[%]%^@;,:_'`%./]->)\", function(match)\n    return vim.api.nvim_eval(string.format([[\"\\%s\"]], match))\n  end))\nend\n\n---Normalize key sequence.\n---@param keys string\n---@return string\nkeymap.normalize = vim.fn.has('nvim-0.8') == 1 and function(keys)\n    local t = string.gsub(keys, \"<([A-Za-z0-9\\\\%-%[%]%^@;,:_'`%./]-)>\", function(match)\n      -- Use the \\<* notation, which distinguishes <C-J> from <NL>, etc.\n      return vim.api.nvim_eval(string.format([[\"\\<*%s>\"]], match))\n    end)\n    return vim.fn.keytrans(t)\n  end or function(keys)\n    local normalize_buf = buffer.ensure('cmp.util.keymap.normalize')\n    vim.api.nvim_buf_set_keymap(normalize_buf, 't', keys, '<Plug>(cmp.utils.keymap.normalize)', {})\n    for _, map in ipairs(vim.api.nvim_buf_get_keymap(normalize_buf, 't')) do\n      if keymap.t(map.rhs) == keymap.t('<Plug>(cmp.utils.keymap.normalize)') then\n        vim.api.nvim_buf_del_keymap(normalize_buf, 't', keys)\n        return map.lhs\n      end\n    end\n    vim.api.nvim_buf_del_keymap(normalize_buf, 't', keys)\n    vim.api.nvim_buf_delete(normalize_buf, {})\n    return keys\n  end\n\n---Return vim notation keymapping (simple conversion).\n---@param s string\n---@return string\nkeymap.to_keymap = setmetatable({\n  ['<CR>'] = { '\\n', '\\r', '\\r\\n' },\n  ['<Tab>'] = { '\\t' },\n  ['<BSlash>'] = { '\\\\' },\n  ['<Bar>'] = { '|' },\n  ['<Space>'] = { ' ' },\n}, {\n  __call = function(self, s)\n    return string.gsub(s, '.', function(c)\n      for key, chars in pairs(self) do\n        if vim.tbl_contains(chars, c) then\n          return key\n        end\n      end\n      return c\n    end)\n  end,\n})\n\n---Mode safe break undo\nkeymap.undobreak = function()\n  if not api.is_insert_mode() then\n    return ''\n  end\n  return keymap.t('<C-g>u')\nend\n\n---Mode safe join undo\nkeymap.undojoin = function()\n  if not api.is_insert_mode() then\n    return ''\n  end\n  return keymap.t('<C-g>U')\nend\n\n---Create backspace keys.\n---@param count string|integer\n---@return string\nkeymap.backspace = function(count)\n  if type(count) == 'string' then\n    count = vim.fn.strchars(count, true)\n  end\n  if count <= 0 then\n    return ''\n  end\n  local keys = {}\n  table.insert(keys, keymap.t(string.rep('<BS>', count)))\n  return table.concat(keys, '')\nend\n\n---Create delete keys.\n---@param count string|integer\n---@return string\nkeymap.delete = function(count)\n  if type(count) == 'string' then\n    count = vim.fn.strchars(count, true)\n  end\n  if count <= 0 then\n    return ''\n  end\n  local keys = {}\n  table.insert(keys, keymap.t(string.rep('<Del>', count)))\n  return table.concat(keys, '')\nend\n\n---Update indentkeys.\n---@param expr? string\n---@return string\nkeymap.indentkeys = function(expr)\n  return string.format(keymap.t('<Cmd>set indentkeys=%s<CR>'), expr and vim.fn.escape(expr, '| \\t\\\\') or '')\nend\n\n---Return two key sequence are equal or not.\n---@param a string\n---@param b string\n---@return boolean\nkeymap.equals = function(a, b)\n  return keymap.normalize(a) == keymap.normalize(b)\nend\n\n---Register keypress handler.\nkeymap.listen = function(mode, lhs, callback)\n  lhs = keymap.normalize(keymap.to_keymap(lhs))\n\n  local existing = keymap.get_map(mode, lhs)\n  if existing.desc == 'cmp.utils.keymap.set_map' then\n    return\n  end\n\n  local bufnr = existing.buffer and vim.api.nvim_get_current_buf() or -1\n  local fallback = keymap.fallback(bufnr, mode, existing)\n  keymap.set_map(bufnr, mode, lhs, function()\n    local ignore = false\n    ignore = ignore or (mode == 'c' and vim.fn.getcmdtype() == '=')\n    if ignore then\n      fallback()\n    else\n      callback(lhs, misc.once(fallback))\n    end\n  end, {\n    expr = false,\n    noremap = true,\n    silent = true,\n  })\nend\n\n---Fallback\nkeymap.fallback = function(bufnr, mode, map)\n  return function()\n    if map.expr then\n      local fallback_lhs = string.format('<Plug>(cmp.u.k.fallback_expr:%s)', map.lhs)\n      keymap.set_map(bufnr, mode, fallback_lhs, function()\n        return keymap.solve(bufnr, mode, map).keys\n      end, {\n        expr = true,\n        noremap = map.noremap,\n        script = map.script,\n        nowait = map.nowait,\n        silent = map.silent and mode ~= 'c',\n        replace_keycodes = map.replace_keycodes,\n      })\n      vim.api.nvim_feedkeys(keymap.t(fallback_lhs), 'im', true)\n    elseif map.callback then\n      map.callback()\n    else\n      local solved = keymap.solve(bufnr, mode, map)\n      vim.api.nvim_feedkeys(solved.keys, solved.mode, true)\n    end\n  end\nend\n\n---Solve\nkeymap.solve = function(bufnr, mode, map)\n  local lhs = keymap.t(map.lhs)\n  local rhs = keymap.t(map.rhs)\n  if map.expr then\n    if map.callback then\n      rhs = map.callback()\n    else\n      rhs = vim.api.nvim_eval(keymap.t(map.rhs))\n    end\n  end\n\n  if map.noremap then\n    return { keys = rhs, mode = 'in' }\n  end\n\n  if string.find(rhs, lhs, 1, true) == 1 then\n    local recursive = string.format('<SNR>0_(cmp.u.k.recursive:%s)', lhs)\n    keymap.set_map(bufnr, mode, recursive, lhs, {\n      noremap = true,\n      script = true,\n      nowait = map.nowait,\n      silent = map.silent and mode ~= 'c',\n      replace_keycodes = map.replace_keycodes,\n    })\n    return { keys = keymap.t(recursive) .. string.gsub(rhs, '^' .. vim.pesc(lhs), ''), mode = 'im' }\n  end\n  return { keys = rhs, mode = 'im' }\nend\n\n---Get map\n---@param mode string\n---@param lhs string\n---@return table\nkeymap.get_map = function(mode, lhs)\n  lhs = keymap.normalize(lhs)\n\n  for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do\n    if keymap.equals(map.lhs, lhs) then\n      return {\n        lhs = map.lhs,\n        rhs = map.rhs or '',\n        expr = map.expr == 1,\n        callback = map.callback,\n        desc = map.desc,\n        noremap = map.noremap == 1,\n        script = map.script == 1,\n        silent = map.silent == 1,\n        nowait = map.nowait == 1,\n        buffer = true,\n        replace_keycodes = map.replace_keycodes == 1,\n      }\n    end\n  end\n\n  for _, map in ipairs(vim.api.nvim_get_keymap(mode)) do\n    if keymap.equals(map.lhs, lhs) then\n      return {\n        lhs = map.lhs,\n        rhs = map.rhs or '',\n        expr = map.expr == 1,\n        callback = map.callback,\n        desc = map.desc,\n        noremap = map.noremap == 1,\n        script = map.script == 1,\n        silent = map.silent == 1,\n        nowait = map.nowait == 1,\n        buffer = false,\n        replace_keycodes = map.replace_keycodes == 1,\n      }\n    end\n  end\n\n  return {\n    lhs = lhs,\n    rhs = lhs,\n    expr = false,\n    callback = nil,\n    noremap = true,\n    script = false,\n    silent = true,\n    nowait = false,\n    buffer = false,\n    replace_keycodes = true,\n  }\nend\n\n---Set keymapping\nkeymap.set_map = function(bufnr, mode, lhs, rhs, opts)\n  if type(rhs) == 'function' then\n    opts.callback = rhs\n    rhs = ''\n  end\n  opts.desc = 'cmp.utils.keymap.set_map'\n\n  if vim.fn.has('nvim-0.8') == 0 then\n    opts.replace_keycodes = nil\n  end\n\n  if bufnr == -1 then\n    vim.api.nvim_set_keymap(mode, lhs, rhs, opts)\n  else\n    vim.api.nvim_buf_set_keymap(bufnr, mode, lhs, rhs, opts)\n  end\nend\n\nreturn keymap\n"
  },
  {
    "path": "lua/cmp/utils/keymap_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\nlocal api = require('cmp.utils.api')\nlocal feedkeys = require('cmp.utils.feedkeys')\n\nlocal keymap = require('cmp.utils.keymap')\n\ndescribe('keymap', function()\n  before_each(spec.before)\n\n  it('t', function()\n    for _, key in ipairs({\n      '<F1>',\n      '<C-a>',\n      '<C-]>',\n      '<C-[>',\n      '<C-^>',\n      '<C-@>',\n      '<C-\\\\>',\n      '<C-;>',\n      '<C-,>',\n      '<C-:>',\n      '<C-.>',\n      '<C-/>',\n      '<C-_>',\n      \"<C-'>\",\n      '<M-`>',\n      '<Tab>',\n      '<S-Tab>',\n      '<Plug>(example)',\n      '<C-r>=\"abc\"<CR>',\n      '<Cmd>normal! ==<CR>',\n    }) do\n      assert.are.equal(keymap.t(key), vim.api.nvim_replace_termcodes(key, true, true, true))\n      assert.are.equal(keymap.t(key .. key), vim.api.nvim_replace_termcodes(key .. key, true, true, true))\n      assert.are.equal(keymap.t(key .. key .. key), vim.api.nvim_replace_termcodes(key .. key .. key, true, true, true))\n    end\n  end)\n\n  it('to_keymap', function()\n    assert.are.equal(keymap.to_keymap('\\n'), '<CR>')\n    assert.are.equal(keymap.to_keymap('<CR>'), '<CR>')\n    assert.are.equal(keymap.to_keymap('|'), '<Bar>')\n  end)\n\n  describe('fallback', function()\n    before_each(spec.before)\n\n    local run_fallback = function(keys, fallback)\n      local state = {}\n      feedkeys.call(keys, '', function()\n        fallback()\n      end)\n      feedkeys.call('', '', function()\n        if api.is_cmdline_mode() then\n          state.buffer = { api.get_current_line() }\n        else\n          state.buffer = vim.api.nvim_buf_get_lines(0, 0, -1, false)\n        end\n        state.cursor = api.get_cursor()\n      end)\n      feedkeys.call('', 'x')\n      return state\n    end\n\n    describe('basic', function()\n      it('<Plug>', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '<Plug>(pairs)', '()<Left>', { noremap = true })\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '<Plug>(pairs)', { noremap = false })\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      it('<C-r>=', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '<C-r>=\"()\"<CR><Left>', {})\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      it('callback', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {\n          callback = function()\n            vim.api.nvim_feedkeys('()' .. keymap.t('<Left>'), 'int', true)\n          end,\n        })\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      it('expr-callback', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {\n          expr = true,\n          noremap = false,\n          silent = true,\n          callback = function()\n            return '()' .. keymap.t('<Left>')\n          end,\n        })\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      -- it('cmdline default <Tab>', function()\n      --   local fallback = keymap.fallback(0, 'c', keymap.get_map('c', '<Tab>'))\n      --   local state = run_fallback(':', fallback)\n      --   assert.are.same({ '' }, state.buffer)\n      --   assert.are.same({ 1, 0 }, state.cursor)\n      -- end)\n    end)\n\n    describe('recursive', function()\n      it('non-expr', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '()<Left>', {\n          expr = false,\n          noremap = false,\n          silent = true,\n        })\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      it('expr', function()\n        vim.api.nvim_buf_set_keymap(0, 'i', '(', '\"()<Left>\"', {\n          expr = true,\n          noremap = false,\n          silent = true,\n        })\n        local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n        local state = run_fallback('i', fallback)\n        assert.are.same({ '()' }, state.buffer)\n        assert.are.same({ 1, 1 }, state.cursor)\n      end)\n\n      it('expr-callback', function()\n        pcall(function()\n          vim.api.nvim_buf_set_keymap(0, 'i', '(', '', {\n            expr = true,\n            noremap = false,\n            silent = true,\n            callback = function()\n              return keymap.t('()<Left>')\n            end,\n          })\n          local fallback = keymap.fallback(0, 'i', keymap.get_map('i', '('))\n          local state = run_fallback('i', fallback)\n          assert.are.same({ '()' }, state.buffer)\n          assert.are.same({ 1, 1 }, state.cursor)\n        end)\n      end)\n    end)\n  end)\n\n  describe('realworld', function()\n    before_each(spec.before)\n\n    it('#226', function()\n      keymap.listen('i', '<c-n>', function(_, fallback)\n        fallback()\n      end)\n      vim.api.nvim_feedkeys(keymap.t('iaiueo<CR>a<C-n><C-n>'), 'tx', true)\n      assert.are.same({ 'aiueo', 'aiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))\n    end)\n\n    it('#414', function()\n      keymap.listen('i', '<M-j>', function()\n        vim.api.nvim_feedkeys(keymap.t('<C-n>'), 'int', true)\n      end)\n      vim.api.nvim_feedkeys(keymap.t('iaiueo<CR>a<M-j><M-j>'), 'tx', true)\n      assert.are.same({ 'aiueo', 'aiueo' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))\n    end)\n\n    it('#744', function()\n      vim.api.nvim_buf_set_keymap(0, 'i', '<C-r>', 'recursive', {\n        noremap = true,\n      })\n      vim.api.nvim_buf_set_keymap(0, 'i', '<CR>', '<CR>recursive', {\n        noremap = false,\n      })\n      keymap.listen('i', '<CR>', function(_, fallback)\n        fallback()\n      end)\n      feedkeys.call(keymap.t('i<CR>'), 'tx')\n      assert.are.same({ '', 'recursive' }, vim.api.nvim_buf_get_lines(0, 0, -1, true))\n    end)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/misc.lua",
    "content": "local misc = {}\n\nlocal islist = vim.islist or vim.tbl_islist\n\n---Create once callback\n---@param callback function\n---@return function\nmisc.once = function(callback)\n  local done = false\n  return function(...)\n    if done then\n      return\n    end\n    done = true\n    callback(...)\n  end\nend\n\n---Return concatenated list\n---@param list1 any[]\n---@param list2 any[]\n---@return any[]\nmisc.concat = function(list1, list2)\n  local new_list = {}\n  for _, v in ipairs(list1) do\n    table.insert(new_list, v)\n  end\n  for _, v in ipairs(list2) do\n    table.insert(new_list, v)\n  end\n  return new_list\nend\n\n---Repeat values\n---@generic T\n---@param str_or_tbl T\n---@param count integer\n---@return T\nmisc.rep = function(str_or_tbl, count)\n  if type(str_or_tbl) == 'string' then\n    return string.rep(str_or_tbl, count)\n  end\n  local rep = {}\n  for _ = 1, count do\n    for _, v in ipairs(str_or_tbl) do\n      table.insert(rep, v)\n    end\n  end\n  return rep\nend\n\n---Return whether the value is empty or not.\n---@param v any\n---@return boolean\nmisc.empty = function(v)\n  if not v then\n    return true\n  end\n  if v == vim.NIL then\n    return true\n  end\n  if type(v) == 'string' and v == '' then\n    return true\n  end\n  if type(v) == 'table' and vim.tbl_isempty(v) then\n    return true\n  end\n  if type(v) == 'number' and v == 0 then\n    return true\n  end\n  return false\nend\n\n---Search value in table\nmisc.contains = function(tbl, v)\n  for _, value in ipairs(tbl) do\n    if value == v then\n      return true\n    end\n  end\n  return false\nend\n\n---The symbol to remove key in misc.merge.\nmisc.none = vim.NIL\n\n---Merge two tables recursively\n---@generic T\n---@param tbl1 T\n---@param tbl2 T\n---@return T\nmisc.merge = function(tbl1, tbl2)\n  local is_dict1 = type(tbl1) == 'table' and (not islist(tbl1) or vim.tbl_isempty(tbl1))\n  local is_dict2 = type(tbl2) == 'table' and (not islist(tbl2) or vim.tbl_isempty(tbl2))\n  if is_dict1 and is_dict2 then\n    local new_tbl = {}\n    for k, v in pairs(tbl2) do\n      if tbl1[k] ~= misc.none then\n        new_tbl[k] = misc.merge(tbl1[k], v)\n      end\n    end\n    for k, v in pairs(tbl1) do\n      if tbl2[k] == nil then\n        if v ~= misc.none then\n          new_tbl[k] = misc.merge(v, {})\n        else\n          new_tbl[k] = nil\n        end\n      end\n    end\n    return new_tbl\n  end\n\n  if tbl1 == misc.none then\n    return nil\n  elseif tbl1 == nil then\n    return misc.merge(tbl2, {})\n  else\n    return tbl1\n  end\nend\n\n---Generate id for group name\nmisc.id = setmetatable({\n  group = {},\n}, {\n  __call = function(_, group)\n    misc.id.group[group] = misc.id.group[group] or 0\n    misc.id.group[group] = misc.id.group[group] + 1\n    return misc.id.group[group]\n  end,\n})\n\n---Treat 1/0 as bool value\n---@param v boolean|1|0\n---@param def boolean\n---@return boolean\nmisc.bool = function(v, def)\n  if v == nil then\n    return def\n  end\n  return v == true or v == 1\nend\n\n---Set value to deep object\n---@param t table\n---@param keys string[]\n---@param v any\nmisc.set = function(t, keys, v)\n  local c = t\n  for i = 1, #keys - 1 do\n    local key = keys[i]\n    c[key] = c[key] or {}\n    c = c[key]\n  end\n  c[keys[#keys]] = v\nend\n\ndo\n  local function do_copy(tbl, seen)\n    if type(tbl) ~= 'table' then\n      return tbl\n    end\n    if seen[tbl] then\n      return seen[tbl]\n    end\n\n    if islist(tbl) then\n      local copy = {}\n      seen[tbl] = copy\n      for i, value in ipairs(tbl) do\n        copy[i] = do_copy(value, seen)\n      end\n      return copy\n    end\n\n    local copy = {}\n    seen[tbl] = copy\n    for key, value in pairs(tbl) do\n      copy[key] = do_copy(value, seen)\n    end\n    return copy\n  end\n\n  ---Copy table\n  ---@generic T\n  ---@param tbl T\n  ---@return T\n  misc.copy = function(tbl)\n    return do_copy(tbl, {})\n  end\nend\n\n---Safe version of vim.str_utfindex\n---@param text string\n---@param vimindex integer|nil\n---@return integer\nmisc.to_utfindex = function(text, vimindex)\n  vimindex = vimindex or #text + 1\n  if vim.fn.has('nvim-0.11') == 1 then\n    return vim.str_utfindex(text, 'utf-16', math.max(0, math.min(vimindex - 1, #text)))\n  end\n  return vim.str_utfindex(text, math.max(0, math.min(vimindex - 1, #text)))\nend\n\n---Safe version of vim.str_byteindex\n---@param text string\n---@param utfindex integer\n---@return integer\nmisc.to_vimindex = function(text, utfindex)\n  utfindex = utfindex or #text\n  for i = utfindex, 1, -1 do\n    local s, v = pcall(function()\n      return vim.str_byteindex(text, 'utf-16', i) + 1\n    end)\n    if s then\n      return v\n    end\n  end\n  return utfindex + 1\nend\n\n---Mark the function as deprecated\nmisc.deprecated = function(fn, msg)\n  local printed = false\n  return function(...)\n    if not printed then\n      print(msg)\n      printed = true\n    end\n    return fn(...)\n  end\nend\n\n--Redraw\nmisc.redraw = setmetatable({\n  doing = false,\n  force = false,\n  -- We use `<Up><Down>` to redraw the screen. (Previously, We use <C-r><ESC>. it will remove the unmatches search history.)\n  incsearch_redraw_keys = ' <BS>',\n}, {\n  __call = function(self, force)\n    local termcode = vim.api.nvim_replace_termcodes(self.incsearch_redraw_keys, true, true, true)\n    if vim.tbl_contains({ '/', '?' }, vim.fn.getcmdtype()) then\n      if vim.o.incsearch then\n        return vim.api.nvim_feedkeys(termcode, 'ni', true)\n      end\n    end\n\n    if self.doing then\n      return\n    end\n    self.doing = true\n    self.force = not not force\n    vim.schedule(function()\n      if self.force then\n        vim.cmd([[redraw!]])\n      else\n        vim.cmd([[redraw]])\n      end\n      self.doing = false\n      self.force = false\n    end)\n  end,\n})\n\nreturn misc\n"
  },
  {
    "path": "lua/cmp/utils/misc_spec.lua",
    "content": "local spec = require('cmp.utils.spec')\n\nlocal misc = require('cmp.utils.misc')\n\ndescribe('misc', function()\n  before_each(spec.before)\n\n  it('copy', function()\n    -- basic.\n    local tbl, copy\n    tbl = {\n      a = {\n        b = 1,\n      },\n    }\n    copy = misc.copy(tbl)\n    assert.are_not.equal(tbl, copy)\n    assert.are_not.equal(tbl.a, copy.a)\n    assert.are.same(tbl, copy)\n\n    -- self reference.\n    tbl = {\n      a = {\n        b = 1,\n      },\n    }\n    tbl.a.c = tbl.a\n    copy = misc.copy(tbl)\n    assert.are_not.equal(tbl, copy)\n    assert.are_not.equal(tbl.a, copy.a)\n    assert.are_not.equal(tbl.a.c, copy.a.c)\n    assert.are.same(tbl, copy)\n  end)\n\n  it('merge', function()\n    local merged\n    merged = misc.merge({\n      a = {},\n    }, {\n      a = {\n        b = 1,\n      },\n    })\n    assert.are.equal(merged.a.b, 1)\n\n    merged = misc.merge({\n      a = {\n        i = 1,\n      },\n    }, {\n      a = {\n        c = 2,\n      },\n    })\n    assert.are.equal(merged.a.i, 1)\n    assert.are.equal(merged.a.c, 2)\n\n    merged = misc.merge({\n      a = false,\n    }, {\n      a = {\n        b = 1,\n      },\n    })\n    assert.are.equal(merged.a, false)\n\n    merged = misc.merge({\n      a = misc.none,\n    }, {\n      a = {\n        b = 1,\n      },\n    })\n    assert.are.equal(merged.a, nil)\n\n    merged = misc.merge({\n      a = misc.none,\n    }, {\n      a = nil,\n    })\n    assert.are.equal(merged.a, nil)\n\n    merged = misc.merge({\n      a = nil,\n    }, {\n      a = misc.none,\n    })\n    assert.are.equal(merged.a, nil)\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/options.lua",
    "content": "local M = {}\n\n-- Set window option without triggering the OptionSet event\n---@param window number\n---@param name string\n---@param value any\nM.win_set_option = function(window, name, value)\n  local eventignore = vim.opt.eventignore:get()\n  vim.opt.eventignore:append('OptionSet')\n  vim.api.nvim_win_set_option(window, name, value)\n  vim.opt.eventignore = eventignore\nend\n\n-- Set buffer option without triggering the OptionSet event\n---@param buffer number\n---@param name string\n---@param value any\nM.buf_set_option = function(buffer, name, value)\n  local eventignore = vim.opt.eventignore:get()\n  vim.opt.eventignore:append('OptionSet')\n  vim.api.nvim_buf_set_option(buffer, name, value)\n  vim.opt.eventignore = eventignore\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cmp/utils/pattern.lua",
    "content": "local pattern = {}\n\npattern._regexes = {}\n\npattern.regex = function(p)\n  if not pattern._regexes[p] then\n    pattern._regexes[p] = vim.regex(p)\n  end\n  return pattern._regexes[p]\nend\n\npattern.offset = function(p, text)\n  local s, e = pattern.regex(p):match_str(text)\n  if s then\n    return s + 1, e + 1\n  end\n  return nil, nil\nend\n\npattern.matchstr = function(p, text)\n  local s, e = pattern.offset(p, text)\n  if s then\n    return string.sub(text, s, e)\n  end\n  return nil\nend\n\nreturn pattern\n"
  },
  {
    "path": "lua/cmp/utils/snippet.lua",
    "content": "local misc = require('cmp.utils.misc')\n\nlocal P = {}\n\n---Take characters until the target characters (The escape sequence is '\\' + char)\n---@param targets string[] The character list for stop consuming text.\n---@param specials string[] If the character isn't contained in targets/specials, '\\' will be left.\nP.take_until = function(targets, specials)\n  targets = targets or {}\n  specials = specials or {}\n\n  return function(input, pos)\n    local new_pos = pos\n    local raw = {}\n    local esc = {}\n    while new_pos <= #input do\n      local c = string.sub(input, new_pos, new_pos)\n      if c == '\\\\' then\n        table.insert(raw, '\\\\')\n        new_pos = new_pos + 1\n        c = string.sub(input, new_pos, new_pos)\n        if not misc.contains(targets, c) and not misc.contains(specials, c) then\n          table.insert(esc, '\\\\')\n        end\n        table.insert(raw, c)\n        table.insert(esc, c)\n        new_pos = new_pos + 1\n      else\n        if misc.contains(targets, c) then\n          break\n        end\n        table.insert(raw, c)\n        table.insert(esc, c)\n        new_pos = new_pos + 1\n      end\n    end\n\n    if new_pos == pos then\n      return P.unmatch(pos)\n    end\n\n    return {\n      parsed = true,\n      value = {\n        raw = table.concat(raw, ''),\n        esc = table.concat(esc, ''),\n      },\n      pos = new_pos,\n    }\n  end\nend\n\nP.unmatch = function(pos)\n  return {\n    parsed = false,\n    value = nil,\n    pos = pos,\n  }\nend\n\nP.map = function(parser, map)\n  return function(input, pos)\n    local result = parser(input, pos)\n    if result.parsed then\n      return {\n        parsed = true,\n        value = map(result.value),\n        pos = result.pos,\n      }\n    end\n    return P.unmatch(pos)\n  end\nend\n\nP.lazy = function(factory)\n  return function(input, pos)\n    return factory()(input, pos)\n  end\nend\n\nP.token = function(token)\n  return function(input, pos)\n    local maybe_token = string.sub(input, pos, pos + #token - 1)\n    if token == maybe_token then\n      return {\n        parsed = true,\n        value = maybe_token,\n        pos = pos + #token,\n      }\n    end\n    return P.unmatch(pos)\n  end\nend\n\nP.pattern = function(p)\n  return function(input, pos)\n    local maybe_match = string.match(string.sub(input, pos), '^' .. p)\n    if maybe_match then\n      return {\n        parsed = true,\n        value = maybe_match,\n        pos = pos + #maybe_match,\n      }\n    end\n    return P.unmatch(pos)\n  end\nend\n\nP.many = function(parser)\n  return function(input, pos)\n    local values = {}\n    local new_pos = pos\n    while new_pos <= #input do\n      local result = parser(input, new_pos)\n      if not result.parsed then\n        break\n      end\n      table.insert(values, result.value)\n      new_pos = result.pos\n    end\n    if #values > 0 then\n      return {\n        parsed = true,\n        value = values,\n        pos = new_pos,\n      }\n    end\n    return P.unmatch(pos)\n  end\nend\n\nP.any = function(...)\n  local parsers = { ... }\n  return function(input, pos)\n    for _, parser in ipairs(parsers) do\n      local result = parser(input, pos)\n      if result.parsed then\n        return result\n      end\n    end\n    return P.unmatch(pos)\n  end\nend\n\nP.opt = function(parser)\n  return function(input, pos)\n    local result = parser(input, pos)\n    return {\n      parsed = true,\n      value = result.value,\n      pos = result.pos,\n    }\n  end\nend\n\nP.seq = function(...)\n  local parsers = { ... }\n  return function(input, pos)\n    local values = {}\n    local new_pos = pos\n    for i, parser in ipairs(parsers) do\n      local result = parser(input, new_pos)\n      if result.parsed then\n        values[i] = result.value\n        new_pos = result.pos\n      else\n        return P.unmatch(pos)\n      end\n    end\n    return {\n      parsed = true,\n      value = values,\n      pos = new_pos,\n    }\n  end\nend\n\nlocal Node = {}\n\nNode.Type = {\n  SNIPPET = 0,\n  TABSTOP = 1,\n  PLACEHOLDER = 2,\n  VARIABLE = 3,\n  CHOICE = 4,\n  TRANSFORM = 5,\n  FORMAT = 6,\n  TEXT = 7,\n}\n\nfunction Node:__tostring()\n  local insert_text = {}\n  if self.type == Node.Type.SNIPPET then\n    for _, c in ipairs(self.children) do\n      table.insert(insert_text, tostring(c))\n    end\n  elseif self.type == Node.Type.CHOICE then\n    table.insert(insert_text, self.items[1])\n  elseif self.type == Node.Type.PLACEHOLDER then\n    for _, c in ipairs(self.children or {}) do\n      table.insert(insert_text, tostring(c))\n    end\n  elseif self.type == Node.Type.TEXT then\n    table.insert(insert_text, self.esc)\n  end\n  return table.concat(insert_text, '')\nend\n\n--@see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar\n\nlocal S = {}\nS.dollar = P.token('$')\nS.open = P.token('{')\nS.close = P.token('}')\nS.colon = P.token(':')\nS.slash = P.token('/')\nS.comma = P.token(',')\nS.pipe = P.token('|')\nS.plus = P.token('+')\nS.minus = P.token('-')\nS.question = P.token('?')\nS.int = P.map(P.pattern('[0-9]+'), function(value)\n  return tonumber(value, 10)\nend)\nS.var = P.pattern('[%a_][%w_]+')\nS.text = function(targets, specials)\n  return P.map(P.take_until(targets, specials), function(value)\n    return setmetatable({\n      type = Node.Type.TEXT,\n      raw = value.raw,\n      esc = value.esc,\n    }, Node)\n  end)\nend\n\nS.toplevel = P.lazy(function()\n  return P.any(S.placeholder, S.tabstop, S.variable, S.choice)\nend)\n\nS.format = P.any(\n  P.map(P.seq(S.dollar, S.int), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[2],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any(P.token('upcase'), P.token('downcase'), P.token('capitalize'), P.token('camelcase'), P.token('pascalcase')), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n      modifier = values[6],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\\\' }))), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n      if_text = values[5][2] and values[5][2].esc or '',\n      else_text = values[5][4] and values[5][4].esc or '',\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\\\' }))), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n      if_text = values[5][2] and values[5][2].esc or '',\n      else_text = '',\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.minus, P.opt(P.take_until({ '}' }, { '\\\\' })), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n      if_text = '',\n      else_text = values[6] and values[6].esc or '',\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\\\' })), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.FORMAT,\n      capture_index = values[3],\n      if_text = '',\n      else_text = values[5] and values[5].esc or '',\n    }, Node)\n  end)\n)\n\nS.transform = P.map(P.seq(S.slash, P.take_until({ '/' }, { '\\\\' }), S.slash, P.many(P.any(S.format, S.text({ '$', '/' }, { '\\\\' }))), S.slash, P.opt(P.pattern('[ig]+'))), function(values)\n  return setmetatable({\n    type = Node.Type.TRANSFORM,\n    pattern = values[2].raw,\n    format = values[4],\n    option = values[6],\n  }, Node)\nend)\n\nS.tabstop = P.any(\n  P.map(P.seq(S.dollar, S.int), function(values)\n    return setmetatable({\n      type = Node.Type.TABSTOP,\n      tabstop = values[2],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values)\n    return setmetatable({\n      type = Node.Type.TABSTOP,\n      tabstop = values[3],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values)\n    return setmetatable({\n      type = Node.Type.TABSTOP,\n      tabstop = values[3],\n      transform = values[4],\n    }, Node)\n  end)\n)\n\nS.placeholder = P.any(P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\\\' })))), S.close), function(values)\n  return setmetatable({\n    type = Node.Type.PLACEHOLDER,\n    tabstop = values[3],\n    -- insert empty text if opt did not match.\n    children = values[5] or {\n      setmetatable({\n        type = Node.Type.TEXT,\n        raw = '',\n        esc = '',\n      }, Node),\n    },\n  }, Node)\nend))\n\nS.choice = P.map(\n  P.seq(\n    S.dollar,\n    S.open,\n    S.int,\n    S.pipe,\n    P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)\n      return values[1].esc\n    end)),\n    S.pipe,\n    S.close\n  ),\n  function(values)\n    return setmetatable({\n      type = Node.Type.CHOICE,\n      tabstop = values[3],\n      items = values[5],\n    }, Node)\n  end\n)\n\nS.variable = P.any(\n  P.map(P.seq(S.dollar, S.var), function(values)\n    return setmetatable({\n      type = Node.Type.VARIABLE,\n      name = values[2],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values)\n    return setmetatable({\n      type = Node.Type.VARIABLE,\n      name = values[3],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values)\n    return setmetatable({\n      type = Node.Type.VARIABLE,\n      name = values[3],\n      transform = values[4],\n    }, Node)\n  end),\n  P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\\\' }))), S.close), function(values)\n    return setmetatable({\n      type = Node.Type.VARIABLE,\n      name = values[3],\n      children = values[5],\n    }, Node)\n  end)\n)\n\nS.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\\\' }))), function(values)\n  return setmetatable({\n    type = Node.Type.SNIPPET,\n    children = values,\n  }, Node)\nend)\n\nlocal M = {}\n\n---The snippet node type enum\n---@types table<string, integer>\nM.NodeType = Node.Type\n\n---Parse snippet string and returns the AST\n---@param input string\n---@return table\nfunction M.parse(input)\n  local result = S.snippet(input, 1)\n  if not result.parsed then\n    error('snippet parsing failed.')\n  end\n  return result.value\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cmp/utils/spec.lua",
    "content": "local context = require('cmp.context')\nlocal source = require('cmp.source')\nlocal types = require('cmp.types')\nlocal config = require('cmp.config')\n\nlocal spec = {}\n\nspec.before = function()\n  vim.cmd([[\n    bdelete!\n    enew!\n    imapclear\n    imapclear <buffer>\n    cmapclear\n    cmapclear <buffer>\n    smapclear\n    smapclear <buffer>\n    xmapclear\n    xmapclear <buffer>\n    tmapclear\n    tmapclear <buffer>\n    setlocal noswapfile\n    setlocal virtualedit=all\n    setlocal completeopt=menu,menuone,noselect\n  ]])\n  config.set_global({\n    sources = {\n      { name = 'spec' },\n    },\n    snippet = {\n      expand = function(args)\n        local ctx = context.new()\n        vim.api.nvim_buf_set_text(ctx.bufnr, ctx.cursor.row - 1, ctx.cursor.col - 1, ctx.cursor.row - 1, ctx.cursor.col - 1, vim.split(string.gsub(args.body, '%$0', ''), '\\n'))\n        for i, t in ipairs(vim.split(args.body, '\\n')) do\n          local s = string.find(t, '$0', 1, true)\n          if s then\n            if i == 1 then\n              vim.api.nvim_win_set_cursor(0, { ctx.cursor.row, ctx.cursor.col + s - 2 })\n            else\n              vim.api.nvim_win_set_cursor(0, { ctx.cursor.row + i - 1, s - 1 })\n            end\n            break\n          end\n        end\n      end,\n    },\n  })\n  config.set_cmdline({\n    sources = {\n      { name = 'spec' },\n    },\n  }, ':')\nend\n\nspec.state = function(text, row, col)\n  vim.fn.setline(1, text)\n  vim.fn.cursor(row, col)\n  local ctx = context.empty()\n  local s = source.new('spec', {\n    complete = function() end,\n  })\n  return {\n    context = function()\n      return ctx\n    end,\n    source = function()\n      return s\n    end,\n    backspace = function()\n      vim.fn.feedkeys('x', 'nx')\n      vim.fn.feedkeys('h', 'nx')\n      ctx = context.new(ctx, { reason = types.cmp.ContextReason.Auto })\n      s:complete(ctx, function() end)\n      return ctx\n    end,\n    input = function(char)\n      vim.fn.feedkeys(('i%s'):format(char), 'nx')\n      vim.fn.feedkeys(string.rep('l', #char), 'nx')\n      ctx.prev_context = nil\n      ctx = context.new(ctx, { reason = types.cmp.ContextReason.Auto })\n      s:complete(ctx, function() end)\n      return ctx\n    end,\n    manual = function()\n      ctx = context.new(ctx, { reason = types.cmp.ContextReason.Manual })\n      s:complete(ctx, function() end)\n      return ctx\n    end,\n  }\nend\n\nreturn spec\n"
  },
  {
    "path": "lua/cmp/utils/str.lua",
    "content": "local char = require('cmp.utils.char')\n\nlocal str = {}\n\nlocal INVALIDS = {}\nINVALIDS[string.byte(\"'\")] = true\nINVALIDS[string.byte('\"')] = true\nINVALIDS[string.byte('=')] = true\nINVALIDS[string.byte('$')] = true\nINVALIDS[string.byte('(')] = true\nINVALIDS[string.byte('[')] = true\nINVALIDS[string.byte('<')] = true\nINVALIDS[string.byte('{')] = true\nINVALIDS[string.byte(' ')] = true\nINVALIDS[string.byte('\\t')] = true\nINVALIDS[string.byte('\\n')] = true\nINVALIDS[string.byte('\\r')] = true\n\nlocal NR_BYTE = string.byte('\\n')\n\nlocal PAIRS = {}\nPAIRS[string.byte('<')] = string.byte('>')\nPAIRS[string.byte('[')] = string.byte(']')\nPAIRS[string.byte('(')] = string.byte(')')\nPAIRS[string.byte('{')] = string.byte('}')\nPAIRS[string.byte('\"')] = string.byte('\"')\nPAIRS[string.byte(\"'\")] = string.byte(\"'\")\n\n---Return if specified text has prefix or not\n---@param text string\n---@param prefix string\n---@return boolean\nstr.has_prefix = function(text, prefix)\n  if #text < #prefix then\n    return false\n  end\n  for i = 1, #prefix do\n    if not char.match(string.byte(text, i), string.byte(prefix, i)) then\n      return false\n    end\n  end\n  return true\nend\n\n---get_common_string\nstr.get_common_string = function(text1, text2)\n  local min = math.min(#text1, #text2)\n  for i = 1, min do\n    if not char.match(string.byte(text1, i), string.byte(text2, i)) then\n      if string.byte(text1, i) > 127 then\n        -- Differing byte is non-ASCII, use Unicode-safe path\n        local char_min_len = math.min(vim.fn.strchars(text1), vim.fn.strchars(text2))\n        for j = 0, char_min_len - 1 do\n          local char1 = vim.fn.strcharpart(text1, j, 1)\n          local char2 = vim.fn.strcharpart(text2, j, 1)\n          -- Use case-insensitive comparison for Unicode like char.match does for ASCII\n          if vim.fn.tolower(char1) ~= vim.fn.tolower(char2) then\n            return vim.fn.strcharpart(text1, 0, j)\n          end\n        end\n        return vim.fn.strcharpart(text1, 0, char_min_len)\n      end\n      return string.sub(text1, 1, i - 1)\n    end\n  end\n  return string.sub(text1, 1, min)\nend\n\n---Remove suffix\n---@param text string\n---@param suffix string\n---@return string\nstr.remove_suffix = function(text, suffix)\n  if #text < #suffix then\n    return text\n  end\n\n  local i = 0\n  while i < #suffix do\n    if string.byte(text, #text - i) ~= string.byte(suffix, #suffix - i) then\n      return text\n    end\n    i = i + 1\n  end\n  return string.sub(text, 1, -#suffix - 1)\nend\n\n---trim\n---@param text string\n---@return string\nstr.trim = function(text)\n  local s = 1\n  for i = 1, #text do\n    if not char.is_white(string.byte(text, i)) then\n      s = i\n      break\n    end\n  end\n\n  local e = #text\n  for i = #text, 1, -1 do\n    if not char.is_white(string.byte(text, i)) then\n      e = i\n      break\n    end\n  end\n  if s == 1 and e == #text then\n    return text\n  end\n  return string.sub(text, s, e)\nend\n\n---get_word\n---@param text string\n---@param stop_char? integer\n---@param min_length? integer\n---@return string\nstr.get_word = function(text, stop_char, min_length)\n  min_length = min_length or 0\n\n  local has_alnum = false\n  local stack = {}\n  local word = {}\n  local add = function(c)\n    table.insert(word, string.char(c))\n    if stack[#stack] == c then\n      table.remove(stack, #stack)\n    else\n      if PAIRS[c] then\n        table.insert(stack, c)\n      end\n    end\n  end\n  for i = 1, #text do\n    local c = string.byte(text, i, i)\n    if #word < min_length then\n      table.insert(word, string.char(c))\n    elseif not INVALIDS[c] then\n      add(c)\n      has_alnum = has_alnum or char.is_alnum(c)\n    elseif not has_alnum then\n      add(c)\n    elseif #stack ~= 0 then\n      add(c)\n      if has_alnum and #stack == 0 then\n        break\n      end\n    else\n      break\n    end\n  end\n  if stop_char and word[#word] == string.char(stop_char) then\n    table.remove(word, #word)\n  end\n  return table.concat(word, '')\nend\n\n---Oneline\n---@param text string\n---@return string\nstr.oneline = function(text)\n  for i = 1, #text do\n    if string.byte(text, i) == NR_BYTE then\n      return string.sub(text, 1, i - 1)\n    end\n  end\n  return text\nend\n\n---Escape special chars\n---@param text string\n---@param chars string[]\n---@return string\nstr.escape = function(text, chars)\n  table.insert(chars, '\\\\')\n  local escaped = {}\n  local i = 1\n  while i <= #text do\n    local c = string.sub(text, i, i)\n    if vim.tbl_contains(chars, c) then\n      table.insert(escaped, '\\\\')\n      table.insert(escaped, c)\n    else\n      table.insert(escaped, c)\n    end\n    i = i + 1\n  end\n  return table.concat(escaped, '')\nend\n\nreturn str\n"
  },
  {
    "path": "lua/cmp/utils/str_spec.lua",
    "content": "local str = require('cmp.utils.str')\n\ndescribe('utils.str', function()\n  it('get_word', function()\n    assert.are.equal(str.get_word('print'), 'print')\n    assert.are.equal(str.get_word('$variable'), '$variable')\n    assert.are.equal(str.get_word('print()'), 'print')\n    assert.are.equal(str.get_word('[\"cmp#confirm\"]'), '[\"cmp#confirm\"]')\n    assert.are.equal(str.get_word('\"devDependencies\":', string.byte('\"')), '\"devDependencies')\n    assert.are.equal(str.get_word('\"devDependencies\": ${1},', string.byte('\"')), '\"devDependencies')\n    assert.are.equal(str.get_word('#[cfg(test)]'), '#[cfg(test)]')\n    assert.are.equal(str.get_word('import { GetStaticProps$1 } from \"next\";', nil, 9), 'import { GetStaticProps')\n  end)\n\n  it('remove_suffix', function()\n    assert.are.equal(str.remove_suffix('log()', '$0'), 'log()')\n    assert.are.equal(str.remove_suffix('log()$0', '$0'), 'log()')\n    assert.are.equal(str.remove_suffix('log()${0}', '${0}'), 'log()')\n    assert.are.equal(str.remove_suffix('log()${0:placeholder}', '${0}'), 'log()${0:placeholder}')\n  end)\n\n  it('escape', function()\n    assert.are.equal(str.escape('plain', {}), 'plain')\n    assert.are.equal(str.escape('plain\\\\', {}), 'plain\\\\\\\\')\n    assert.are.equal(str.escape('plain\\\\\"', {}), 'plain\\\\\\\\\"')\n    assert.are.equal(str.escape('pla\"in', { '\"' }), 'pla\\\\\"in')\n    assert.are.equal(str.escape('call(\"\")', { '\"' }), 'call(\\\\\"\\\\\")')\n  end)\n\n  it('get_common_string', function()\n    -- ASCII tests\n    assert.are.equal(str.get_common_string('hello', 'help'), 'hel')\n    assert.are.equal(str.get_common_string('abc', 'xyz'), '')\n    assert.are.equal(str.get_common_string('test', 'Testing'), 'test')\n\n    -- Unicode tests\n    assert.are.equal(str.get_common_string('получаем', 'получив'), 'получ')\n    assert.are.equal(str.get_common_string('тест', 'тестинг'), 'тест')\n    assert.are.equal(str.get_common_string('тест', 'Тестинг'), 'тест')\n    assert.are.equal(str.get_common_string('Тест', 'тестинг'), 'Тест')\n    assert.are.equal(str.get_common_string('тЕст', 'тестинг'), 'тЕст')\n    assert.are.equal(str.get_common_string('тест', 'тЕстинг'), 'тест')\n    assert.are.equal(str.get_common_string('тесТ', 'тЕстинг'), 'тесТ')\n    assert.are.equal(str.get_common_string('тест', 'тесТинг'), 'тест')\n    assert.are.equal(str.get_common_string('а', 'я'), '') -- 0xD0 0xB0 - 0xD1 0x8F\n    assert.are.equal(str.get_common_string('а', 'б'), '') -- 0xD0 0xB0 - 0xD0 0xB1\n    assert.are.equal(str.get_common_string('Я', 'я'), 'Я') -- 0xD0 0xAF - 0xD1 0x8F\n    assert.are.equal(str.get_common_string('А', 'а'), 'А') -- 0xD0 0x90 - 0xD0 0xB0\n    -- Normalization is not supported yet\n    assert.are.equal(str.get_common_string('й', 'и'), '') -- 0xD0 0xB9 - 0xD0 0xB8\n    assert.are.equal(str.get_common_string('й', 'и'), 'и') -- 0xD0 0xB8 0xD1 0x8E - 0xD0 0xB8\n  end)\nend)\n"
  },
  {
    "path": "lua/cmp/utils/window.lua",
    "content": "local misc = require('cmp.utils.misc')\nlocal opt = require('cmp.utils.options')\nlocal buffer = require('cmp.utils.buffer')\nlocal api = require('cmp.utils.api')\nlocal config = require('cmp.config')\n\n---@class cmp.WindowStyle\n---@field public relative string\n---@field public row integer\n---@field public col integer\n---@field public width integer|float\n---@field public height integer|float\n---@field public border string|string[]|nil\n---@field public zindex integer|nil\n\n---@class cmp.Window\n---@field public name string\n---@field public win integer|nil\n---@field public thumb_win integer|nil\n---@field public sbar_win integer|nil\n---@field public style cmp.WindowStyle\n---@field public opt table<string, any>\n---@field public buffer_opt table<string, any>\nlocal window = {}\n\n---new\n---@return cmp.Window\nwindow.new = function()\n  local self = setmetatable({}, { __index = window })\n  self.name = misc.id('cmp.utils.window.new')\n  self.win = nil\n  self.sbar_win = nil\n  self.thumb_win = nil\n  self.style = {}\n  self.opt = {}\n  self.buffer_opt = {}\n  return self\nend\n\n---Set window option.\n---NOTE: If the window already visible, immediately applied to it.\n---@param key string\n---@param value any\nwindow.option = function(self, key, value)\n  if vim.fn.exists('+' .. key) == 0 then\n    return\n  end\n\n  if value == nil then\n    return self.opt[key]\n  end\n\n  self.opt[key] = value\n  if self:visible() then\n    opt.win_set_option(self.win, key, value)\n  end\nend\n\n---Set buffer option.\n---NOTE: If the buffer already visible, immediately applied to it.\n---@param key string\n---@param value any\nwindow.buffer_option = function(self, key, value)\n  if vim.fn.exists('+' .. key) == 0 then\n    return\n  end\n\n  if value == nil then\n    return self.buffer_opt[key]\n  end\n\n  self.buffer_opt[key] = value\n  local existing_buf = buffer.get(self.name)\n  if existing_buf then\n    opt.buf_set_option(existing_buf, key, value)\n  end\nend\n\n---Set style.\n---@param style cmp.WindowStyle\nwindow.set_style = function(self, style)\n  self.style = style\n  local info = self:info()\n\n  if vim.o.lines and vim.o.lines <= info.row + info.height + 1 then\n    self.style.height = vim.o.lines - info.row - info.border_info.vert - 1\n  end\n\n  self.style.zindex = self.style.zindex or 1\n\n  --- GUI clients are allowed to return fractional bounds, but we need integer\n  --- bounds to open the window\n  self.style.width = math.ceil(self.style.width)\n  self.style.height = math.ceil(self.style.height)\nend\n\n---Return buffer id.\n---@return integer\nwindow.get_buffer = function(self)\n  local buf, created_new = buffer.ensure(self.name)\n  if created_new then\n    for k, v in pairs(self.buffer_opt) do\n      opt.buf_set_option(buf, k, v)\n    end\n  end\n  return buf\nend\n\n---Open window\n---@param style cmp.WindowStyle\nwindow.open = function(self, style)\n  if style then\n    self:set_style(style)\n  end\n\n  if self.style.width < 1 or self.style.height < 1 then\n    return\n  end\n\n  if self.win and vim.api.nvim_win_is_valid(self.win) then\n    vim.api.nvim_win_set_config(self.win, self.style)\n  else\n    local s = misc.copy(self.style)\n    s.noautocmd = true\n    self.win = vim.api.nvim_open_win(self:get_buffer(), false, s)\n    for k, v in pairs(self.opt) do\n      opt.win_set_option(self.win, k, v)\n    end\n  end\n  self:update()\nend\n\n---Update\nwindow.update = function(self)\n  local info = self:info()\n  if info.scrollable and self.style.height > 0 then\n    -- Draw the background of the scrollbar\n\n    if not info.border_info.visible then\n      local style = {\n        relative = 'editor',\n        style = 'minimal',\n        width = 1,\n        height = self.style.height,\n        row = info.row,\n        col = info.col + info.width - info.scrollbar_offset, -- info.col was already contained the scrollbar offset.\n        zindex = (self.style.zindex and (self.style.zindex + 1) or 1),\n        border = 'none',\n      }\n      if self.sbar_win and vim.api.nvim_win_is_valid(self.sbar_win) then\n        vim.api.nvim_win_set_config(self.sbar_win, style)\n      else\n        style.noautocmd = true\n        self.sbar_win = vim.api.nvim_open_win(buffer.ensure(self.name .. 'sbar_buf'), false, style)\n        opt.win_set_option(self.sbar_win, 'winhighlight', 'EndOfBuffer:PmenuSbar,NormalFloat:PmenuSbar')\n      end\n    end\n\n    -- Draw the scrollbar thumb\n    local thumb_height = math.floor(info.inner_height * (info.inner_height / self:get_content_height()))\n    thumb_height = math.max(1, thumb_height)\n    local topline = vim.fn.getwininfo(self.win)[1].topline\n    local scroll_ratio = topline / (self:get_content_height() - info.inner_height + 1)\n    -- row grid start from 0 on nvim-0.10\n    local thumb_offset_raw = (info.inner_height - thumb_height) * scroll_ratio\n    -- round half if topline > 1\n    local thumb_offset = math.floor(thumb_offset_raw)\n    if topline > 1 and thumb_offset_raw + 0.5 >= thumb_offset + 1 then\n      thumb_offset = thumb_offset + 1\n    end\n\n    local style = {\n      relative = 'editor',\n      style = 'minimal',\n      width = 1,\n      height = thumb_height,\n      row = info.row + thumb_offset + (info.border_info.visible and info.border_info.top or 0),\n      col = info.col + info.width - 1, -- info.col was already added scrollbar offset.\n      zindex = (self.style.zindex and (self.style.zindex + 2) or 2),\n      border = 'none',\n    }\n    if self.thumb_win and vim.api.nvim_win_is_valid(self.thumb_win) then\n      vim.api.nvim_win_set_config(self.thumb_win, style)\n    else\n      style.noautocmd = true\n      self.thumb_win = vim.api.nvim_open_win(buffer.ensure(self.name .. 'thumb_buf'), false, style)\n      opt.win_set_option(self.thumb_win, 'winhighlight', 'EndOfBuffer:PmenuThumb,NormalFloat:PmenuThumb')\n    end\n  else\n    if self.sbar_win and vim.api.nvim_win_is_valid(self.sbar_win) then\n      vim.api.nvim_win_hide(self.sbar_win)\n      self.sbar_win = nil\n    end\n    if self.thumb_win and vim.api.nvim_win_is_valid(self.thumb_win) then\n      vim.api.nvim_win_hide(self.thumb_win)\n      self.thumb_win = nil\n    end\n  end\n\n  -- In cmdline, vim does not redraw automatically.\n  if api.is_cmdline_mode() then\n    vim.api.nvim_win_call(self.win, function()\n      misc.redraw()\n    end)\n  end\nend\n\n---Close window\nwindow.close = function(self)\n  if self.win and vim.api.nvim_win_is_valid(self.win) then\n    if self.win and vim.api.nvim_win_is_valid(self.win) then\n      vim.api.nvim_win_hide(self.win)\n      self.win = nil\n    end\n    if self.sbar_win and vim.api.nvim_win_is_valid(self.sbar_win) then\n      vim.api.nvim_win_hide(self.sbar_win)\n      self.sbar_win = nil\n    end\n    if self.thumb_win and vim.api.nvim_win_is_valid(self.thumb_win) then\n      vim.api.nvim_win_hide(self.thumb_win)\n      self.thumb_win = nil\n    end\n  end\nend\n\n---Return the window is visible or not.\nwindow.visible = function(self)\n  return self.win and vim.api.nvim_win_is_valid(self.win)\nend\n\n---Return win info.\nwindow.info = function(self)\n  local border_info = self:get_border_info()\n  local scrollbar = config.get().window.completion.scrollbar\n  local info = {\n    row = self.style.row,\n    col = self.style.col,\n    width = self.style.width + border_info.left + border_info.right,\n    height = self.style.height + border_info.top + border_info.bottom,\n    inner_width = self.style.width,\n    inner_height = self.style.height,\n    border_info = border_info,\n    scrollable = false,\n    scrollbar_offset = 0,\n  }\n\n  if self:get_content_height() > info.inner_height and scrollbar then\n    info.scrollable = true\n    if not border_info.visible then\n      info.scrollbar_offset = 1\n      info.width = info.width + 1\n    end\n  end\n\n  return info\nend\n\n---Return border information.\n---@return { top: integer, left: integer, right: integer, bottom: integer, vert: integer, horiz: integer, visible: boolean }\nwindow.get_border_info = function(self)\n  local border = self.style.border\n  if not border or border == 'none' then\n    return {\n      top = 0,\n      left = 0,\n      right = 0,\n      bottom = 0,\n      vert = 0,\n      horiz = 0,\n      visible = false,\n    }\n  end\n  if type(border) == 'string' then\n    if border == 'shadow' then\n      return {\n        top = 0,\n        left = 0,\n        right = 1,\n        bottom = 1,\n        vert = 1,\n        horiz = 1,\n        visible = false,\n      }\n    end\n    return {\n      top = 1,\n      left = 1,\n      right = 1,\n      bottom = 1,\n      vert = 2,\n      horiz = 2,\n      visible = true,\n    }\n  end\n\n  local new_border = {}\n  while #new_border <= 8 do\n    for _, b in ipairs(border) do\n      table.insert(new_border, type(b) == 'string' and b or b[1])\n    end\n  end\n  local info = {}\n  info.top = new_border[2] == '' and 0 or 1\n  info.right = new_border[4] == '' and 0 or 1\n  info.bottom = new_border[6] == '' and 0 or 1\n  info.left = new_border[8] == '' and 0 or 1\n  info.vert = info.top + info.bottom\n  info.horiz = info.left + info.right\n  info.visible = not (vim.tbl_contains({ '', ' ' }, new_border[2]) and vim.tbl_contains({ '', ' ' }, new_border[4]) and vim.tbl_contains({ '', ' ' }, new_border[6]) and vim.tbl_contains({ '', ' ' }, new_border[8]))\n  return info\nend\n\n---Get scroll height.\n---NOTE: The result of vim.fn.strdisplaywidth depends on the buffer it was called in (see comment in cmp.Entry.get_view).\n---@return integer\nwindow.get_content_height = function(self)\n  if not self:option('wrap') then\n    return vim.api.nvim_buf_line_count(self:get_buffer())\n  end\n  local height = 0\n  vim.api.nvim_buf_call(self:get_buffer(), function()\n    for _, text in ipairs(vim.api.nvim_buf_get_lines(self:get_buffer(), 0, -1, false)) do\n      -- nvim_buf_get_lines sometimes returns a blob. see #2050\n      if vim.fn.type(text) == vim.v.t_blob then\n        text = vim.fn.string(text)\n      end\n      height = height + math.max(1, math.ceil(vim.fn.strdisplaywidth(text) / self.style.width))\n    end\n  end)\n  return height\nend\n\nreturn window\n"
  },
  {
    "path": "lua/cmp/view/custom_entries_view.lua",
    "content": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal window = require('cmp.utils.window')\nlocal config = require('cmp.config')\nlocal types = require('cmp.types')\nlocal keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\nlocal api = require('cmp.utils.api')\n\nlocal DEFAULT_HEIGHT = 10 -- @see https://github.com/vim/vim/blob/master/src/popupmenu.c#L45\n\n---@class cmp.CustomEntriesView\n---@field private entries_win cmp.Window\n---@field private offset integer\n---@field private active boolean\n---@field private entries cmp.Entry[]\n---@field private column_width any\n---@field private bottom_up boolean\n---@field public event cmp.Event\nlocal custom_entries_view = {}\n\ncustom_entries_view.ns = vim.api.nvim_create_namespace('cmp.view.custom_entries_view')\n\ncustom_entries_view.new = function()\n  local self = setmetatable({}, { __index = custom_entries_view })\n\n  self.entries_win = window.new()\n  self.entries_win:option('conceallevel', 2)\n  self.entries_win:option('concealcursor', 'n')\n  self.entries_win:option('cursorlineopt', 'line')\n  self.entries_win:option('foldenable', false)\n  self.entries_win:option('wrap', false)\n  -- This is done so that strdisplaywidth calculations for lines in the\n  -- custom_entries_view window exactly match with what is really displayed,\n  -- see comment in cmp.Entry.get_view. Setting tabstop to 1 makes all tabs be\n  -- always rendered one column wide, which removes the unpredictability coming\n  -- from variable width of the tab character.\n  self.entries_win:buffer_option('tabstop', 1)\n  self.entries_win:buffer_option('filetype', 'cmp_menu')\n  self.entries_win:buffer_option('buftype', 'nofile')\n  self.event = event.new()\n  self.offset = -1\n  self.active = false\n  self.entries = {}\n  self.bottom_up = false\n\n  autocmd.subscribe(\n    'CompleteChanged',\n    vim.schedule_wrap(function()\n      if self:visible() and vim.fn.pumvisible() == 1 then\n        self:close()\n      end\n    end)\n  )\n\n  vim.api.nvim_set_decoration_provider(custom_entries_view.ns, {\n    on_win = function(_, win, buf, top, bot)\n      if win ~= self.entries_win.win or buf ~= self.entries_win:get_buffer() then\n        return\n      end\n\n      local fields = config.get().formatting.fields\n      for i = top, bot do\n        local e = self.entries[i + 1]\n        if e then\n          local v = e:get_view(self.offset, buf)\n          local o = config.get().window.completion.side_padding\n          local a = 0\n          for _, field in ipairs(fields) do\n            if field == types.cmp.ItemField.Abbr then\n              a = o\n            end\n\n            if type(v[field].hl_group) == 'table' then\n              for _, extmark in ipairs(v[field].hl_group) do\n                local hl_start, hl_end = unpack(extmark.range)\n                vim.api.nvim_buf_set_extmark(buf, custom_entries_view.ns, i, o + hl_start, {\n                  end_line = i,\n                  end_col = o + hl_end,\n                  hl_group = extmark[1],\n                  hl_eol = false,\n                  ephemeral = true,\n                })\n              end\n            else\n              vim.api.nvim_buf_set_extmark(buf, custom_entries_view.ns, i, o, {\n                end_line = i,\n                end_col = o + v[field].bytes,\n                hl_group = v[field].hl_group,\n                hl_mode = 'combine',\n                ephemeral = true,\n              })\n            end\n\n            o = o + v[field].bytes + (self.column_width[field] - v[field].width) + 1\n          end\n\n          for _, m in ipairs(e:get_view_matches(v.abbr.text) or {}) do\n            vim.api.nvim_buf_set_extmark(buf, custom_entries_view.ns, i, a + m.word_match_start - 1, {\n              end_line = i,\n              end_col = a + m.word_match_end,\n              hl_group = m.fuzzy and 'CmpItemAbbrMatchFuzzy' or 'CmpItemAbbrMatch',\n              hl_mode = 'combine',\n              ephemeral = true,\n            })\n          end\n        end\n      end\n    end,\n  })\n\n  return self\nend\n\ncustom_entries_view.ready = function()\n  return vim.fn.pumvisible() == 0\nend\n\ncustom_entries_view.on_change = function(self)\n  self.active = false\nend\n\ncustom_entries_view.is_direction_top_down = function(self)\n  local c = config.get()\n  if (c.view and c.view.entries and c.view.entries.selection_order) == 'top_down' then\n    return true\n  elseif c.view.entries == nil or c.view.entries.selection_order == nil then\n    return true\n  else\n    return not self.bottom_up\n  end\nend\n\ncustom_entries_view.open = function(self, offset, entries)\n  local c = config.get()\n  local completion = c.window.completion\n  assert(completion, 'config.get() must resolve window.completion with defaults')\n\n  self.offset = offset\n  self.entries = {}\n  self.column_width = { abbr = 0, icon = 0, kind = 0, menu = 0 }\n\n  local entries_buf = self.entries_win:get_buffer()\n  local fields = config.get().formatting.fields\n  local lines = {}\n  local dedup = {}\n  local preselect_index = 0\n  for _, e in ipairs(entries) do\n    local view = e:get_view(offset, entries_buf)\n    if view.dup == 1 or not dedup[e.completion_item.label] then\n      dedup[e.completion_item.label] = true\n      for _, field in ipairs(fields) do\n        self.column_width[field] = math.max(self.column_width[field], view[field].width)\n      end\n      table.insert(self.entries, e)\n      table.insert(lines, ' ')\n      if preselect_index == 0 and e.completion_item.preselect then\n        preselect_index = #self.entries\n      end\n    end\n  end\n  if vim.bo[entries_buf].modifiable == false then\n    vim.bo[entries_buf].modifiable = true\n    vim.api.nvim_buf_set_lines(entries_buf, 0, -1, false, lines)\n    vim.bo[entries_buf].modifiable = false\n  else\n    vim.api.nvim_buf_set_lines(entries_buf, 0, -1, false, lines)\n  end\n  vim.api.nvim_buf_set_option(entries_buf, 'modified', false)\n\n  local width = 0\n  width = width + 1\n  width = width + self.column_width.abbr + (self.column_width.icon > 0 and 1 or 0)\n  width = width + self.column_width.icon + (self.column_width.kind > 0 and 1 or 0)\n  width = width + self.column_width.kind + (self.column_width.menu > 0 and 1 or 0)\n  width = width + self.column_width.menu + 1\n\n  local height = completion.max_height or vim.api.nvim_get_option_value('pumheight', {})\n  height = height ~= 0 and height or #self.entries\n  height = math.min(height, #self.entries)\n\n  local delta = 0\n  if not config.get().view.entries.follow_cursor then\n    local cursor_before_line = api.get_cursor_before_line()\n    delta = vim.fn.strdisplaywidth(cursor_before_line:sub(self.offset))\n  end\n  local pos = api.get_screen_cursor()\n  local row, col = pos[1], pos[2] - delta - 1\n\n  local border_info = window.get_border_info({ style = completion })\n  local border_offset_row = border_info.top + border_info.bottom\n  local border_offset_col = border_info.left + border_info.right\n\n  local prefers_above = c.view.entries.vertical_positioning == 'above'\n  local prefers_auto = c.view.entries.vertical_positioning == 'auto'\n  local cant_fit_at_bottom = vim.o.lines - row - border_offset_row <= math.min(DEFAULT_HEIGHT, height)\n  local cant_fit_at_top = row - border_offset_row <= math.min(DEFAULT_HEIGHT, height)\n  local is_in_top_half = math.floor(vim.o.lines * 0.5) > row + border_offset_row\n  local should_position_above = cant_fit_at_bottom or (prefers_above and not cant_fit_at_top) or (prefers_auto and is_in_top_half)\n  if should_position_above then\n    self.bottom_up = true\n    height = math.min(height, row - 1)\n    row = row - height - border_offset_row - 1\n    if row < 0 then\n      height = height + row\n    end\n  else\n    self.bottom_up = false\n  end\n  if math.floor(vim.o.columns * 0.5) <= col + border_offset_col and vim.o.columns - col - border_offset_col <= width then\n    width = math.min(width, vim.o.columns - 1)\n    col = vim.o.columns - width - border_offset_col - 1\n    if col < 0 then\n      width = width + col\n    end\n  end\n\n  if not self:is_direction_top_down() then\n    local n = #self.entries\n    for i = 1, math.floor(n / 2) do\n      self.entries[i], self.entries[n - i + 1] = self.entries[n - i + 1], self.entries[i]\n    end\n    if preselect_index ~= 0 then\n      preselect_index = #self.entries - preselect_index + 1\n    end\n  end\n\n  -- Apply window options (that might be changed) on the custom completion menu.\n  self.entries_win:option('winblend', completion.winblend)\n  self.entries_win:option('winhighlight', completion.winhighlight)\n  self.entries_win:option('scrolloff', completion.scrolloff)\n  self.entries_win:open({\n    relative = 'editor',\n    style = 'minimal',\n    row = math.max(0, row),\n    col = math.max(0, col + completion.col_offset),\n    width = width,\n    height = height,\n    border = completion.border,\n    zindex = completion.zindex or 1001,\n  })\n\n  -- Don't set the cursor if the entries_win:open function fails\n  -- due to the window's width or height being less than 1\n  if self.entries_win.win == nil then\n    return\n  end\n\n  -- Always set cursor when starting. It will be adjusted on the call to _select\n  vim.api.nvim_win_set_cursor(self.entries_win.win, { 1, 0 })\n  if preselect_index > 0 and c.preselect == types.cmp.PreselectMode.Item then\n    self:_select(preselect_index, { behavior = types.cmp.SelectBehavior.Select, active = false })\n  elseif not string.match(c.completion.completeopt, 'noselect') then\n    if self:is_direction_top_down() then\n      self:_select(1, { behavior = types.cmp.SelectBehavior.Select, active = false })\n    else\n      self:_select(#self.entries, { behavior = types.cmp.SelectBehavior.Select, active = false })\n    end\n  else\n    if self:is_direction_top_down() then\n      self:_select(0, { behavior = types.cmp.SelectBehavior.Select, active = false })\n    else\n      self:_select(#self.entries + 1, { behavior = types.cmp.SelectBehavior.Select, active = false })\n    end\n  end\nend\n\ncustom_entries_view.close = function(self)\n  self.prefix = nil\n  self.offset = -1\n  self.active = false\n  self.entries = {}\n  self.entries_win:close()\n  self.bottom_up = false\nend\n\ncustom_entries_view.abort = function(self)\n  if self.prefix then\n    self:_insert(self.prefix)\n  end\n  feedkeys.call('', 'n', function()\n    self:close()\n  end)\nend\n\ncustom_entries_view.draw = function(self)\n  local info = vim.fn.getwininfo(self.entries_win.win)[1]\n  local topline = info.topline - 1\n  local botline = info.topline + info.height - 1\n  local texts = {}\n  local fields = config.get().formatting.fields\n  local entries_buf = self.entries_win:get_buffer()\n  for i = topline, botline - 1 do\n    local e = self.entries[i + 1]\n    if e then\n      local view = e:get_view(self.offset, entries_buf)\n      local text = {}\n      table.insert(text, string.rep(' ', config.get().window.completion.side_padding))\n      for _, field in ipairs(fields) do\n        table.insert(text, view[field].text)\n        table.insert(text, string.rep(' ', 1 + self.column_width[field] - view[field].width))\n      end\n      table.insert(text, string.rep(' ', config.get().window.completion.side_padding))\n      table.insert(texts, table.concat(text, ''))\n    end\n  end\n  if vim.bo[entries_buf].modifiable == false then\n    vim.bo[entries_buf].modifiable = true\n    vim.api.nvim_buf_set_lines(entries_buf, topline, botline, false, texts)\n    vim.bo[entries_buf].modifiable = false\n  else\n    vim.api.nvim_buf_set_lines(entries_buf, topline, botline, false, texts)\n  end\n  vim.api.nvim_buf_set_option(entries_buf, 'modified', false)\n\n  if api.is_cmdline_mode() then\n    vim.api.nvim_win_call(self.entries_win.win, function()\n      misc.redraw()\n    end)\n  end\nend\n\ncustom_entries_view.visible = function(self)\n  return self.entries_win:visible()\nend\n\ncustom_entries_view.info = function(self)\n  return self.entries_win:info()\nend\n\ncustom_entries_view.get_selected_index = function(self)\n  if self:visible() and self.entries_win:option('cursorline') then\n    return vim.api.nvim_win_get_cursor(self.entries_win.win)[1]\n  end\nend\n\ncustom_entries_view.select_next_item = function(self, option)\n  if self:visible() then\n    local cursor = self:get_selected_index()\n    local is_top_down = self:is_direction_top_down()\n    local last = #self.entries\n\n    if not self.entries_win:option('cursorline') then\n      cursor = (is_top_down and 1) or last\n    else\n      if is_top_down then\n        if cursor == last then\n          cursor = 0\n        else\n          cursor = cursor + option.count\n          if last < cursor then\n            cursor = last\n          end\n        end\n      else\n        if cursor == 0 then\n          cursor = last\n        else\n          cursor = cursor - option.count\n          if cursor < 0 then\n            cursor = 0\n          end\n        end\n      end\n    end\n\n    self:_select(cursor, {\n      behavior = option.behavior or types.cmp.SelectBehavior.Insert,\n      active = true,\n    })\n  end\nend\n\ncustom_entries_view.select_prev_item = function(self, option)\n  if self:visible() then\n    local cursor = self:get_selected_index()\n    local is_top_down = self:is_direction_top_down()\n    local last = #self.entries\n\n    if not self.entries_win:option('cursorline') then\n      cursor = (is_top_down and last) or 1\n    else\n      if is_top_down then\n        if cursor == 1 then\n          cursor = 0\n        else\n          cursor = cursor - option.count\n          if cursor < 0 then\n            cursor = 1\n          end\n        end\n      else\n        if cursor == last then\n          cursor = 0\n        else\n          cursor = cursor + option.count\n          if last < cursor then\n            cursor = last\n          end\n        end\n      end\n    end\n\n    self:_select(cursor, {\n      behavior = option.behavior or types.cmp.SelectBehavior.Insert,\n      active = true,\n    })\n  end\nend\n\ncustom_entries_view.get_offset = function(self)\n  if self:visible() then\n    return self.offset\n  end\n  return nil\nend\n\ncustom_entries_view.get_entries = function(self)\n  if self:visible() then\n    return self.entries\n  end\n  return {}\nend\n\ncustom_entries_view.get_first_entry = function(self)\n  if self:visible() then\n    return (self:is_direction_top_down() and self.entries[1]) or self.entries[#self.entries]\n  end\nend\n\ncustom_entries_view.get_selected_entry = function(self)\n  if self:visible() and self.entries_win:option('cursorline') then\n    return self.entries[self:get_selected_index()]\n  end\nend\n\ncustom_entries_view.get_active_entry = function(self)\n  if self:visible() and self.active then\n    return self:get_selected_entry()\n  end\nend\n\ncustom_entries_view._select = function(self, cursor, option)\n  local is_insert = (option.behavior or types.cmp.SelectBehavior.Insert) == types.cmp.SelectBehavior.Insert\n  if is_insert and not self.active then\n    self.prefix = string.sub(api.get_current_line(), self.offset, api.get_cursor()[2]) or ''\n  end\n  self.active = (0 < cursor and cursor <= #self.entries and option.active == true)\n\n  self.entries_win:option('cursorline', cursor > 0 and cursor <= #self.entries)\n  vim.api.nvim_win_set_cursor(self.entries_win.win, {\n    math.max(math.min(cursor, #self.entries), 1),\n    0,\n  })\n\n  if is_insert then\n    self:_insert(self.entries[cursor] and self.entries[cursor]:get_vim_item(self.offset).word or self.prefix)\n  end\n\n  self.entries_win:update()\n  self:draw()\n  self.event:emit('change')\nend\n\ncustom_entries_view._insert = setmetatable({\n  pending = false,\n}, {\n  __call = function(this, self, word)\n    word = word or ''\n    if api.is_cmdline_mode() then\n      local cursor = api.get_cursor()\n      -- setcmdline() added in v0.8.0\n      if vim.fn.has('nvim-0.8') == 1 then\n        local current_line = api.get_current_line()\n        local before_line = current_line:sub(1, self.offset - 1)\n        local after_line = current_line:sub(cursor[2] + 1)\n        local pos = #before_line + #word + 1\n        vim.fn.setcmdline(before_line .. word .. after_line, pos)\n        vim.api.nvim_feedkeys(keymap.t('<Cmd>redraw<CR>'), 'ni', false)\n      else\n        vim.api.nvim_feedkeys(keymap.backspace(string.sub(api.get_current_line(), self.offset, cursor[2])) .. word, 'int', true)\n      end\n    else\n      if this.pending then\n        return\n      end\n      this.pending = true\n\n      local release = require('cmp').suspend()\n      feedkeys.call('', '', function()\n        local cursor = api.get_cursor()\n        local keys = {}\n        table.insert(keys, keymap.indentkeys())\n        table.insert(keys, keymap.backspace(string.sub(api.get_current_line(), self.offset, cursor[2])))\n        table.insert(keys, word)\n        table.insert(keys, keymap.indentkeys(vim.bo.indentkeys))\n        feedkeys.call(\n          table.concat(keys, ''),\n          'int',\n          vim.schedule_wrap(function()\n            this.pending = false\n            release()\n          end)\n        )\n      end)\n    end\n  end,\n})\n\nreturn custom_entries_view\n"
  },
  {
    "path": "lua/cmp/view/docs_view.lua",
    "content": "local window = require('cmp.utils.window')\nlocal config = require('cmp.config')\n\n---@class cmp.DocsView\n---@field public window cmp.Window\nlocal docs_view = {}\n\n---Create new floating window module\ndocs_view.new = function()\n  local self = setmetatable({}, { __index = docs_view })\n  self.entry = nil\n  self.window = window.new()\n  self.window:option('conceallevel', 2)\n  self.window:option('concealcursor', 'n')\n  self.window:option('foldenable', false)\n  self.window:option('linebreak', true)\n  self.window:option('scrolloff', 0)\n  self.window:option('showbreak', 'NONE')\n  self.window:option('wrap', true)\n  self.window:buffer_option('filetype', 'cmp_docs')\n  self.window:buffer_option('buftype', 'nofile')\n  return self\nend\n\n---Open documentation window\n---@param e cmp.Entry\n---@param view cmp.WindowStyle\n---@param bottom_up boolean|nil\ndocs_view.open = function(self, e, view, bottom_up)\n  local documentation = config.get().window.documentation\n  if not documentation then\n    return\n  end\n\n  if not e or not view then\n    return self:close()\n  end\n\n  local border_info = window.get_border_info({ style = documentation })\n  local right_space = vim.o.columns - (view.col + view.width) - 1\n  local left_space = view.col - 1\n  local max_width = math.max(left_space, right_space)\n  if documentation.max_width > 0 then\n    max_width = math.min(documentation.max_width, max_width)\n  end\n\n  -- Update buffer content if needed.\n  if not self.entry or e.id ~= self.entry.id then\n    local documents = e:get_documentation()\n    if #documents == 0 then\n      return self:close()\n    end\n\n    self.entry = e\n    vim.api.nvim_buf_call(self.window:get_buffer(), function()\n      vim.cmd([[syntax clear]])\n      vim.api.nvim_buf_set_lines(self.window:get_buffer(), 0, -1, false, {})\n    end)\n    local opts = {\n      max_width = max_width - border_info.horiz,\n    }\n    if documentation.max_height > 0 then\n      opts.max_height = documentation.max_height\n    end\n    vim.lsp.util.stylize_markdown(self.window:get_buffer(), documents, opts)\n  end\n\n  -- Set buffer as not modified, so it can be removed without errors\n  vim.api.nvim_buf_set_option(self.window:get_buffer(), 'modified', false)\n\n  -- Calculate window size.\n  local width, height = vim.lsp.util._make_floating_popup_size(vim.api.nvim_buf_get_lines(self.window:get_buffer(), 0, -1, false), {\n    max_width = max_width - border_info.horiz,\n    max_height = documentation.max_height - border_info.vert,\n  })\n  if width <= 0 or height <= 0 then\n    return self:close()\n  end\n\n  -- Calculate window position.\n  local right_col = view.col + view.width\n  local left_col = view.col - width - border_info.horiz\n  local col, left\n  if right_space >= width and left_space >= width then\n    if right_space < left_space then\n      col = left_col\n      left = true\n    else\n      col = right_col\n    end\n  elseif right_space >= width then\n    col = right_col\n  elseif left_space >= width then\n    col = left_col\n    left = true\n  else\n    return self:close()\n  end\n\n  local row = bottom_up and math.max(view.row - (height + border_info.vert - view.height), 1) or view.row\n\n  -- Render window.\n  self.window:option('winblend', documentation.winblend)\n  self.window:option('winhighlight', documentation.winhighlight)\n  local style = {\n    relative = 'editor',\n    style = 'minimal',\n    width = width,\n    height = height,\n    row = view.row,\n    col = col + documentation.col_offset,\n    border = documentation.border,\n    zindex = documentation.zindex or 50,\n  }\n  self.window:open(style)\n\n  -- Correct left-col for scrollbar existence.\n  if left then\n    style.col = col - self.window:info().scrollbar_offset - documentation.col_offset\n    self.window:open(style)\n  end\nend\n\n---Close floating window\ndocs_view.close = function(self)\n  self.window:close()\n  self.entry = nil\nend\n\ndocs_view.scroll = function(self, delta)\n  if self:visible() then\n    local info = vim.fn.getwininfo(self.window.win)[1] or {}\n    local top = info.topline or 1\n    top = top + delta\n    top = math.max(top, 1)\n    top = math.min(top, self.window:get_content_height() - info.height + 1)\n\n    vim.defer_fn(function()\n      vim.api.nvim_buf_call(self.window:get_buffer(), function()\n        vim.api.nvim_command('normal! ' .. top .. 'zt')\n        self.window:update()\n      end)\n    end, 0)\n  end\nend\n\ndocs_view.visible = function(self)\n  return self.window:visible()\nend\n\nreturn docs_view\n"
  },
  {
    "path": "lua/cmp/view/ghost_text_view.lua",
    "content": "local config = require('cmp.config')\nlocal misc = require('cmp.utils.misc')\nlocal snippet = require('cmp.utils.snippet')\n-- local str = require('cmp.utils.str')\nlocal api = require('cmp.utils.api')\nlocal types = require('cmp.types')\n\n---@class cmp.GhostTextView\n---@field win number|nil\n---@field entry cmp.Entry|nil\nlocal ghost_text_view = {}\n\nghost_text_view.ns = vim.api.nvim_create_namespace('cmp:GHOST_TEXT')\n\nlocal has_inline = (function()\n  return (pcall(function()\n    local id = vim.api.nvim_buf_set_extmark(0, ghost_text_view.ns, 0, 0, {\n      virt_text = { { ' ', 'Comment' } },\n      virt_text_pos = 'inline',\n      hl_mode = 'combine',\n      ephemeral = false,\n    })\n    vim.api.nvim_buf_del_extmark(0, ghost_text_view.ns, id)\n  end))\nend)()\n\nghost_text_view.new = function()\n  local self = setmetatable({}, { __index = ghost_text_view })\n  self.win = nil\n  self.entry = nil\n  self.extmark_id = nil\n  vim.api.nvim_set_decoration_provider(ghost_text_view.ns, {\n    on_win = function(_, win)\n      if self.extmark_id then\n        if vim.api.nvim_buf_is_loaded(self.extmark_buf) then\n          vim.api.nvim_buf_del_extmark(self.extmark_buf, ghost_text_view.ns, self.extmark_id)\n        end\n        self.extmark_id = nil\n      end\n\n      if win ~= self.win then\n        return false\n      end\n\n      local c = config.get().experimental.ghost_text\n      if not c then\n        return\n      end\n\n      if not self.entry then\n        return\n      end\n\n      local row, col = unpack(vim.api.nvim_win_get_cursor(0))\n\n      local line = vim.api.nvim_get_current_line()\n      if not has_inline then\n        if string.sub(line, col + 1) ~= '' then\n          return\n        end\n      end\n\n      local text = self.text_gen(self, line, col)\n      if #text > 0 then\n        local virt_lines = {}\n        for _, l in ipairs(vim.fn.split(text, '\\n')) do\n          table.insert(virt_lines, { { l, type(c) == 'table' and c.hl_group or 'Comment' } })\n        end\n        local first_line = table.remove(virt_lines, 1)\n        self.extmark_buf = vim.api.nvim_get_current_buf()\n        self.extmark_id = vim.api.nvim_buf_set_extmark(self.extmark_buf, ghost_text_view.ns, row - 1, col, {\n          right_gravity = true,\n          virt_text = first_line,\n          virt_text_pos = has_inline and 'inline' or 'overlay',\n          virt_lines = virt_lines,\n          hl_mode = 'combine',\n          ephemeral = false,\n        })\n      end\n    end,\n  })\n  return self\nend\n\n---Generate the ghost text\n---  This function calculates the bytes of the entry to display calculating the number\n---  of character differences instead of just byte difference.\nghost_text_view.text_gen = function(self, line, cursor_col)\n  local word = self.entry:get_insert_text()\n  if self.entry:get_completion_item().insertTextFormat == types.lsp.InsertTextFormat.Snippet then\n    word = tostring(snippet.parse(word))\n  end\n  local word_clen = vim.fn.strchars(word, true)\n  local cword = string.sub(line, self.entry.offset, cursor_col)\n  local cword_clen = vim.fn.strchars(cword, true)\n  -- Number of characters from entry text (word) to be displayed as ghost thext\n  local nchars = word_clen - cword_clen\n  -- Missing characters to complete the entry text\n  local text\n  if nchars > 0 then\n    text = string.sub(word, misc.to_vimindex(word, word_clen - nchars))\n  else\n    text = ''\n  end\n  return text\nend\n\n---Show ghost text\n---@param e cmp.Entry\nghost_text_view.show = function(self, e)\n  if not api.is_insert_mode() then\n    return\n  end\n  local c = config.get().experimental.ghost_text\n  if not c then\n    return\n  end\n  local changed = e ~= self.entry\n  self.win = vim.api.nvim_get_current_win()\n  self.entry = e\n  if changed then\n    misc.redraw(true) -- force invoke decoration provider.\n  end\nend\n\nghost_text_view.hide = function(self)\n  if self.win and self.entry then\n    self.win = nil\n    self.entry = nil\n    misc.redraw(true) -- force invoke decoration provider.\n  end\nend\n\nreturn ghost_text_view\n"
  },
  {
    "path": "lua/cmp/view/native_entries_view.lua",
    "content": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal keymap = require('cmp.utils.keymap')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal types = require('cmp.types')\nlocal config = require('cmp.config')\nlocal api = require('cmp.utils.api')\n\n---@class cmp.NativeEntriesView\n---@field private offset integer\n---@field private items vim.CompletedItem\n---@field private entries cmp.Entry[]\n---@field private preselect_index integer\n---@field public event cmp.Event\nlocal native_entries_view = {}\n\nnative_entries_view.new = function()\n  local self = setmetatable({}, { __index = native_entries_view })\n  self.event = event.new()\n  self.offset = -1\n  self.items = {}\n  self.entries = {}\n  self.preselect_index = 0\n  autocmd.subscribe('CompleteChanged', function()\n    self.event:emit('change')\n  end)\n  return self\nend\n\nnative_entries_view.ready = function(_)\n  if vim.fn.pumvisible() == 0 then\n    return true\n  end\n  return vim.fn.complete_info({ 'mode' }).mode == 'eval'\nend\n\nnative_entries_view.on_change = function(self)\n  if #self.entries > 0 and self.offset <= vim.api.nvim_win_get_cursor(0)[2] + 1 then\n    local preselect_enabled = config.get().preselect == types.cmp.PreselectMode.Item\n\n    local completeopt = vim.o.completeopt\n    if self.preselect_index == 1 and preselect_enabled then\n      vim.o.completeopt = 'menu,menuone,noinsert'\n    else\n      vim.o.completeopt = config.get().completion.completeopt\n    end\n    vim.fn.complete(self.offset, self.items)\n    vim.o.completeopt = completeopt\n\n    if self.preselect_index > 1 and preselect_enabled then\n      self:preselect(self.preselect_index)\n    end\n  end\nend\n\nnative_entries_view.open = function(self, offset, entries)\n  local dedup = {}\n  local items = {}\n  local dedup_entries = {}\n  local preselect_index = 0\n  for _, e in ipairs(entries) do\n    local item = e:get_vim_item(offset)\n    if item.dup == 1 or not dedup[item.abbr] then\n      dedup[item.abbr] = true\n      table.insert(items, item)\n      table.insert(dedup_entries, e)\n      if preselect_index == 0 and e.completion_item.preselect then\n        preselect_index = #dedup_entries\n      end\n    end\n  end\n  self.offset = offset\n  self.items = items\n  self.entries = dedup_entries\n  self.preselect_index = preselect_index\n  self:on_change()\nend\n\nnative_entries_view.close = function(self)\n  if api.is_insert_mode() and self:visible() then\n    vim.api.nvim_select_popupmenu_item(-1, false, true, {})\n  end\n  self.offset = -1\n  self.entries = {}\n  self.items = {}\n  self.preselect_index = 0\nend\n\nnative_entries_view.abort = function(_)\n  if api.is_suitable_mode() then\n    vim.api.nvim_select_popupmenu_item(-1, true, true, {})\n  end\nend\n\nnative_entries_view.visible = function(_)\n  return vim.fn.pumvisible() == 1\nend\n\nnative_entries_view.info = function(self)\n  if self:visible() then\n    local info = vim.fn.pum_getpos()\n    return {\n      width = info.width + (info.scrollbar and 1 or 0) + (info.col == 0 and 0 or 1),\n      height = info.height,\n      row = info.row,\n      col = info.col == 0 and 0 or info.col - 1,\n    }\n  end\nend\n\nnative_entries_view.preselect = function(self, index)\n  if self:visible() then\n    if index <= #self.entries then\n      vim.api.nvim_select_popupmenu_item(index - 1, false, false, {})\n    end\n  end\nend\n\nnative_entries_view.get_selected_index = function(self)\n  if self:visible() then\n    local idx = vim.fn.complete_info({ 'selected' }).selected\n    if idx > -1 then\n      return math.max(0, idx) + 1\n    end\n  end\nend\n\nnative_entries_view.select_next_item = function(self, option)\n  local callback = function()\n    self.event:emit('change')\n  end\n  if self:visible() then\n    if (option.behavior or types.cmp.SelectBehavior.Insert) == types.cmp.SelectBehavior.Insert then\n      feedkeys.call(keymap.t(string.rep('<C-n>', option.count)), 'n', callback)\n    else\n      feedkeys.call(keymap.t(string.rep('<Down>', option.count)), 'n', callback)\n    end\n  end\nend\n\nnative_entries_view.select_prev_item = function(self, option)\n  local callback = function()\n    self.event:emit('change')\n  end\n  if self:visible() then\n    if (option.behavior or types.cmp.SelectBehavior.Insert) == types.cmp.SelectBehavior.Insert then\n      feedkeys.call(keymap.t(string.rep('<C-p>', option.count)), 'n', callback)\n    else\n      feedkeys.call(keymap.t(string.rep('<Up>', option.count)), 'n', callback)\n    end\n  end\nend\n\nnative_entries_view.get_offset = function(self)\n  if self:visible() then\n    return self.offset\n  end\n  return nil\nend\n\nnative_entries_view.get_entries = function(self)\n  if self:visible() then\n    return self.entries\n  end\n  return {}\nend\n\nnative_entries_view.get_first_entry = function(self)\n  if self:visible() then\n    return self.entries[1]\n  end\nend\n\nnative_entries_view.get_selected_entry = function(self)\n  local idx = self:get_selected_index()\n  if idx then\n    return self.entries[idx]\n  end\nend\n\nnative_entries_view.get_active_entry = function(self)\n  if self:visible() and (vim.v.completed_item or {}).word then\n    return self:get_selected_entry()\n  end\nend\n\nreturn native_entries_view\n"
  },
  {
    "path": "lua/cmp/view/wildmenu_entries_view.lua",
    "content": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal config = require('cmp.config')\nlocal window = require('cmp.utils.window')\nlocal types = require('cmp.types')\nlocal keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\nlocal api = require('cmp.utils.api')\n\n---@class cmp.CustomEntriesView\n---@field private offset integer\n---@field private entries_win cmp.Window\n---@field private active boolean\n---@field private entries cmp.Entry[]\n---@field public event cmp.Event\nlocal wildmenu_entries_view = {}\n\nwildmenu_entries_view.ns = vim.api.nvim_create_namespace('cmp.view.statusline_entries_view')\n\nwildmenu_entries_view.new = function()\n  local self = setmetatable({}, { __index = wildmenu_entries_view })\n  self.event = event.new()\n  self.offset = -1\n  self.active = false\n  self.entries = {}\n  self.offsets = {}\n  self.selected_index = 0\n  self.entries_win = window.new()\n\n  self.entries_win:option('conceallevel', 2)\n  self.entries_win:option('concealcursor', 'n')\n  self.entries_win:option('cursorlineopt', 'line')\n  self.entries_win:option('foldenable', false)\n  self.entries_win:option('wrap', false)\n  self.entries_win:option('scrolloff', 0)\n  self.entries_win:option('sidescrolloff', 0)\n  self.entries_win:option('winhighlight', 'Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None')\n  self.entries_win:buffer_option('tabstop', 1)\n\n  autocmd.subscribe(\n    'CompleteChanged',\n    vim.schedule_wrap(function()\n      if self:visible() and vim.fn.pumvisible() == 1 then\n        self:close()\n      end\n    end)\n  )\n\n  vim.api.nvim_set_decoration_provider(wildmenu_entries_view.ns, {\n    on_win = function(_, win, buf, _, _)\n      if win ~= self.entries_win.win or buf ~= self.entries_win:get_buffer() then\n        return\n      end\n\n      for i, e in ipairs(self.entries) do\n        if e then\n          local view = e:get_view(self.offset, buf)\n          vim.api.nvim_buf_set_extmark(buf, wildmenu_entries_view.ns, 0, self.offsets[i], {\n            end_line = 0,\n            end_col = self.offsets[i] + view.abbr.bytes,\n            hl_group = view.abbr.hl_group,\n            hl_mode = 'combine',\n            ephemeral = true,\n          })\n\n          if i == self.selected_index then\n            vim.api.nvim_buf_set_extmark(buf, wildmenu_entries_view.ns, 0, self.offsets[i], {\n              end_line = 0,\n              end_col = self.offsets[i] + view.abbr.bytes,\n              hl_group = 'PmenuSel',\n              hl_mode = 'combine',\n              ephemeral = true,\n            })\n          end\n\n          for _, m in ipairs(e:get_view_matches(view.abbr.text) or {}) do\n            vim.api.nvim_buf_set_extmark(buf, wildmenu_entries_view.ns, 0, self.offsets[i] + m.word_match_start - 1, {\n              end_line = 0,\n              end_col = self.offsets[i] + m.word_match_end,\n              hl_group = m.fuzzy and 'CmpItemAbbrMatchFuzzy' or 'CmpItemAbbrMatch',\n              hl_mode = 'combine',\n              ephemeral = true,\n            })\n          end\n        end\n      end\n    end,\n  })\n  return self\nend\n\nwildmenu_entries_view.close = function(self)\n  self.entries_win:close()\nend\n\nwildmenu_entries_view.ready = function()\n  return vim.fn.pumvisible() == 0\nend\n\nwildmenu_entries_view.on_change = function(self)\n  self.active = false\nend\n\nwildmenu_entries_view.open = function(self, offset, entries)\n  self.offset = offset\n  self.entries = {}\n\n  -- Apply window options (that might be changed) on the custom completion menu.\n  self.entries_win:option('winblend', vim.o.pumblend)\n\n  local dedup = {}\n  local preselect = 0\n  local i = 1\n  for _, e in ipairs(entries) do\n    local view = e:get_view(offset, 0)\n    if view.dup == 1 or not dedup[e.completion_item.label] then\n      dedup[e.completion_item.label] = true\n      table.insert(self.entries, e)\n      if preselect == 0 and e.completion_item.preselect then\n        preselect = i\n      end\n      i = i + 1\n    end\n  end\n\n  self.entries_win:open({\n    relative = 'editor',\n    style = 'minimal',\n    row = vim.o.lines - 2,\n    col = 0,\n    width = vim.o.columns,\n    height = 1,\n    zindex = 1001,\n  })\n  self:draw()\n\n  if preselect > 0 and config.get().preselect == types.cmp.PreselectMode.Item then\n    self:_select(preselect, { behavior = types.cmp.SelectBehavior.Select })\n  elseif not string.match(config.get().completion.completeopt, 'noselect') then\n    self:_select(1, { behavior = types.cmp.SelectBehavior.Select })\n  else\n    self:_select(0, { behavior = types.cmp.SelectBehavior.Select })\n  end\nend\n\nwildmenu_entries_view.abort = function(self)\n  feedkeys.call('', 'n', function()\n    self:close()\n  end)\nend\n\nwildmenu_entries_view.draw = function(self)\n  self.offsets = {}\n\n  local entries_buf = self.entries_win:get_buffer()\n  local texts = {}\n  local offset = 0\n  for _, e in ipairs(self.entries) do\n    local view = e:get_view(self.offset, entries_buf)\n    table.insert(self.offsets, offset)\n    table.insert(texts, view.abbr.text)\n    offset = offset + view.abbr.bytes + #self:_get_separator()\n  end\n\n  vim.api.nvim_buf_set_lines(entries_buf, 0, 1, false, { table.concat(texts, self:_get_separator()) })\n  vim.api.nvim_buf_set_option(entries_buf, 'modified', false)\n\n  vim.api.nvim_win_call(0, function()\n    misc.redraw()\n  end)\nend\n\nwildmenu_entries_view.visible = function(self)\n  return self.entries_win:visible()\nend\n\nwildmenu_entries_view.info = function(self)\n  return self.entries_win:info()\nend\n\nwildmenu_entries_view.get_selected_index = function(self)\n  if self:visible() and self.active then\n    return self.selected_index\n  end\nend\n\nwildmenu_entries_view.select_next_item = function(self, option)\n  if self:visible() then\n    local cursor\n    if self.selected_index == 0 or self.selected_index == #self.entries then\n      cursor = option.count\n    else\n      cursor = self.selected_index + option.count\n    end\n    cursor = math.max(math.min(cursor, #self.entries), 0)\n    self:_select(cursor, option)\n  end\nend\n\nwildmenu_entries_view.select_prev_item = function(self, option)\n  if self:visible() then\n    if self.selected_index == 0 or self.selected_index <= 1 then\n      self:_select(#self.entries, option)\n    else\n      self:_select(math.max(self.selected_index - option.count, 0), option)\n    end\n  end\nend\n\nwildmenu_entries_view.get_offset = function(self)\n  if self:visible() then\n    return self.offset\n  end\n  return nil\nend\n\nwildmenu_entries_view.get_entries = function(self)\n  if self:visible() then\n    return self.entries\n  end\n  return {}\nend\n\nwildmenu_entries_view.get_first_entry = function(self)\n  if self:visible() then\n    return self.entries[1]\n  end\nend\n\nwildmenu_entries_view.get_selected_entry = function(self)\n  local idx = self:get_selected_index()\n  if idx then\n    return self.entries[idx]\n  end\nend\n\nwildmenu_entries_view.get_active_entry = function(self)\n  if self:visible() and self.active then\n    return self:get_selected_entry()\n  end\nend\n\nwildmenu_entries_view._select = function(self, selected_index, option)\n  local is_next = self.selected_index < selected_index\n  self.selected_index = selected_index\n  self.active = (selected_index ~= 0)\n\n  if self.active then\n    local e = self:get_active_entry()\n    if option.behavior == types.cmp.SelectBehavior.Insert then\n      local cursor = api.get_cursor()\n      local word = e:get_vim_item(self.offset).word\n      vim.api.nvim_feedkeys(keymap.backspace(string.sub(api.get_current_line(), self.offset, cursor[2])) .. word, 'int', true)\n    end\n    vim.api.nvim_win_call(self.entries_win.win, function()\n      local view = e:get_view(self.offset, self.entries_win:get_buffer())\n      vim.api.nvim_win_set_cursor(0, { 1, self.offsets[selected_index] + (is_next and view.abbr.bytes or 0) })\n      vim.cmd([[redraw!]]) -- Force refresh for vim.api.nvim_set_decoration_provider\n    end)\n  end\n\n  self.event:emit('change')\nend\n\nwildmenu_entries_view._get_separator = function()\n  local c = config.get()\n  return (c and c.view and c.view.entries and c.view.entries.separator) or '  '\nend\n\nreturn wildmenu_entries_view\n"
  },
  {
    "path": "lua/cmp/view.lua",
    "content": "local config = require('cmp.config')\nlocal async = require('cmp.utils.async')\nlocal event = require('cmp.utils.event')\nlocal keymap = require('cmp.utils.keymap')\nlocal docs_view = require('cmp.view.docs_view')\nlocal custom_entries_view = require('cmp.view.custom_entries_view')\nlocal wildmenu_entries_view = require('cmp.view.wildmenu_entries_view')\nlocal native_entries_view = require('cmp.view.native_entries_view')\nlocal ghost_text_view = require('cmp.view.ghost_text_view')\n\n---@class cmp.View\n---@field public event cmp.Event\n---@field private is_docs_view_pinned boolean\n---@field private resolve_dedup cmp.AsyncDedup\n---@field private native_entries_view cmp.NativeEntriesView\n---@field private custom_entries_view cmp.CustomEntriesView\n---@field private wildmenu_entries_view cmp.CustomEntriesView\n---@field private change_dedup cmp.AsyncDedup\n---@field private docs_view cmp.DocsView\n---@field private ghost_text_view cmp.GhostTextView\nlocal view = {}\n\n---Create menu\nview.new = function()\n  local self = setmetatable({}, { __index = view })\n  self.resolve_dedup = async.dedup()\n  self.is_docs_view_pinned = false\n  self.custom_entries_view = custom_entries_view.new()\n  self.native_entries_view = native_entries_view.new()\n  self.wildmenu_entries_view = wildmenu_entries_view.new()\n  self.docs_view = docs_view.new()\n  self.ghost_text_view = ghost_text_view.new()\n  self.event = event.new()\n\n  return self\nend\n\n---Return the view components are available or not.\n---@return boolean\nview.ready = function(self)\n  return self:_get_entries_view():ready()\nend\n\n---OnChange handler.\nview.on_change = function(self)\n  self:_get_entries_view():on_change()\nend\n\n---Open menu\n---@param ctx cmp.Context\n---@param sources cmp.Source[]\n---@return boolean did_open\nview.open = function(self, ctx, sources)\n  local source_group_map = {}\n  for _, s in ipairs(sources) do\n    local group_index = s:get_source_config().group_index or 0\n    if not source_group_map[group_index] then\n      source_group_map[group_index] = {}\n    end\n    table.insert(source_group_map[group_index], s)\n  end\n\n  local group_indexes = vim.tbl_keys(source_group_map)\n  table.sort(group_indexes, function(a, b)\n    return a ~= b and (a < b) or nil\n  end)\n\n  local entries = {}\n  for _, group_index in ipairs(group_indexes) do\n    local source_group = source_group_map[group_index] or {}\n\n    -- check the source triggered by character\n    local has_triggered_by_symbol_source = false\n    for _, s in ipairs(source_group) do\n      if #s:get_entries(ctx) > 0 then\n        if s.is_triggered_by_symbol then\n          has_triggered_by_symbol_source = true\n          break\n        end\n      end\n    end\n\n    -- create filtered entries.\n    local offset = ctx.cursor.col\n    local group_entries = {}\n    local max_item_counts = {}\n    for i, s in ipairs(source_group) do\n      if s.offset <= ctx.cursor.col then\n        if not has_triggered_by_symbol_source or s.is_triggered_by_symbol then\n          -- prepare max_item_counts map for filtering after sort.\n          local max_item_count = s:get_source_config().max_item_count\n          if max_item_count ~= nil then\n            max_item_counts[s.name] = max_item_count\n          end\n\n          -- source order priority bonus.\n          local priority = s:get_source_config().priority or ((#source_group - (i - 1)) * config.get().sorting.priority_weight)\n\n          for _, e in ipairs(s:get_entries(ctx)) do\n            e.score = e.score + priority\n            table.insert(group_entries, e)\n            offset = math.min(offset, e.offset)\n          end\n        end\n      end\n    end\n\n    -- sort.\n    local comparators = config.get().sorting.comparators\n    table.sort(group_entries, function(e1, e2)\n      for _, fn in ipairs(comparators) do\n        local diff = fn(e1, e2)\n        if diff ~= nil then\n          return diff\n        end\n      end\n    end)\n\n    -- filter by max_item_count.\n    for _, e in ipairs(group_entries) do\n      if max_item_counts[e.source.name] ~= nil then\n        if max_item_counts[e.source.name] > 0 then\n          max_item_counts[e.source.name] = max_item_counts[e.source.name] - 1\n          table.insert(entries, e)\n        end\n      else\n        table.insert(entries, e)\n      end\n    end\n\n    local max_view_entries = config.get().performance.max_view_entries or 200\n    entries = vim.list_slice(entries, 1, max_view_entries)\n\n    -- open\n    if #entries > 0 then\n      self:_get_entries_view():open(offset, entries)\n      self.event:emit('menu_opened', {\n        window = self:_get_entries_view(),\n      })\n      break\n    end\n  end\n\n  -- complete_done.\n  if #entries == 0 then\n    self:close()\n  end\n  return #entries > 0\nend\n\n---Close menu\nview.close = function(self)\n  if self:visible() then\n    self.is_docs_view_pinned = false\n    self.event:emit('complete_done', {\n      entry = self:_get_entries_view():get_selected_entry(),\n    })\n  end\n  self:_get_entries_view():close()\n  self.docs_view:close()\n  self.ghost_text_view:hide()\n  self.event:emit('menu_closed', {\n    window = self:_get_entries_view(),\n  })\nend\n\n---Abort menu\nview.abort = function(self)\n  if self:visible() then\n    self.is_docs_view_pinned = false\n  end\n  self:_get_entries_view():abort()\n  self.docs_view:close()\n  self.ghost_text_view:hide()\n  self.event:emit('menu_closed', {\n    window = self:_get_entries_view(),\n  })\nend\n\n---Return the view is visible or not.\n---@return boolean\nview.visible = function(self)\n  return self:_get_entries_view():visible()\nend\n\n---Opens the documentation window.\nview.open_docs = function(self)\n  self.is_docs_view_pinned = true\n  local e = self:get_selected_entry()\n  if e then\n    e:resolve(vim.schedule_wrap(self.resolve_dedup(function()\n      if not self:visible() then\n        return\n      end\n      local bottom_up = self.custom_entries_view.bottom_up\n      self.docs_view:open(e, self:_get_entries_view():info(), bottom_up)\n    end)))\n  end\nend\n\n---Closes the documentation window.\nview.close_docs = function(self)\n  self.is_docs_view_pinned = false\n  if self:get_selected_entry() then\n    self.docs_view:close()\n  end\nend\n\n---Scroll documentation window if possible.\n---@param delta integer\nview.scroll_docs = function(self, delta)\n  self.docs_view:scroll(delta)\nend\n\n---Get what number candidates are currently selected.\n---If not selected, nil is returned.\n---@return integer|nil\nview.get_selected_index = function(self)\n  return self:_get_entries_view():get_selected_index()\nend\n\n---Select prev menu item.\n---@param option cmp.SelectOption\nview.select_next_item = function(self, option)\n  self:_get_entries_view():select_next_item(option)\nend\n\n---Select prev menu item.\n---@param option cmp.SelectOption\nview.select_prev_item = function(self, option)\n  self:_get_entries_view():select_prev_item(option)\nend\n\n---Get offset.\nview.get_offset = function(self)\n  return self:_get_entries_view():get_offset()\nend\n\n---Get entries.\n---@return cmp.Entry[]\nview.get_entries = function(self)\n  return self:_get_entries_view():get_entries()\nend\n\n---Get first entry\n---@param self cmp.Entry|nil\nview.get_first_entry = function(self)\n  return self:_get_entries_view():get_first_entry()\nend\n\n---Get current selected entry\n---@return cmp.Entry|nil\nview.get_selected_entry = function(self)\n  return self:_get_entries_view():get_selected_entry()\nend\n\n---Get current active entry\n---@return cmp.Entry|nil\nview.get_active_entry = function(self)\n  return self:_get_entries_view():get_active_entry()\nend\n\n---Return current configured entries_view\n---@return cmp.CustomEntriesView|cmp.NativeEntriesView\nview._get_entries_view = function(self)\n  self.native_entries_view.event:clear()\n  self.custom_entries_view.event:clear()\n  self.wildmenu_entries_view.event:clear()\n\n  local c = config.get()\n  local v = self.custom_entries_view\n  if (c.view and c.view.entries and (c.view.entries.name or c.view.entries)) == 'wildmenu' then\n    v = self.wildmenu_entries_view\n  elseif (c.view and c.view.entries and (c.view.entries.name or c.view.entries)) == 'native' then\n    v = self.native_entries_view\n  end\n  v.event:on('change', function()\n    self:on_entry_change()\n  end)\n  return v\nend\n\n---On entry change\nview.on_entry_change = async.throttle(function(self)\n  if not self:visible() then\n    return\n  end\n  local e = self:get_selected_entry()\n  if e then\n    for _, c in ipairs(config.get().confirmation.get_commit_characters(e:get_commit_characters())) do\n      keymap.listen('i', c, function(...)\n        self.event:emit('keymap', ...)\n      end)\n    end\n    e:resolve(vim.schedule_wrap(self.resolve_dedup(function()\n      if not self:visible() then\n        return\n      end\n      if self.is_docs_view_pinned or config.get().view.docs.auto_open then\n        local bottom_up = self.custom_entries_view.bottom_up\n        self.docs_view:open(e, self:_get_entries_view():info(), bottom_up)\n      end\n    end)))\n  else\n    self.docs_view:close()\n  end\n\n  e = e or self:get_first_entry()\n  if e then\n    self.ghost_text_view:show(e)\n  else\n    self.ghost_text_view:hide()\n  end\nend, 20)\n\nreturn view\n"
  },
  {
    "path": "lua/cmp/vim_source.lua",
    "content": "local misc = require('cmp.utils.misc')\n\nlocal vim_source = {}\n\n---@param id integer\n---@param args any[]\nvim_source.on_callback = function(id, args)\n  if vim_source.to_callback.callbacks[id] then\n    vim_source.to_callback.callbacks[id](unpack(args))\n  end\nend\n\n---@param callback function\n---@return integer\nvim_source.to_callback = setmetatable({\n  callbacks = {},\n}, {\n  __call = function(self, callback)\n    local id = misc.id('cmp.vim_source.to_callback')\n    self.callbacks[id] = function(...)\n      callback(...)\n      self.callbacks[id] = nil\n    end\n    return id\n  end,\n})\n\n---Convert to serializable args.\n---@param args any[]\nvim_source.to_args = function(args)\n  for i, arg in ipairs(args) do\n    if type(arg) == 'function' then\n      args[i] = vim_source.to_callback(arg)\n    end\n  end\n  return args\nend\n\n---@param bridge_id integer\n---@param methods string[]\nvim_source.new = function(bridge_id, methods)\n  local self = {}\n  for _, method in ipairs(methods) do\n    self[method] = (function(m)\n      return function(_, ...)\n        return vim.fn['cmp#_method'](bridge_id, m, vim_source.to_args({ ... }))\n      end\n    end)(method)\n  end\n  return self\nend\n\nreturn vim_source\n"
  },
  {
    "path": "nvim-cmp-scm-1.rockspec",
    "content": "local MODREV, SPECREV = 'scm', '-1'\nrockspec_format = '3.0'\npackage = 'nvim-cmp'\nversion = MODREV .. SPECREV\n\ndescription = {\n  summary = 'A completion plugin for neovim',\n  labels = { 'neovim' },\n  detailed = [[\n    A completion engine plugin for neovim written in Lua. Completion sources are installed from external repositories and \"sourced\".\n   ]],\n  homepage = 'https://github.com/hrsh7th/nvim-cmp',\n  license = 'MIT',\n}\n\ndependencies = {\n  'lua >= 5.1, < 5.4',\n}\n\nsource = {\n  url = 'git://github.com/hrsh7th/nvim-cmp',\n}\n\nbuild = {\n  type = 'builtin',\n  copy_directories = {\n    'autoload',\n    'plugin',\n    'doc'\n  }\n}\n"
  },
  {
    "path": "plugin/cmp.lua",
    "content": "if vim.g.loaded_cmp then\n  return\nend\nvim.g.loaded_cmp = true\n\nif not vim.api.nvim_create_autocmd then\n  return print('[nvim-cmp] Your nvim does not has `nvim_create_autocmd` function. Please update to latest nvim.')\nend\n\nlocal api = require('cmp.utils.api')\nlocal types = require('cmp.types')\nlocal highlight = require('cmp.utils.highlight')\nlocal autocmd = require('cmp.utils.autocmd')\n\nvim.api.nvim_set_hl(0, 'CmpItemAbbr', { link = 'CmpItemAbbrDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemAbbrDeprecated', { link = 'CmpItemAbbrDeprecatedDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemAbbrMatch', { link = 'CmpItemAbbrMatchDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemAbbrMatchFuzzy', { link = 'CmpItemAbbrMatchFuzzyDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemKind', { link = 'CmpItemKindDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemKindIcon', { link = 'CmpItemKindIconDefault', default = true })\nvim.api.nvim_set_hl(0, 'CmpItemMenu', { link = 'CmpItemMenuDefault', default = true })\nfor kind in pairs(types.lsp.CompletionItemKind) do\n  if type(kind) == 'string' then\n    local name = ('CmpItemKind%s'):format(kind)\n    local icon_hl = name .. \"Icon\"\n    vim.api.nvim_set_hl(0, name, { link = ('%sDefault'):format(name), default = true })\n    vim.api.nvim_set_hl(0, icon_hl, { link = ('%sDefault'):format(icon_hl), default = true })\n  end\nend\n\nautocmd.subscribe({ 'ColorScheme', 'UIEnter' }, function()\n  highlight.inherit('CmpItemAbbrDefault', 'Pmenu', { bg = 'NONE', default = false })\n  highlight.inherit('CmpItemAbbrDeprecatedDefault', 'Comment', { bg = 'NONE', default = false })\n  highlight.inherit('CmpItemAbbrMatchDefault', 'Pmenu', { bg = 'NONE', default = false })\n  highlight.inherit('CmpItemAbbrMatchFuzzyDefault', 'Pmenu', { bg = 'NONE', default = false })\n  highlight.inherit('CmpItemKindDefault', 'Special', { bg = 'NONE', default = false })\n  highlight.inherit('CmpItemKindIconDefault', 'Special', {bg = 'NONE', default = false })\n  highlight.inherit('CmpItemMenuDefault', 'Pmenu', { bg = 'NONE', default = false })\n  for name in pairs(types.lsp.CompletionItemKind) do\n    if type(name) == 'string' then\n      vim.api.nvim_set_hl(0, ('CmpItemKind%sDefault'):format(name), { link = 'CmpItemKind', default = false })\n      vim.api.nvim_set_hl(0, ('CmpItemKind%sIconDefault'):format(name), {link = 'CmpItemKindIcon', default = false })\n    end\n  end\nend)\nautocmd.emit('ColorScheme')\n\nif vim.on_key then\n  local control_c_termcode = vim.api.nvim_replace_termcodes('<C-c>', true, true, true)\n  vim.on_key(function(keys)\n    if keys == control_c_termcode then\n      vim.schedule(function()\n        if not api.is_suitable_mode() then\n          autocmd.emit('InsertLeave')\n        end\n      end)\n    end\n  end, vim.api.nvim_create_namespace('cmp.plugin'))\nend\n\n\nvim.api.nvim_create_user_command('CmpStatus', function()\n  require('cmp').status()\nend, { desc = 'Check status of cmp sources' })\n\nvim.cmd([[doautocmd <nomodeline> User CmpReady]])\n"
  },
  {
    "path": "stylua.toml",
    "content": "indent_type = \"Spaces\"\nindent_width = 2\ncolumn_width = 1200\nquote_style = \"AutoPreferSingle\"\n"
  },
  {
    "path": "utils/vimrc.vim",
    "content": "if has('vim_starting')\n  set encoding=utf-8\nendif\nscriptencoding utf-8\n\nif &compatible\n  set nocompatible\nendif\n\nlet s:plug_dir = expand('/tmp/plugged/vim-plug')\nif !filereadable(s:plug_dir .. '/plug.vim')\n  execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)\nend\n\nexecute 'set runtimepath+=' . s:plug_dir\ncall plug#begin(s:plug_dir)\nPlug 'hrsh7th/nvim-cmp'\nPlug 'hrsh7th/cmp-buffer'\nPlug 'hrsh7th/cmp-nvim-lsp'\nPlug 'hrsh7th/vim-vsnip'\nPlug 'neovim/nvim-lspconfig'\ncall plug#end()\nPlugInstall | quit\n\n\" Setup global configuration. More on configuration below.\nlua << EOF\nlocal cmp = require \"cmp\"\ncmp.setup {\n  snippet = {\n    expand = function(args)\n      vim.fn[\"vsnip#anonymous\"](args.body)\n    end,\n  },\n\n  mapping = {\n    ['<CR>'] = cmp.mapping.confirm({ select = true })\n  },\n\n  sources = cmp.config.sources({\n    { name = \"nvim_lsp\" },\n    { name = \"buffer\" },\n  }),\n}\nEOF\n\nlua << EOF\nlocal capabilities = require('cmp_nvim_lsp').default_capabilities()\n\nvim.lsp.config('cssls', {\n  capabilities = capabilities,\n})\nvim.lsp.enable('cssls')\nEOF\n\n"
  }
]