Full Code of hrsh7th/nvim-cmp for AI

main da88697d7f45 cached
73 files
320.6 KB
85.5k tokens
1 requests
Download .txt
Showing preview only (340K chars total). Download the full file or copy to clipboard to get everything.
Repository: hrsh7th/nvim-cmp
Branch: main
Commit: da88697d7f45
Files: 73
Total size: 320.6 KB

Directory structure:
gitextract_18jfbkey/

├── .githooks/
│   └── pre-commit
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   └── workflows/
│       ├── format.yaml
│       ├── integration.yaml
│       └── release-please.yaml
├── .gitignore
├── .luacheckrc
├── LICENSE
├── Makefile
├── README.md
├── autoload/
│   └── cmp.vim
├── doc/
│   └── cmp.txt
├── init.sh
├── lua/
│   └── cmp/
│       ├── config/
│       │   ├── compare.lua
│       │   ├── context.lua
│       │   ├── default.lua
│       │   ├── mapping.lua
│       │   ├── sources.lua
│       │   └── window.lua
│       ├── config.lua
│       ├── context.lua
│       ├── context_spec.lua
│       ├── core.lua
│       ├── core_spec.lua
│       ├── entry.lua
│       ├── entry_spec.lua
│       ├── init.lua
│       ├── matcher.lua
│       ├── matcher_spec.lua
│       ├── source.lua
│       ├── source_spec.lua
│       ├── types/
│       │   ├── cmp.lua
│       │   ├── init.lua
│       │   ├── lsp.lua
│       │   ├── lsp_spec.lua
│       │   └── vim.lua
│       ├── utils/
│       │   ├── api.lua
│       │   ├── api_spec.lua
│       │   ├── async.lua
│       │   ├── async_spec.lua
│       │   ├── autocmd.lua
│       │   ├── binary.lua
│       │   ├── binary_spec.lua
│       │   ├── buffer.lua
│       │   ├── cache.lua
│       │   ├── char.lua
│       │   ├── debug.lua
│       │   ├── event.lua
│       │   ├── feedkeys.lua
│       │   ├── feedkeys_spec.lua
│       │   ├── highlight.lua
│       │   ├── keymap.lua
│       │   ├── keymap_spec.lua
│       │   ├── misc.lua
│       │   ├── misc_spec.lua
│       │   ├── options.lua
│       │   ├── pattern.lua
│       │   ├── snippet.lua
│       │   ├── spec.lua
│       │   ├── str.lua
│       │   ├── str_spec.lua
│       │   └── window.lua
│       ├── view/
│       │   ├── custom_entries_view.lua
│       │   ├── docs_view.lua
│       │   ├── ghost_text_view.lua
│       │   ├── native_entries_view.lua
│       │   └── wildmenu_entries_view.lua
│       ├── view.lua
│       └── vim_source.lua
├── nvim-cmp-scm-1.rockspec
├── plugin/
│   └── cmp.lua
├── stylua.toml
└── utils/
    └── vimrc.vim

================================================
FILE CONTENTS
================================================

================================================
FILE: .githooks/pre-commit
================================================
#!/bin/sh

DIR="$(dirname $(dirname $( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )))"

cd $DIR
make pre-commit
for FILE in `git diff --staged --name-only`; do
    git add $FILE
done


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Report a problem in nvim-cmp
labels: [bug]
body:
  - type: checkboxes
    id: faq-prerequisite
    attributes:
      label: FAQ
      options:
        - label: I have checked the [FAQ](https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt) and it didn't resolve my problem.
          required: true

  - type: checkboxes
    id: announcement-prerequisite
    attributes:
      label: Announcement
      options:
        - label: I have checked [Breaking change announcement](https://github.com/hrsh7th/nvim-cmp/issues/231).
          required: true

  - type: textarea
    attributes:
      label: "Minimal reproducible full config"
      description: |
        You must provide a working config based on [this](https://github.com/hrsh7th/nvim-cmp/blob/main/utils/vimrc.vim). Not part of config.
        1. Copy the base minimal config to the `~/cmp-repro.vim`
        2. Edit `~/cmp-repro.vim` for reproducing the issue
        3. Open `nvim -u ~/cmp-repro.vim`
        4. Check reproduction step
      value: |
        ```vim
        
        ```
    validations:
      required: true

  - type: textarea
    attributes:
      label: "Description"
      description: "Describe in detail what happens"
    validations:
      required: true

  - type: textarea
    attributes:
      label: "Steps to reproduce"
      description: "Full reproduction steps. Include a sample file if your issue relates to a specific filetype."
    validations:
      required: true

  - type: textarea
    attributes:
      label: "Expected behavior"
      description: "A description of the behavior you expected."
    validations:
      required: true

  - type: textarea
    attributes:
      label: "Actual behavior"
      description: "A description of the actual behavior."
    validations:
      required: true

  - type: textarea
    attributes:
      label: "Additional context"
      description: "Any other relevant information"


================================================
FILE: .github/workflows/format.yaml
================================================
name: format

on:
  push:
    branches:
      - main
    paths:
      - '**.lua'

jobs:
  postprocessing:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Format with Stylua
        uses: JohnnyMorganz/stylua-action@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          version: v0.16.1
          args: ./lua

      - uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: "Format with stylua"


================================================
FILE: .github/workflows/integration.yaml
================================================
name: integration

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  integration:
    runs-on: ubuntu-latest
    steps:

    - name: Checkout
      uses: actions/checkout@v2

    - name: Setup neovim
      uses: rhysd/action-setup-vim@v1
      with:
        version: nightly
        neovim: true

    - name: Setup lua
      uses: leafo/gh-actions-lua@v10
      with:
        luaVersion: "luajit-openresty"

    - name: Setup luarocks
      uses: leafo/gh-actions-luarocks@v6

    - name: Setup tools
      shell: bash
      run: |
        luarocks install luacheck
        luarocks install vusted

    - name: Run tests
      shell: bash
      run: make integration


================================================
FILE: .github/workflows/release-please.yaml
================================================
---
permissions:
  contents: write
  pull-requests: write

name: Release Please

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  release:
    name: release
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
        with:
          release-type: simple


================================================
FILE: .gitignore
================================================
doc/tags
utils/stylua
.DS_Store



================================================
FILE: .luacheckrc
================================================
globals = { 'vim', 'describe', 'it', 'before_each', 'after_each', 'assert', 'async' }
max_line_length = false


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 hrsh7th

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
.PHONY: lint
lint:
	luacheck ./lua

.PHONY: test
test:
	vusted --output=gtest ./lua

.PHONY: pre-commit
pre-commit:
	luacheck lua
	vusted lua

.PHONY: integration
integration:
	luacheck lua
	vusted lua


================================================
FILE: README.md
================================================
# nvim-cmp

A completion engine plugin for neovim written in Lua.
Completion sources are installed from external repositories and "sourced".

https://github.com/hrsh7th/nvim-cmp/assets/22756295/afa70011-9121-4e42-aedd-0153b630eeab

Readme!
====================

1. 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.
2. This is my hobby project. You can support me via GitHub sponsors.
3. Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue.
4. 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.

Concept
====================

- Full support for LSP completion related capabilities
- Powerful customizability via Lua functions
- Smart handling of key mappings
- No flicker


Setup
====================

### Recommended Configuration

This example configuration uses `vim-plug` as the plugin manager and `vim-vsnip` as a snippet plugin.

```vim
call plug#begin(s:plug_dir)
Plug 'neovim/nvim-lspconfig'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-cmdline'
Plug 'hrsh7th/nvim-cmp'

" For vsnip users.
Plug 'hrsh7th/cmp-vsnip'
Plug 'hrsh7th/vim-vsnip'

" For luasnip users.
" Plug 'L3MON4D3/LuaSnip'
" Plug 'saadparwaiz1/cmp_luasnip'

" For mini.snippets users.
" Plug 'echasnovski/mini.snippets'
" Plug 'abeldekat/cmp-mini-snippets'

" For ultisnips users.
" Plug 'SirVer/ultisnips'
" Plug 'quangnguyen30192/cmp-nvim-ultisnips'

" For snippy users.
" Plug 'dcampos/nvim-snippy'
" Plug 'dcampos/cmp-snippy'

call plug#end()

lua <<EOF
  -- Set up nvim-cmp.
  local cmp = require'cmp'

  cmp.setup({
    snippet = {
      -- REQUIRED - you must specify a snippet engine
      expand = function(args)
        vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
        -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
        -- require('snippy').expand_snippet(args.body) -- For `snippy` users.
        -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
        -- vim.snippet.expand(args.body) -- For native neovim snippets (Neovim v0.10+)

        -- For `mini.snippets` users:
        -- local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
        -- insert({ body = args.body }) -- Insert at cursor
        -- cmp.resubscribe({ "TextChangedI", "TextChangedP" })
        -- require("cmp.config").set_onetime({ sources = {} })
      end,
    },
    window = {
      -- completion = cmp.config.window.bordered(),
      -- documentation = cmp.config.window.bordered(),
    },
    mapping = cmp.mapping.preset.insert({
      ['<C-b>'] = cmp.mapping.scroll_docs(-4),
      ['<C-f>'] = cmp.mapping.scroll_docs(4),
      ['<C-Space>'] = cmp.mapping.complete(),
      ['<C-e>'] = cmp.mapping.abort(),
      ['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
    }),
    sources = cmp.config.sources({
      { name = 'nvim_lsp' },
      { name = 'vsnip' }, -- For vsnip users.
      -- { name = 'luasnip' }, -- For luasnip users.
      -- { name = 'ultisnips' }, -- For ultisnips users.
      -- { name = 'snippy' }, -- For snippy users.
    }, {
      { name = 'buffer' },
    })
  })

  -- To use git you need to install the plugin petertriho/cmp-git and uncomment lines below
  -- Set configuration for specific filetype.
  --[[ cmp.setup.filetype('gitcommit', {
    sources = cmp.config.sources({
      { name = 'git' },
    }, {
      { name = 'buffer' },
    })
 })
 require("cmp_git").setup() ]]--

  -- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).
  cmp.setup.cmdline({ '/', '?' }, {
    mapping = cmp.mapping.preset.cmdline(),
    sources = {
      { name = 'buffer' }
    }
  })

  -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
  cmp.setup.cmdline(':', {
    mapping = cmp.mapping.preset.cmdline(),
    sources = cmp.config.sources({
      { name = 'path' }
    }, {
      { name = 'cmdline' }
    }),
    matching = { disallow_symbol_nonprefix_matching = false }
  })

  -- Set up lspconfig.
  local capabilities = require('cmp_nvim_lsp').default_capabilities()
  -- Replace <YOUR_LSP_SERVER> with each lsp server you've enabled.
  vim.lsp.config('<YOUR_LSP_SERVER>', {
    capabilities = capabilities
  })
  vim.lsp.enable('<YOUR_LSP_SERVER>')
EOF
```


### Where can I find more completion sources?

Have 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).


### Where can I find advanced configuration examples?

See the [Wiki](https://github.com/hrsh7th/nvim-cmp/wiki).


================================================
FILE: autoload/cmp.vim
================================================
let s:bridge_id = 0
let s:sources = {}

"
" cmp#register_source
"
function! cmp#register_source(name, source) abort
  let l:methods = []
  for l:method in [
  \   'is_available',
  \   'get_debug_name',
  \   'get_position_encoding_kind',
  \   'get_trigger_characters',
  \   'get_keyword_pattern',
  \   'complete',
  \   'execute',
  \   'resolve'
  \ ]
    if has_key(a:source, l:method) && type(a:source[l:method]) == v:t_func
      call add(l:methods, l:method)
    endif
  endfor

  let s:bridge_id += 1
  let a:source.bridge_id = s:bridge_id
  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])
  let s:sources[s:bridge_id] = a:source
  return a:source.id
endfunction

"
" cmp#unregister_source
"
function! cmp#unregister_source(id) abort
  if has_key(s:sources, a:id)
    unlet s:sources[a:id]
  endif
  call luaeval('require("cmp").unregister_source(_A)', a:id)
endfunction

"
" cmp#_method
"
function! cmp#_method(bridge_id, method, args) abort
  try
    let l:source = s:sources[a:bridge_id]
    if a:method ==# 'is_available'
      return l:source[a:method]()
    elseif a:method ==# 'get_debug_name'
      return l:source[a:method]()
    elseif a:method ==# 'get_position_encoding_kind'
      return l:source[a:method](a:args[0])
    elseif a:method ==# 'get_keyword_pattern'
      return l:source[a:method](a:args[0])
    elseif a:method ==# 'get_trigger_characters'
      return l:source[a:method](a:args[0])
    elseif a:method ==# 'complete'
      return l:source[a:method](a:args[0], s:callback(a:args[1]))
    elseif a:method ==# 'resolve'
      return l:source[a:method](a:args[0], s:callback(a:args[1]))
    elseif a:method ==# 'execute'
      return l:source[a:method](a:args[0], s:callback(a:args[1]))
    endif
  catch /.*/
    echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
  endtry
  return v:null
endfunction

"
" s:callback
"
function! s:callback(id) abort
  return { ... -> luaeval('require("cmp.vim_source").on_callback(_A[1], _A[2])', [a:id, a:000]) }
endfunction


================================================
FILE: doc/cmp.txt
================================================
*nvim-cmp* *cmp*

A completion plugin for neovim coded in Lua.

==============================================================================
CONTENTS                                                          *cmp-contents*

Abstract                                                          |cmp-abstract|
Concept                                                            |cmp-concept|
Usage                                                                |cmp-usage|
Function                                                          |cmp-function|
Mapping                                                            |cmp-mapping|
Command                                                            |cmp-command|
Highlight                                                        |cmp-highlight|
FileType                                                          |cmp-filetype|
Autocmd                                                            |cmp-autocmd|
Config                                                              |cmp-config|
Config Helper                                                |cmp-config-helper|
Develop                                                            |cmp-develop|
FAQ                                                                    |cmp-faq|

==============================================================================
Abstract                                                          *cmp-abstract*

This is nvim-cmp's document.

1. This help file uses the type definition notation like `{lsp,cmp,vim}.*`
  - You can find it in `../lua/cmp/types/init.lua`.
2. Advanced configuration is described in the wiki.
  - https://github.com/hrsh7th/nvim-cmp/wiki

==============================================================================
Concept                                                            *cmp-concept*

- Full support for LSP completion related capabilities
- Powerful customization abilities via Lua functions
- Smart handling of key mappings
- No flicker

==============================================================================
Usage                                                                *cmp-usage*

A recommended configuration can be found below.
  NOTE:
    1. You must provide a `snippet.expand` function.
    2. `cmp.setup.cmdline` won't work if you use the `native` completion menu.
    3. You can disable the `default` options by specifying `cmp.config.disable` value.
>vim
  call plug#begin(s:plug_dir)
  Plug 'neovim/nvim-lspconfig'
  Plug 'hrsh7th/cmp-nvim-lsp'
  Plug 'hrsh7th/cmp-buffer'
  Plug 'hrsh7th/cmp-path'
  Plug 'hrsh7th/cmp-cmdline'
  Plug 'hrsh7th/nvim-cmp'

  " For vsnip users.
  Plug 'hrsh7th/cmp-vsnip'
  Plug 'hrsh7th/vim-vsnip'

  " For luasnip users.
  " Plug 'L3MON4D3/LuaSnip'
  " Plug 'saadparwaiz1/cmp_luasnip'

  " For mini.snippets users.
  " Plug 'echasnovski/mini.snippets'
  " Plug 'abeldekat/cmp-mini-snippets'

  " For snippy users.
  " Plug 'dcampos/nvim-snippy'
  " Plug 'dcampos/cmp-snippy'

  " For ultisnips users.
  " Plug 'SirVer/ultisnips'
  " Plug 'quangnguyen30192/cmp-nvim-ultisnips'

  call plug#end()

  set completeopt=menu,menuone,noselect

  lua <<EOF
    local cmp = require'cmp'

    -- Global setup.
    cmp.setup({
      snippet = {
        expand = function(args)
          vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
          -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
          -- require'snippy'.expand_snippet(args.body) -- For `snippy` users.
          -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
          -- vim.snippet.expand(args.body) -- For native neovim snippets (Neovim v0.10+)

          -- For `mini.snippets` users:
          -- local insert = MiniSnippets.config.expand.insert or MiniSnippets.default_insert
          -- insert({ body = args.body }) -- Insert at cursor
          -- cmp.resubscribe({ "TextChangedI", "TextChangedP" })
          -- require("cmp.config").set_onetime({ sources = {} })
        end,
      },
      window = {
        -- completion = cmp.config.window.bordered(),
        -- documentation = cmp.config.window.bordered(),
      },
      mapping = cmp.mapping.preset.insert({
        ['<C-d>'] = cmp.mapping.scroll_docs(-4),
        ['<C-f>'] = cmp.mapping.scroll_docs(4),
        ['<C-Space>'] = cmp.mapping.complete(),
        ['<CR>'] = cmp.mapping.confirm({ select = true }),
      }),
      sources = cmp.config.sources({
        { name = 'nvim_lsp' },
        { name = 'vsnip' }, -- For vsnip users.
        -- { name = 'luasnip' }, -- For luasnip users.
        -- { name = 'snippy' }, -- For snippy users.
        -- { name = 'ultisnips' }, -- For ultisnips users.
      }, {
        { name = 'buffer' },
      })
    })

    -- `/` cmdline setup.
    cmp.setup.cmdline('/', {
      mapping = cmp.mapping.preset.cmdline(),
      sources = {
        { name = 'buffer' }
      }
    })

    -- `:` cmdline setup.
    cmp.setup.cmdline(':', {
      mapping = cmp.mapping.preset.cmdline(),
      sources = cmp.config.sources({
        { name = 'path' }
      }, {
        { name = 'cmdline' }
      }),
      matching = { disallow_symbol_nonprefix_matching = false }
    })

    -- Setup lspconfig.
    local capabilities = require('cmp_nvim_lsp').default_capabilities()
    vim.lsp.config(%YOUR_LSP_SERVER%, {
      capabilities = capabilities
    })
    vim.lsp.enable(%YOUR_LSP_SERVER%)
  EOF
<
==============================================================================
Function                                                          *cmp-function*

NOTE: `<Cmd>lua require('cmp').complete()<CR>` can be used to call these functions in a mapping.

*cmp.setup* (config: cmp.ConfigSchema)
  Setup global configuration. See configuration options.

*cmp.setup.filetype* (filetype: string, config: cmp.ConfigSchema)
  Setup filetype-specific configuration.

*cmp.setup.buffer* (config: cmp.ConfigSchema)
  Setup configuration for the current buffer.

*cmp.setup.cmdline* (cmdtype: string, config: cmp.ConfigSchema)
  Setup cmdline configuration for the specific type of command.
  See |getcmdtype()|.
  NOTE: nvim-cmp does not support the `=` command type.

*cmp.get_registered_sources* ()
  Get all registered sources.

*cmp.visible* ()
  Return a boolean showing whether the completion menu is visible or not.

*cmp.visible_docs* ()
  Return a boolean showing whether the docs window is visible or not.

*cmp.get_entries* ()
  Return all current entries.

*cmp.get_selected_entry* ()
  Return currently selected entry (including preselected).

*cmp.get_active_entry* ()
  Return currently selected entry (excluding preselected).

*cmp.close* ()
  Close the completion menu.

*cmp.abort* ()
  Closes the completion menu and restore the current line to the state before the current completion was started.

*cmp.select_next_item* (option: { behavior = cmp.SelectBehavior, count = 1 })
  Select the next item. Set count with large number to select pagedown.
  `behavior` can be one of:
  - `cmp.SelectBehavior.Insert`: Inserts the text at cursor.
  - `cmp.SelectBehavior.Select`: Only selects the text, potentially adds ghost_text at
    cursor.
>lua
  cmp.setup {
    mapping = {
      ["<C-j>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }),
    }
  }
<

*cmp.select_prev_item* (option: { behavior = cmp.SelectBehavior, count = 1 })
  Select the previous item. Set count with large number to select pageup.
  `behavior` can be one of:
  - `cmp.SelectBehavior.Insert`: Inserts the text at cursor.
  - `cmp.SelectBehavior.Select`: Only selects the text, potentially adds ghost_text at
    cursor.
>lua
  cmp.setup {
    mapping = {
      ["<C-k>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }),
    }
  }
<
*cmp.open_docs* ()
  Open docs view.

*cmp.close_docs* ()
  Close docs view.

*cmp.scroll_docs* (delta: number)
  Scroll the documentation window if visible.

*cmp.complete* (option: { reason = cmp.ContextReason, config = cmp.ConfigSchema })
  Invoke completion.

  The following configuration defines a key mapping to show completion only for vsnip snippets.
>lua
  cmp.setup {
    mapping = {
      ['<C-s>'] = cmp.mapping.complete({
        config = {
          sources = {
            { name = 'vsnip' }
          }
        }
      })
    }
  }
< >vim
  inoremap <C-S> <Cmd>lua require('cmp').complete({ config = { sources = { { name = 'vsnip' } } } })<CR>
<
  NOTE: `config` in that case means a temporary setting, but `config.mapping` remains permanent.

*cmp.complete_common_string* ()
  Complete common string (similar to shell completion behavior).
>lua
  cmp.setup {
    mapping = {
      ['<C-l>'] = cmp.mapping(function(fallback)
        if cmp.visible() then
          return cmp.complete_common_string()
        end
        fallback()
      end, { 'i', 'c' }),
    }
  }
<
*cmp.confirm* (option: cmp.ConfirmOption, callback: function)
  Accepts the currently selected completion item.
  If you didn't select any item and the option table contains `select = true`,
  nvim-cmp will automatically select the first item.

  You can control how the completion item is injected into
  the file through the `behavior` option:

  `behavior=cmp.ConfirmBehavior.Insert`: inserts the selected item and
    moves adjacent text to the right (default).
  `behavior=cmp.ConfirmBehavior.Replace`: replaces adjacent text with
    the selected item.
>lua
  cmp.setup {
    mapping = {
      ["<CR>"] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }),
    }
  }
<
*cmp.event:on* (%EVENT_NAME%, callback)
  Subscribe to nvim-cmp's event. Events are listed below.

  - `complete_done`: emit after current completion is done.
  - `confirm_done`: emit after confirmation is done.
  - `menu_opened`: emit after opening a new completion menu. Called with a table holding a key
    named `window`, pointing to the completion menu implementation.
  - `menu_closed`: emit after completion menu is closed. Called with a table holding a key
    named `window`, pointing to the completion menu implementation.

==============================================================================
Mapping                                                            *cmp-mapping*

Nvim-cmp's mapping mechanism is complex but flexible and user-friendly.

You can specify a mapping function that receives a `fallback` function as an argument.
The `fallback` function can be used to call an existing mapping.

For example, typical pair-wise plugins automatically define mappings for `<CR>` and `(`.
Nvim-cmp will overwrite it if you provide a mapping. To call the existing mapping,
you would need to invoke the `fallback` function.
>lua
  cmp.setup {
    mapping = {
      ['<CR>'] = function(fallback)
        if cmp.visible() then
          cmp.confirm()
        else
          fallback() -- If you use vim-endwise, this fallback will behave the same as vim-endwise.
        end
      end
    }
  }
< >lua
  cmp.setup {
    mapping = {
      ['<Tab>'] = function(fallback)
        if cmp.visible() then
          cmp.select_next_item()
        else
          fallback()
        end
      end
    }
  }
<

It is possible to specify the modes the mapping should be active in (`i` = insert mode, `c` = command mode, `s` = select mode):
>lua
  cmp.setup {
    mapping = {
      ['<CR>'] = cmp.mapping(your_mapping_function, { 'i', 'c' })
    }
  }
<
You can also specify different mappings for different modes by passing a table:
>lua
  cmp.setup {
    mapping = {
      ['<CR>'] = cmp.mapping({
        i = your_mapping_function_a,
        c = your_mapping_function_b,
      })
    }
  }
<
There are also builtin mapping helper functions you can use:

  *cmp.mapping.close* ()
    Same as |cmp.close|.

  *cmp.mapping.abort* ()
    Same as |cmp.abort|.

  *cmp.mapping.select_next_item* (option: { behavior = cmp.SelectBehavior, count = 1 })
    Same as |cmp.select_next_item|.

  *cmp.mapping.select_prev_item* (option: { behavior = cmp.SelectBehavior, count = 1 })
    Same as |cmp.select_prev_item|.

  *cmp.mapping.open_docs* ()
    Same as |cmp.open_docs|.

  *cmp.mapping.close_docs* ()
    Same as |cmp.close_docs|.

  *cmp.mapping.scroll_docs* (delta: number)
    Same as |cmp.scroll_docs|.

  *cmp.mapping.complete* (option: cmp.CompleteParams)
    Same as |cmp.complete|.

  *cmp.mapping.complete_common_string* ()
    Same as |cmp.complete_common_string|.

  *cmp.mapping.confirm* (option: cmp.ConfirmOption)
    Same as |cmp.confirm|.

Built-in mapping helpers are only available as a configuration option.
If you want to call nvim-cmp features directly, please use |cmp-function| instead.



==============================================================================
Command                                                            *cmp-command*

*CmpStatus*
  Describes statuses and states of sources.
  Sometimes `unknown` will be printed - this is expected.
  For example, `cmp-nvim-lsp` registers itself on InsertEnter autocommand
  so the status will be shown as `unknown` when running the command.



==============================================================================
Highlight                                                        *cmp-highlight*

*CmpItemAbbr*
  Highlight group for unmatched characters of each completion field.

*CmpItemAbbrDeprecated*
  Highlight group for unmatched characters of each deprecated completion field.

*CmpItemAbbrMatch*
  Highlight group for matched characters of each completion field. Matched characters
  must form a substring of a field which share a starting position.

*CmpItemAbbrMatchFuzzy*
  Highlight group for fuzzy-matched characters of each completion field.

*CmpItemKind*
  Highlight group for the kind of the field.

  NOTE: `kind` is a symbol after each completion option.

*CmpItemKindIcon
  Highlight group for the icons used for each `lsp.CompletionItemKind`.

*CmpItemKind%KIND_NAME%*
  Highlight group for the kind of the field for a specific `lsp.CompletionItemKind`.
  If you only want to overwrite the `method` kind's highlight group, you can do this:
>vim
    highlight CmpItemKindMethod guibg=NONE guifg=Orange

*CmpItemKind%KIND_NAME%Icon
  Highlight group for the icon shown for a specific `lsp.CompletionItemKind`.
  Can be overriden the same way as shown above OR with a custom function
  in your format table (e.g.):
    
      local function set_colors(str)
        local color = vim.api.nvim_get_hl(args...)
        vim.api.nvim_set_hl(args...)
      end
<
*CmpItemMenu*
  The menu field's highlight group.

==============================================================================
FileType                                                          *cmp-filetype*

*cmp_menu*
  The completion menu buffer's filetype.

*cmp_docs*
  The documentation window buffer's filetype.

==============================================================================
Autocmd                                                            *cmp-autocmd*

You can create custom autocommands for certain nvim-cmp events by defining
autocommands for the User event with the following patterns:

*CmpReady*
  Invoked when nvim-cmp gets sourced from `plugin/cmp.lua`.

*CmpRegisterSource*
  Invoke when source was registered.

*CmpUnregisterSource*
  Invoke when source was un-registered.

==============================================================================
Config                                                              *cmp-config*

You can use the following options via `cmp.setup { ... }` .

                                                            *cmp-config.enabled*
enabled~
  `boolean | fun(): boolean`
  Toggles the plugin on and off.

                                                *cmp-config.performance.debounce*
performance.debounce~
  `number`
  Sets debounce time
  This is the interval used to group up completions from different sources
  for filtering and displaying.

                                                *cmp-config.performance.throttle*
performance.throttle~
  `number`
  Sets throttle time
  This is used to delay filtering and displaying completions.

                                        *cmp-config.performance.fetching_timeout*
performance.fetching_timeout~
    `number`
    Sets the timeout of candidate fetching process.
    The nvim-cmp will wait to display the most prioritized source.

                                *cmp-config.performance.filtering_context_budget*
performance.filtering_context_budget~
    `number`
    Sets the filtering context budget in ms.
    If filtering takes longer than this, it will be deferred.

                                 *cmp-config.performance.confirm_resolve_timeout*
performance.confirm_resolve_timeout~
    `number`
    Sets the timeout for resolving item before confirmation.

                                            *cmp-config.performance.async_budget*
performance.async_budget~
    `number`
    Maximum time (in ms) an async function is allowed to run during
    one step of the event loop.

                                        *cmp-config.performance.max_view_entries*
performance.max_view_entries~
    `number`
    Maximum number of items to show in the entries list.

                                                          *cmp-config.preselect*
preselect~
  `cmp.PreselectMode`

  1. `cmp.PreselectMode.Item`
    nvim-cmp will preselect the item that the source specified.
  2. `cmp.PreselectMode.None`
    nvim-cmp will not preselect any items.

                                                            *cmp-config.mapping*
mapping~
  `table<string, fun(fallback: function)`
  See |cmp-mapping| section.

                                                     *cmp-config.snippet.expand*
snippet.expand~
  `fun(option: cmp.SnippetExpansionParams)`
  The snippet expansion function. That's how nvim-cmp interacts with a
  particular snippet engine.

                                          *cmp-config.completion.keyword_length*
completion.keyword_length~
  `number`
  The number of characters needed to trigger auto-completion.

                                         *cmp-config.completion.keyword_pattern*
completion.keyword_pattern~
  `string`
  The default keyword pattern.

                                            *cmp-config.completion.autocomplete*
completion.autocomplete~
  `cmp.TriggerEvent[] | false`
  The event to trigger autocompletion. If set to `false`, then completion is
  only invoked manually (e.g. by calling `cmp.complete`).

                                             *cmp-config.completion.completeopt*
completion.completeopt~
  `string`
  Like vim's completeopt setting. See 'completeopt'.
  In general, you don't need to change this.

                                 *cmp-config.confirmation.get_commit_characters*
confirmation.get_commit_characters~
  `fun(commit_characters:string[]):string[]`
  You can append or exclude commitCharacters via this configuration option
  function. The commitCharacters are defined by the LSP spec.

                                    *cmp-config.formatting.expandable_indicator*
formatting.expandable_indicator~
  `cmp.expandable_indicator`
  Boolean to show the `~` expandable indicator in cmp's floating window.

                                                  *cmp-config.formatting.fields*
formatting.fields~
  `cmp.ItemField[]`
  An array of completion fields to specify their order.

                                                  *cmp-config.formatting.format*
formatting.format~
  `fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem`
  The function used to customize the appearance of the completion menu. See
  |complete-items|. This value can also be used to modify the `dup` property.
  NOTE: The `vim.CompletedItem` can contain the special properties
  `abbr_hl_group`, `icon_hl_group`, `kind_hl_group` and `menu_hl_group`.

                                   *cmp-config.matching.disallow_fuzzy_matching*
matching.disallow_fuzzy_matching~
  `boolean`
  Whether to allow fuzzy matching.

                               *cmp-config.matching.disallow_fullfuzzy_matching*
matching.disallow_fullfuzzy_matching~
  `boolean`
  Whether to allow full-fuzzy matching.

                           *cmp-config.matching.disallow_partial_fuzzy_matching*
matching.disallow_partial_fuzzy_matching~
  `boolean`
  Whether to allow fuzzy matching without prefix matching.
                                 *cmp-config.matching.disallow_partial_matching*
matching.disallow_partial_matching~
  `boolean`
  Whether to allow partial matching.

                                *cmp-config.matching.disallow_prefix_unmatching*
matching.disallow_prefix_unmatching~
  `boolean`
  Whether to allow prefix unmatching.

                                cmp-config.matching.disallow_symbol_nonprefix_matching
matching.disallow_symbol_nonprefix_matching
  `boolean`
  Whether to allow symbols in matches if the match is not a prefix match.

                                            *cmp-config.sorting.priority_weight*
sorting.priority_weight~
  `number`
  Each item's original priority (given by its corresponding source) will be
  increased by `#sources - (source_index - 1)` and multiplied by `priority_weight`.
  That is, the final priority is calculated by the following formula:
>lua
  final_score = orig_score + ((#sources - (source_index - 1)) * sorting.priority_weight)
<
                                                *cmp-config.sorting.comparators*
sorting.comparators~
  `(fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil)[]`
  The function to customize the sorting behavior.
  You can use built-in comparators via `cmp.config.compare.*`.

                                                            *cmp-config.sources*
sources~
  `cmp.SourceConfig[]`
  List of the sources and their configurations to use.
  The order of the sources determines their order in the completion results.

                                                    *cmp-config.sources[n].name*
sources[n].name~
  `string`
  The name of the source.

                                                  *cmp-config.sources[n].option*
sources[n].option~
  `table`
  Any specific options defined by the source itself.

                                          *cmp-config.sources[n].keyword_length*
sources[n].keyword_length~
  `number`
  The source-specific keyword length to trigger auto completion.

                                         *cmp-config.sources[n].keyword_pattern*
sources[n].keyword_pattern~
  `string`
  The source-specific keyword pattern.

                                      *cmp-config.sources[n].trigger_characters*
sources[n].trigger_characters~
  `string[]`
  A source-specific keyword pattern.

                                                *cmp-config.sources[n].priority*
sources[n].priority~
  `number`
  The source-specific priority value.

                                          *cmp-config.sources[n].max_item_count*
sources[n].max_item_count~
  `number`
  The source-specific maximum item count option
  Note: This is applied before sorting, so items that aren't well-matched may be selected.

                                             *cmp-config.sources[n].group_index*
sources[n].group_index~
  `number`
  The source group index.

  For instance, you can set the `buffer`'s source `group_index` to a larger number
  if you don't want to see `buffer` source items while `nvim-lsp` source is available:
>lua
    cmp.setup {
      sources = {
        { name = 'nvim_lsp', group_index = 1 },
        { name = 'buffer', group_index = 2 },
      }
    }
<
  You can also achieve this by using the built-in configuration helper like this:
>lua
    cmp.setup {
      sources = cmp.config.sources({
        { name = 'nvim_lsp' },
      }, {
        { name = 'buffer' },
      })
    }
<

                                             *cmp-config.sources[n].entry_filter*
sources[n].entry_filter~
  `function`
  A source-specific entry filter, with the following function signature:
>
  function(entry: cmp.Entry, ctx: cmp.Context): boolean
<

  Returning `true` will keep the entry, while returning `false` will remove it.

  This can be used to hide certain entries from a given source. For instance, you
  could hide all entries with kind `Text` from the `nvim_lsp` filter using the
  following source definition:
>lua
  {
    name = 'nvim_lsp',
    entry_filter = function(entry, ctx)
      return require('cmp.types').lsp.CompletionItemKind[entry:get_kind()] ~= 'Text'
    end
  }
<
  Using the `ctx` parameter, you can further customize the behaviour of the
  source.

                                                               *cmp-config.view*
view~
  `{ docs: cmp.DocsViewConfig }`
  `{ entries: cmp.EntriesViewConfig|string }`
  The view class used to customize nvim-cmp's appearance.
  Currently available configuration options are:

                                                *cmp-config.view.docs.auto_open*
view.docs.auto_open~
  `boolean`

  Specify whether to show the docs_view when selecting an item.

                                                *cmp-config.view.entries.selection_order*
view.entries.selection_order~
  `string`

  Specify whether to select the option in the pmenu that is at
  the top (`top_down`) or nearest to the cursor (`near_cursor`).
  Useful if pmenu is above cursor and you want to change default
  selection direction. Custom view only. `top_down` by default.

                                                 *cmp-config.view.entries.follow_cursor*
view.entries.follow_cursor~
  `boolean`

  Specify whether the pmenu should follow the current position of the cursor
  as the user types. Custom view only. `false` by default.

                           *cmp-config.window.{completion,documentation}.border*
window.{completion,documentation}.border~
  `string | string[] | nil`
  Border characters used for the completion popup menu when |experimental.native_menu| is disabled.
  See |nvim_open_win|.

                     *cmp-config.window.{completion,documentation}.winhighlight*
window.{completion,documentation}.winhighlight~
  `string | cmp.WinhighlightConfig`
  Specify the window's winhighlight option.
  See |nvim_open_win|.

                     *cmp-config.window.{completion,documentation}.winblend*
window.{completion,documentation}.winblend~
  `string | cmp.WinhighlightConfig`
  Specify the window's winblend option.
  See |nvim_open_win|.

                           *cmp-config.window.{completion,documentation}.zindex*
window.{completion,documentation}.zindex~
  `number`
  The completion window's zindex.
  See |nvim_open_win|.

                        *cmp-config.window.{completion,documentation}.scrolloff*
window.completion.scrolloff~
  `number`
  Specify the window's scrolloff option.
  See |'scrolloff'|.

                       *cmp-config.window.{completion,documentation}.col_offset*
window.completion.col_offset~
  `number`
  Offsets the completion window relative to the cursor.
  Offsets the documentation window relative to the completion window.

                                     *cmp-config.window.completion.side_padding*
window.completion.side_padding~
  `number`
  The amount of padding to add on the completion window's sides

                                     *cmp-config.window.completion.scrollbar*
window.completion.scrollbar~
  `boolean`
  Whether the scrollbar should be enabled if there are more items that fit

                                     *cmp-config.window.completion.max_height*
window.completion.max_height~
  `number | nil`
  The completion window's max height, can be set to 0 to use all available
  space. To use `vim.o.pumheight` set this to `nil`.
  See |'pumheight'|.

                                     *cmp-config.window.documentation.max_width*
window.documentation.max_width~
  `number`
  The documentation window's max width, can be set to 0 to use all available
  space.

                                    *cmp-config.window.documentation.max_height*
window.documentation.max_height~
  `number`
  The documentation window's max height, can be set to 0 to use all available
  space.

                                            *cmp-config.experimental.ghost_text*
experimental.ghost_text~
  `boolean | { hl_group = string }`
  Whether to enable the ghost_text feature.

==============================================================================
Config Helper                                                *cmp-config-helper*

You can use the following configuration helpers:

cmp.config.compare~

  TBD

cmp.config.context~

  The `cmp.config.context` can be used for context-aware completion toggling.
>lua
    cmp.setup {
      enabled = function()
        -- disable completion if the cursor is `Comment` syntax group.
        return not cmp.config.context.in_syntax_group('Comment')
      end
    }
<
  *cmp.config.context.in_syntax_group* (group)
    You can specify the vim's built-in syntax group.
    If you use tree-sitter, you should use `cmp.config.context.in_treesitter_capture` instead.

  *cmp.config.context.in_treesitter_capture* (capture)
    You can specify the treesitter capture name.
    If you don't use the `nvim-treesitter` plugin, this helper will not work correctly.

cmp.config.mapping~

  See |cmp-mapping|.

cmp.config.sources~

  *cmp.config.sources* (...sources)
    You can specify multiple source arrays. The sources are grouped in the
    order you specify, and the groups are displayed as a fallback, like chain
    completion.
>lua
    cmp.setup {
      sources = cmp.config.sources({
        { name = 'nvim_lsp' },
      }, {
        { name = 'buffer' },
      })
    }
<
cmp.config.window~

  *cmp.config.window.bordered* (option)
    Make the completion window `bordered`.
    The option is described in `cmp.ConfigSchema`.
>lua
    cmp.setup {
      window = {
        completion = cmp.config.window.bordered(),
        documentation = cmp.config.window.bordered(),
      }
    }
<
==============================================================================
Develop                                                            *cmp-develop*

Creating a custom source~

NOTE:
  1. The `complete` method is required. Others can be omitted.
  2. The `callback` function must always be called.
  3. You can use only `require('cmp')` in custom source.
  4. If the LSP spec was changed, nvim-cmp may implement it without any announcement (potentially introducing breaking changes).
  5. You should read ./lua/cmp/types and https://microsoft.github.io/language-server-protocol/specifications/specification-current.
  6. Please add your source to the list of sources in the Wiki (https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources)
  and if you publish it on GitHub, add the `nvim-cmp` topic so users can find it more easily.

Here is an example on how to create a custom source:
>lua
  local source = {}

  ---Return whether this source is available in the current context or not (optional).
  ---@return boolean
  function source:is_available()
    return true
  end

  ---Return the debug name of this source (optional).
  ---@return string
  function source:get_debug_name()
    return 'debug name'
  end

  ---Return LSP's PositionEncodingKind.
  ---@NOTE: If this method is omitted, the default value will be `utf-16`.
  ---@return lsp.PositionEncodingKind
  function source:get_position_encoding_kind()
    return 'utf-16'
  end

  ---Return the keyword pattern for triggering completion (optional).
  ---If this is omitted, nvim-cmp will use a default keyword pattern. See |cmp-config.completion.keyword_pattern|.
  ---@return string
  function source:get_keyword_pattern()
    return [[\k\+]]
  end

  ---Return trigger characters for triggering completion (optional).
  function source:get_trigger_characters()
    return { '.' }
  end

  ---Invoke completion (required).
  ---@param params cmp.SourceCompletionApiParams
  ---@param callback fun(response: lsp.CompletionResponse|nil)
  function source:complete(params, callback)
    callback({
      { label = 'January' },
      { label = 'February' },
      { label = 'March' },
      { label = 'April' },
      { label = 'May' },
      { label = 'June' },
      { label = 'July' },
      { label = 'August' },
      { label = 'September' },
      { label = 'October' },
      { label = 'November' },
      { label = 'December' },
    })
  end

  ---Resolve completion item (optional). This is called right before the completion is about to be displayed.
  ---Useful for setting the text shown in the documentation window (`completion_item.documentation`).
  ---@param completion_item lsp.CompletionItem
  ---@param callback fun(completion_item: lsp.CompletionItem|nil)
  function source:resolve(completion_item, callback)
    callback(completion_item)
  end

  ---Executed after the item was selected.
  ---@param completion_item lsp.CompletionItem
  ---@param callback fun(completion_item: lsp.CompletionItem|nil)
  function source:execute(completion_item, callback)
    callback(completion_item)
  end

  ---Register your source to nvim-cmp.
  require('cmp').register_source('month', source)
<
==============================================================================
FAQ                                                                    *cmp-faq*

Why does cmp automatically select a particular item? ~
How to disable the preselect feature? ~

  Nvim-cmp respects the LSP (Language Server Protocol) specification.
  The LSP spec defines the `preselect` feature for completion.

  You can disable the `preselect` feature like this:
>lua
  cmp.setup {
    preselect = cmp.PreselectMode.None
  }
<
How to disable only specific language-server's completion?~

  You can disable `completionProvider` in lspconfig configuration.
>lua
  lspconfig[%SERVER_NAME%].setup {
    on_attach = function(client)
      client.server_capabilities.completionProvider = false
    end
  }
<


How to disable commitCharacters?~

  You can disable the commitCharacters feature (which is defined in LSP spec):
>lua
  cmp.setup {
    confirmation = {
      get_commit_characters = function(commit_characters)
        return {}
      end
    }
  }
<

How to disable automatic display of docs view?~

  You can add the `view.docs.auto_open = false` for configuration.
>lua
  cmp.setup {
    ...
    view = {
      docs = {
        auto_open = false
      }
    }
    ...
  }
<

  additionally, if you want to open/close docs view via your key mapping, you
  can define keymapping as the following.
>lua
  cmp.setup {
    ...
    mapping = {
      ['<C-g>'] = function()
        if cmp.visible_docs() then
          cmp.close_docs()
        else
          cmp.open_docs()
        end
      end
    }
    ...
  }
<

How to disable auto-completion?~
How to use nvim-cmp as omnifunc?~

  You can disable auto-completion like this:
>lua
  cmp.setup {
    ...
    completion = {
      autocomplete = false
    }
    ...
  }
<
  Then you will need to invoke completion manually.
>vim
  inoremap <C-x><C-o> <Cmd>lua require('cmp').complete()<CR>
<

How to disable nvim-cmp for a specific buffer?~
How to setup nvim-cmp for a specific buffer?~

  You can setup buffer-specific configuration like this:
>lua
  cmp.setup.filetype({ 'markdown', 'help' }, {
    sources = {
      { name = 'path' },
      { name = 'buffer' },
    }
  })
<

How to disable the documentation window?~

  Simply use the following config:
>lua
  cmp.setup.filetype({ 'markdown', 'help' }, {
    window = {
      documentation = cmp.config.disable
    }
  })
<

I'm using clangd. The menu items are mis-indented.~

  It's caused by clangd. You can specify `--header-insertion-decorators` for
  clangd's command-line arguments. See #999.


How to integrate with copilot.vim?~

  Copilot.vim and nvim-cmp both have a `key-mapping fallback` mechanism.
  Therefore, you should manage those plugins by yourself.

  Fortunately, the copilot.vim has a feature that disables the fallback mechanism.
>vim
  let g:copilot_no_tab_map = v:true
  imap <expr> <Plug>(vimrc:copilot-dummy-map) copilot#Accept("\<Tab>")
<
  You can manage copilot.vim's accept feature inside nvim-cmp's key-mapping function:
>lua
  cmp.setup {
    mapping = {
      ['<C-g>'] = cmp.mapping(function(fallback)
        vim.api.nvim_feedkeys(vim.fn['copilot#Accept'](vim.api.nvim_replace_termcodes('<Tab>', true, true, true)), 'n', true)
      end)
    },
    experimental = {
      ghost_text = false -- this feature conflict with copilot.vim's preview.
    }
  }
<
nvim-cmp does not work as expected.~

  There are some known issues. Please check the following.

  - nvim-cmp does not work with `set paste` option.
  - Command line mode key mapping is unified regardless of `:`, `/`, `?`. Therefore, it is impossible to apply the mapping only to `:`.

How to customize the menu appearance?~

  Have a look at the wiki (https://github.com/hrsh7th/nvim-cmp/wiki).

==============================================================================
 vim:tw=78:ts=2:et:ft=help:norl:


================================================
FILE: init.sh
================================================
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

rm $DIR/.git/hooks/*
cp $DIR/.githooks/* $DIR/.git/hooks/
chmod 755 $DIR/.git/hooks/*




================================================
FILE: lua/cmp/config/compare.lua
================================================
local types = require('cmp.types')
local cache = require('cmp.utils.cache')

---@type cmp.Comparator[]
local compare = {}

--- Comparators (:help cmp-config.sorting.comparators) should return
--- true when the first entry should come EARLIER (i.e., higher ranking) than the second entry,
--- or nil if no pairwise ordering preference from the comparator.
--- See also :help table.sort() and cmp.view.open() to see how comparators are used.

---@class cmp.ComparatorFunctor
---@overload fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil
---@alias cmp.ComparatorFunction fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil
---@alias cmp.Comparator cmp.ComparatorFunction | cmp.ComparatorFunctor

---offset: Entries with smaller offset will be ranked higher.
---@type cmp.ComparatorFunction
compare.offset = function(entry1, entry2)
  local diff = entry1.offset - entry2.offset
  if diff < 0 then
    return true
  elseif diff > 0 then
    return false
  end
  return nil
end

---exact: Entries with exact == true will be ranked higher.
---@type cmp.ComparatorFunction
compare.exact = function(entry1, entry2)
  if entry1.exact ~= entry2.exact then
    return entry1.exact
  end
  return nil
end

---score: Entries with higher score will be ranked higher.
---@type cmp.ComparatorFunction
compare.score = function(entry1, entry2)
  local diff = entry2.score - entry1.score
  if diff < 0 then
    return true
  elseif diff > 0 then
    return false
  end
  return nil
end

---recently_used: Entries that are used recently will be ranked higher.
---@type cmp.ComparatorFunctor
compare.recently_used = setmetatable({
  records = {},
  add_entry = function(self, e)
    self.records[e.completion_item.label] = vim.loop.now()
  end,
}, {
  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
  __call = function(self, entry1, entry2)
    local t1 = self.records[entry1.completion_item.label] or -1
    local t2 = self.records[entry2.completion_item.label] or -1
    if t1 ~= t2 then
      return t1 > t2
    end
    return nil
  end,
})

---kind: Entries with smaller ordinal value of 'kind' will be ranked higher.
---(see lsp.CompletionItemKind enum).
---Exceptions are that Text(1) will be ranked the lowest, and snippets be the highest.
---@type cmp.ComparatorFunction
compare.kind = function(entry1, entry2)
  local kind1 = entry1:get_kind() --- @type lsp.CompletionItemKind | number
  local kind2 = entry2:get_kind() --- @type lsp.CompletionItemKind | number
  kind1 = kind1 == types.lsp.CompletionItemKind.Text and 100 or kind1
  kind2 = kind2 == types.lsp.CompletionItemKind.Text and 100 or kind2
  if kind1 ~= kind2 then
    if kind1 == types.lsp.CompletionItemKind.Snippet then
      return true
    end
    if kind2 == types.lsp.CompletionItemKind.Snippet then
      return false
    end
    local diff = kind1 - kind2
    if diff < 0 then
      return true
    elseif diff > 0 then
      return false
    end
  end
  return nil
end

---sort_text: Entries will be ranked according to the lexicographical order of sortText.
---@type cmp.ComparatorFunction
compare.sort_text = function(entry1, entry2)
  if entry1.completion_item.sortText and entry2.completion_item.sortText then
    local diff = vim.stricmp(entry1.completion_item.sortText, entry2.completion_item.sortText)
    if diff < 0 then
      return true
    elseif diff > 0 then
      return false
    end
  end
  return nil
end

---length: Entries with shorter label length will be ranked higher.
---@type cmp.ComparatorFunction
compare.length = function(entry1, entry2)
  local diff = #entry1.completion_item.label - #entry2.completion_item.label
  if diff < 0 then
    return true
  elseif diff > 0 then
    return false
  end
  return nil
end

----order: Entries with smaller id will be ranked higher.
---@type fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
compare.order = function(entry1, entry2)
  local diff = entry1.id - entry2.id
  if diff < 0 then
    return true
  elseif diff > 0 then
    return false
  end
  return nil
end

---locality: Entries with higher locality (i.e., words that are closer to the cursor)
---will be ranked higher. See GH-183 for more details.
---@type cmp.ComparatorFunctor
compare.locality = setmetatable({
  lines_count = 10,
  lines_cache = cache.new(),
  locality_map = {},
  update = function(self)
    local config = require('cmp').get_config()
    if not vim.tbl_contains(config.sorting.comparators, compare.locality) then
      return
    end

    local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf()
    local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1
    local max = vim.api.nvim_buf_line_count(buf)

    if self.lines_cache:get('buf') ~= buf then
      self.lines_cache:clear()
      self.lines_cache:set('buf', buf)
    end

    self.locality_map = {}
    for i = math.max(0, cursor_row - self.lines_count), math.min(max, cursor_row + self.lines_count) do
      local is_above = i < cursor_row
      local buffer = vim.api.nvim_buf_get_lines(buf, i, i + 1, false)[1] or ''
      local locality_map = self.lines_cache:ensure({ 'line', buffer }, function()
        local locality_map = {}
        local regexp = vim.regex(config.completion.keyword_pattern)
        -- the buffer length check is to avoid performance issues on very long lines, #1841
        while buffer ~= '' and #buffer < 5000 do
          local s, e = regexp:match_str(buffer)
          if s and e then
            local w = string.sub(buffer, s + 1, e)
            local d = math.abs(i - cursor_row) - (is_above and 1 or 0)
            locality_map[w] = math.min(locality_map[w] or math.huge, d)
            buffer = string.sub(buffer, e + 1)
          else
            break
          end
        end
        return locality_map
      end)
      for w, d in pairs(locality_map) do
        self.locality_map[w] = math.min(self.locality_map[w] or d, math.abs(i - cursor_row))
      end
    end
  end,
}, {
  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
  __call = function(self, entry1, entry2)
    local local1 = self.locality_map[entry1.word]
    local local2 = self.locality_map[entry2.word]
    if local1 ~= local2 then
      if local1 == nil then
        return false
      end
      if local2 == nil then
        return true
      end
      return local1 < local2
    end
    return nil
  end,
})

---scopes: Entries defined in a closer scope will be ranked higher (e.g., prefer local variables to globals).
---@type cmp.ComparatorFunctor
compare.scopes = setmetatable({
  scopes_map = {},
  update = function(self)
    local config = require('cmp').get_config()
    if not vim.tbl_contains(config.sorting.comparators, compare.scopes) then
      return
    end

    local ok, locals = pcall(require, 'nvim-treesitter.locals')
    if ok then
      local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf()
      local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1

      -- Cursor scope.
      local cursor_scope = nil
      -- Prioritize the older get_scopes method from nvim-treesitter `master` over get from `main`
      local scopes = locals.get_scopes and locals.get_scopes(buf) or select(3, locals.get(buf))
      for _, scope in ipairs(scopes) do
        if scope:start() <= cursor_row and cursor_row <= scope:end_() then
          if not cursor_scope then
            cursor_scope = scope
          else
            if cursor_scope:start() <= scope:start() and scope:end_() <= cursor_scope:end_() then
              cursor_scope = scope
            end
          end
        elseif cursor_scope and cursor_scope:end_() <= scope:start() then
          break
        end
      end

      -- Definitions.
      local definitions = locals.get_definitions_lookup_table(buf)

      -- Narrow definitions.
      local depth = 0
      for scope in locals.iter_scope_tree(cursor_scope, buf) do
        local s, e = scope:start(), scope:end_()

        -- Check scope's direct child.
        for _, definition in pairs(definitions) do
          if s <= definition.node:start() and definition.node:end_() <= e then
            if scope:id() == locals.containing_scope(definition.node, buf):id() then
              local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text
              local text = get_node_text(definition.node, buf) or ''
              if not self.scopes_map[text] then
                self.scopes_map[text] = depth
              end
            end
          end
        end
        depth = depth + 1
      end
    end
  end,
}, {
  ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil
  __call = function(self, entry1, entry2)
    local local1 = self.scopes_map[entry1.word]
    local local2 = self.scopes_map[entry2.word]
    if local1 ~= local2 then
      if local1 == nil then
        return false
      end
      if local2 == nil then
        return true
      end
      return local1 < local2
    end
  end,
})

return compare


================================================
FILE: lua/cmp/config/context.lua
================================================
local api = require('cmp.utils.api')

local context = {}

---Check if cursor is in syntax group
---@param group string | []string
---@return boolean
context.in_syntax_group = function(group)
  local row, col = unpack(vim.api.nvim_win_get_cursor(0))
  if not api.is_insert_mode() then
    col = col + 1
  end

  for _, syn_id in ipairs(vim.fn.synstack(row, col)) do
    syn_id = vim.fn.synIDtrans(syn_id) -- Resolve :highlight links
    local g = vim.fn.synIDattr(syn_id, 'name')
    if type(group) == 'string' and g == group then
      return true
    elseif type(group) == 'table' and vim.tbl_contains(group, g) then
      return true
    end
  end

  return false
end

---Check if cursor is in treesitter capture
---@param capture string | []string
---@return boolean
context.in_treesitter_capture = function(capture)
  local buf = vim.api.nvim_get_current_buf()
  local row, col = unpack(vim.api.nvim_win_get_cursor(0))
  row = row - 1
  if vim.api.nvim_get_mode().mode == 'i' then
    col = col - 1
  end

  local get_captures_at_pos = -- See neovim/neovim#20331
    require('vim.treesitter').get_captures_at_pos -- for neovim >= 0.8 or require('vim.treesitter').get_captures_at_position -- for neovim < 0.8

  local captures_at_cursor = vim.tbl_map(function(x)
    return x.capture
  end, get_captures_at_pos(buf, row, col))

  if vim.tbl_isempty(captures_at_cursor) then
    return false
  elseif type(capture) == 'string' and vim.tbl_contains(captures_at_cursor, capture) then
    return true
  elseif type(capture) == 'table' then
    for _, v in ipairs(capture) do
      if vim.tbl_contains(captures_at_cursor, v) then
        return true
      end
    end
  end

  return false
end

return context


================================================
FILE: lua/cmp/config/default.lua
================================================
local compare = require('cmp.config.compare')
local types = require('cmp.types')
local window = require('cmp.config.window')

local WIDE_HEIGHT = 40

---@return cmp.ConfigSchema
return function()
  ---@type cmp.ConfigSchema
  local config = {
    enabled = function()
      local disabled = false
      disabled = disabled or (vim.api.nvim_get_option_value('buftype', { buf = 0 }) == 'prompt')
      disabled = disabled or (vim.fn.reg_recording() ~= '')
      disabled = disabled or (vim.fn.reg_executing() ~= '')
      return not disabled
    end,

    performance = {
      debounce = 60,
      throttle = 30,
      fetching_timeout = 500,
      filtering_context_budget = 3,
      confirm_resolve_timeout = 80,
      async_budget = 1,
      max_view_entries = 200,
    },

    preselect = types.cmp.PreselectMode.Item,

    mapping = {},

    snippet = {
      expand = vim.fn.has('nvim-0.10') == 1 and function(args)
        vim.snippet.expand(args.body)
      end or function(_)
        error('snippet engine is not configured.')
      end,
    },

    completion = {
      autocomplete = {
        types.cmp.TriggerEvent.TextChanged,
      },
      completeopt = 'menu,menuone,noselect',
      keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]],
      keyword_length = 1,
    },

    formatting = {
      expandable_indicator = true,
      fields = { 'abbr', 'icon', 'kind', 'menu' },
      format = function(_, vim_item)
        return vim_item
      end,
    },

    matching = {
      disallow_fuzzy_matching = false,
      disallow_fullfuzzy_matching = false,
      disallow_partial_fuzzy_matching = true,
      disallow_partial_matching = false,
      disallow_prefix_unmatching = false,
      disallow_symbol_nonprefix_matching = true,
    },

    sorting = {
      priority_weight = 2,
      comparators = {
        compare.offset,
        compare.exact,
        -- compare.scopes,
        compare.score,
        compare.recently_used,
        compare.locality,
        compare.kind,
        compare.sort_text,
        compare.length,
        compare.order,
      },
    },

    sources = {},

    confirmation = {
      default_behavior = types.cmp.ConfirmBehavior.Insert,
      get_commit_characters = function(commit_characters)
        return commit_characters
      end,
    },

    event = {},

    experimental = {
      ghost_text = false,
    },

    view = {
      entries = {
        name = 'custom',
        selection_order = 'top_down',
        vertical_positioning = 'below',
        follow_cursor = false,
      },
      docs = {
        auto_open = true,
      },
    },

    window = {
      completion = {
        border = window.get_border(),
        winhighlight = 'Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None',
        winblend = vim.o.pumblend,
        scrolloff = 0,
        col_offset = 0,
        side_padding = 1,
        scrollbar = true,
      },
      documentation = {
        max_height = math.floor(WIDE_HEIGHT * (WIDE_HEIGHT / vim.o.lines)),
        max_width = math.floor((WIDE_HEIGHT * 2) * (vim.o.columns / (WIDE_HEIGHT * 2 * 16 / 9))),
        border = window.get_border(),
        winhighlight = 'FloatBorder:NormalFloat',
        winblend = vim.o.pumblend,
        col_offset = 0,
      },
    },
  }
  return config
end


================================================
FILE: lua/cmp/config/mapping.lua
================================================
local types = require('cmp.types')
local misc = require('cmp.utils.misc')
local keymap = require('cmp.utils.keymap')

local function merge_keymaps(base, override)
  local normalized_base = {}
  for k, v in pairs(base) do
    normalized_base[keymap.normalize(k)] = v
  end

  local normalized_override = {}
  for k, v in pairs(override) do
    normalized_override[keymap.normalize(k)] = v
  end

  return misc.merge(normalized_base, normalized_override)
end

local mapping = setmetatable({}, {
  __call = function(_, invoke, modes)
    if type(invoke) == 'function' then
      local map = {}
      for _, mode in ipairs(modes or { 'i' }) do
        map[mode] = invoke
      end
      return map
    end
    return invoke
  end,
})

---Mapping preset configuration.
mapping.preset = {}

---Mapping preset insert-mode configuration.
mapping.preset.insert = function(override)
  return merge_keymaps(override or {}, {
    ['<Down>'] = {
      i = mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Select }),
    },
    ['<Up>'] = {
      i = mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Select }),
    },
    ['<C-n>'] = {
      i = function()
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_next_item({ behavior = types.cmp.SelectBehavior.Insert })
        else
          cmp.complete()
        end
      end,
    },
    ['<C-p>'] = {
      i = function()
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_prev_item({ behavior = types.cmp.SelectBehavior.Insert })
        else
          cmp.complete()
        end
      end,
    },
    ['<C-y>'] = {
      i = mapping.confirm({ select = false }),
    },
    ['<C-e>'] = {
      i = mapping.abort(),
    },
  })
end

---Mapping preset cmdline-mode configuration.
mapping.preset.cmdline = function(override)
  return merge_keymaps(override or {}, {
    ['<C-z>'] = {
      c = function()
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_next_item()
        else
          cmp.complete()
        end
      end,
    },
    ['<Tab>'] = {
      c = function()
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_next_item()
        else
          cmp.complete()
        end
      end,
    },
    ['<S-Tab>'] = {
      c = function()
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_prev_item()
        else
          cmp.complete()
        end
      end,
    },
    ['<C-n>'] = {
      c = function(fallback)
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_next_item()
        else
          fallback()
        end
      end,
    },
    ['<C-p>'] = {
      c = function(fallback)
        local cmp = require('cmp')
        if cmp.visible() then
          cmp.select_prev_item()
        else
          fallback()
        end
      end,
    },
    ['<C-e>'] = {
      c = mapping.abort(),
    },
    ['<C-y>'] = {
      c = mapping.confirm({ select = false }),
    },
  })
end

---Invoke completion
---@param option? cmp.CompleteParams
mapping.complete = function(option)
  return function(fallback)
    if not require('cmp').complete(option) then
      fallback()
    end
  end
end

---Complete common string.
mapping.complete_common_string = function()
  return function(fallback)
    if not require('cmp').complete_common_string() then
      fallback()
    end
  end
end

---Close current completion menu if it displayed.
mapping.close = function()
  return function(fallback)
    if not require('cmp').close() then
      fallback()
    end
  end
end

---Abort current completion menu if it displayed.
mapping.abort = function()
  return function(fallback)
    if not require('cmp').abort() then
      fallback()
    end
  end
end

---Scroll documentation window.
mapping.scroll_docs = function(delta)
  return function(fallback)
    if not require('cmp').scroll_docs(delta) then
      fallback()
    end
  end
end

--- Opens the documentation window.
mapping.open_docs = function()
  return function(fallback)
    if not require('cmp').open_docs() then
      fallback()
    end
  end
end

--- Close the documentation window.
mapping.close_docs = function()
  return function(fallback)
    if not require('cmp').close_docs() then
      fallback()
    end
  end
end

---Select next completion item.
mapping.select_next_item = function(option)
  return function(fallback)
    if not require('cmp').select_next_item(option) then
      local release = require('cmp').core:suspend()
      fallback()
      vim.schedule(release)
    end
  end
end

---Select prev completion item.
mapping.select_prev_item = function(option)
  return function(fallback)
    if not require('cmp').select_prev_item(option) then
      local release = require('cmp').core:suspend()
      fallback()
      vim.schedule(release)
    end
  end
end

---Confirm selection
mapping.confirm = function(option)
  return function(fallback)
    if not require('cmp').confirm(option) then
      fallback()
    end
  end
end

return mapping


================================================
FILE: lua/cmp/config/sources.lua
================================================
return function(...)
  local sources = {}
  for i, group in ipairs({ ... }) do
    for _, source in ipairs(group) do
      source.group_index = i
      table.insert(sources, source)
    end
  end
  return sources
end


================================================
FILE: lua/cmp/config/window.lua
================================================
local window = {}

window.bordered = function(opts)
  opts = opts or {}
  return {
    border = opts.border or window.get_border(),
    winhighlight = opts.winhighlight or 'Normal:Normal,FloatBorder:FloatBorder,CursorLine:Visual,Search:None',
    zindex = opts.zindex or 1001,
    scrolloff = opts.scrolloff or 0,
    col_offset = opts.col_offset or 0,
    side_padding = opts.side_padding or 1,
    scrollbar = opts.scrollbar == nil or opts.scrollbar,
    max_height = opts.max_height or nil,
  }
end

window.get_border = function()
  -- On neovim 0.11+, use the vim.o.winborder option by default
  local has_winborder, winborder = pcall(function()
    return vim.o.winborder
  end)
  if has_winborder and winborder ~= '' then
    return winborder
  end

  -- On lower versions return the default
  return 'none'
end

return window


================================================
FILE: lua/cmp/config.lua
================================================
local mapping = require('cmp.config.mapping')
local cache = require('cmp.utils.cache')
local keymap = require('cmp.utils.keymap')
local misc = require('cmp.utils.misc')
local api = require('cmp.utils.api')

---@class cmp.Config
---@field public g cmp.ConfigSchema
local config = {}

---@type cmp.Cache
config.cache = cache.new()

---@type cmp.ConfigSchema
config.global = require('cmp.config.default')()

---@type table<integer, cmp.ConfigSchema>
config.buffers = {}

---@type table<string, cmp.ConfigSchema>
config.filetypes = {}

---@type table<string, cmp.ConfigSchema>
config.cmdline = {}

---@type cmp.ConfigSchema
config.onetime = {}

---Set configuration for global.
---@param c cmp.ConfigSchema
config.set_global = function(c)
  config.global = config.normalize(misc.merge(c, config.global))
  config.global.revision = config.global.revision or 1
  config.global.revision = config.global.revision + 1
end

---Set configuration for buffer
---@param c cmp.ConfigSchema
---@param bufnr integer
config.set_buffer = function(c, bufnr)
  local revision = (config.buffers[bufnr] or {}).revision or 1
  config.buffers[bufnr] = c or {}
  config.buffers[bufnr].revision = revision + 1
end

---Set configuration for filetype
---@param c cmp.ConfigSchema
---@param filetypes string[]|string
config.set_filetype = function(c, filetypes)
  for _, filetype in ipairs(type(filetypes) == 'table' and filetypes or { filetypes }) do
    local revision = (config.filetypes[filetype] or {}).revision or 1
    config.filetypes[filetype] = c or {}
    config.filetypes[filetype].revision = revision + 1
  end
end

---Set configuration for cmdline
---@param c cmp.ConfigSchema
---@param cmdtypes string|string[]
config.set_cmdline = function(c, cmdtypes)
  for _, cmdtype in ipairs(type(cmdtypes) == 'table' and cmdtypes or { cmdtypes }) do
    local revision = (config.cmdline[cmdtype] or {}).revision or 1
    config.cmdline[cmdtype] = c or {}
    config.cmdline[cmdtype].revision = revision + 1
  end
end

---Set configuration as oneshot completion.
---@param c cmp.ConfigSchema
config.set_onetime = function(c)
  local revision = (config.onetime or {}).revision or 1
  config.onetime = c or {}
  config.onetime.revision = revision + 1
end

---@return cmp.ConfigSchema
config.get = function()
  local global_config = config.global

  -- The config object already has `revision` key.
  if #vim.tbl_keys(config.onetime) > 1 then
    local onetime_config = config.onetime
    return config.cache:ensure({
      'get',
      'onetime',
      global_config.revision or 0,
      onetime_config.revision or 0,
    }, function()
      local c = {}
      c = misc.merge(c, config.normalize(onetime_config))
      c = misc.merge(c, config.normalize(global_config))
      return c
    end)
  elseif api.is_cmdline_mode() then
    local cmdtype = vim.fn.getcmdtype()
    local cmdline_config = config.cmdline[cmdtype] or { revision = 1, sources = {} }
    return config.cache:ensure({
      'get',
      'cmdline',
      global_config.revision or 0,
      cmdtype,
      cmdline_config.revision or 0,
    }, function()
      local c = {}
      c = misc.merge(c, config.normalize(cmdline_config))
      c = misc.merge(c, config.normalize(global_config))
      return c
    end)
  else
    local bufnr = vim.api.nvim_get_current_buf()
    local filetype = vim.api.nvim_get_option_value('filetype', { buf = bufnr })
    local buffer_config = config.buffers[bufnr] or { revision = 1 }
    local filetype_config = config.filetypes[filetype] or { revision = 1 }
    return config.cache:ensure({
      'get',
      'default',
      global_config.revision or 0,
      filetype,
      filetype_config.revision or 0,
      bufnr,
      buffer_config.revision or 0,
    }, function()
      local c = {}
      c = misc.merge(config.normalize(c), config.normalize(buffer_config))
      c = misc.merge(config.normalize(c), config.normalize(filetype_config))
      c = misc.merge(config.normalize(c), config.normalize(global_config))
      return c
    end)
  end
end

---Return cmp is enabled or not.
config.enabled = function()
  local enabled = config.get().enabled
  if type(enabled) == 'function' then
    enabled = enabled()
  end
  return enabled and api.is_suitable_mode()
end

---Return source config
---@param name string
---@return cmp.SourceConfig
config.get_source_config = function(name)
  local c = config.get()
  for _, s in ipairs(c.sources) do
    if s.name == name then
      return s
    end
  end
  return nil
end

---Return the current menu is native or not.
config.is_native_menu = function()
  local c = config.get()
  if c.view and c.view.entries then
    return c.view.entries == 'native' or c.view.entries.name == 'native'
  end
  return false
end

---Normalize mapping key
---@param c any
---@return cmp.ConfigSchema
config.normalize = function(c)
  -- make sure c is not 'nil'
  ---@type any
  c = c == nil and {} or c

  -- Normalize mapping.
  if c.mapping then
    local normalized = {}
    for k, v in pairs(c.mapping) do
      normalized[keymap.normalize(k)] = mapping(v, { 'i' })
    end
    c.mapping = normalized
  end

  -- Notice experimental.native_menu.
  if c.experimental and c.experimental.native_menu then
    vim.api.nvim_echo({
      { '[nvim-cmp] ', 'Normal' },
      { 'experimental.native_menu', 'WarningMsg' },
      { ' is deprecated.\n', 'Normal' },
      { '[nvim-cmp] Please use ', 'Normal' },
      { 'view.entries = "native"', 'WarningMsg' },
      { ' instead.', 'Normal' },
    }, true, {})

    c.view = c.view or {}
    c.view.entries = c.view.entries or 'native'
  end

  -- Notice documentation.
  if c.documentation ~= nil then
    vim.api.nvim_echo({
      { '[nvim-cmp] ', 'Normal' },
      { 'documentation', 'WarningMsg' },
      { ' is deprecated.\n', 'Normal' },
      { '[nvim-cmp] Please use ', 'Normal' },
      { 'window.documentation = cmp.config.window.bordered()', 'WarningMsg' },
      { ' instead.', 'Normal' },
    }, true, {})
    c.window = c.window or {}
    c.window.documentation = c.documentation
  end

  -- Notice sources.[n].opts
  if c.sources then
    for _, s in ipairs(c.sources) do
      if s.opts and not s.option then
        s.option = s.opts
        s.opts = nil
        vim.api.nvim_echo({
          { '[nvim-cmp] ', 'Normal' },
          { 'sources[number].opts', 'WarningMsg' },
          { ' is deprecated.\n', 'Normal' },
          { '[nvim-cmp] Please use ', 'Normal' },
          { 'sources[number].option', 'WarningMsg' },
          { ' instead.', 'Normal' },
        }, true, {})
      end
      s.option = s.option or {}
    end
  end

  return c
end

return config


================================================
FILE: lua/cmp/context.lua
================================================
local misc = require('cmp.utils.misc')
local pattern = require('cmp.utils.pattern')
local types = require('cmp.types')
local cache = require('cmp.utils.cache')
local api = require('cmp.utils.api')

---@class cmp.Context
---@field public id string
---@field public cache cmp.Cache
---@field public prev_context cmp.Context
---@field public option cmp.ContextOption
---@field public filetype string
---@field public time integer
---@field public bufnr integer
---@field public cursor vim.Position|lsp.Position
---@field public cursor_line string
---@field public cursor_after_line string
---@field public cursor_before_line string
---@field public aborted boolean
local context = {}

---Create new empty context
---@return cmp.Context
context.empty = function()
  local ctx = context.new({}) -- dirty hack to prevent recursive call `context.empty`.
  ctx.bufnr = -1
  ctx.input = ''
  ctx.cursor = {}
  ctx.cursor.row = -1
  ctx.cursor.col = -1
  return ctx
end

---Create new context
---@param prev_context? cmp.Context
---@param option? cmp.ContextOption
---@return cmp.Context
context.new = function(prev_context, option)
  option = option or {}

  local self = setmetatable({}, { __index = context })
  self.id = misc.id('cmp.context.new')
  self.cache = cache.new()
  self.prev_context = prev_context or context.empty()
  self.option = option or { reason = types.cmp.ContextReason.None }
  self.filetype = vim.api.nvim_get_option_value('filetype', { buf = 0 })
  self.time = vim.loop.now()
  self.bufnr = vim.api.nvim_get_current_buf()

  local cursor = api.get_cursor()
  self.cursor_line = api.get_current_line()
  self.cursor = {}
  self.cursor.row = cursor[1]
  self.cursor.col = cursor[2] + 1
  self.cursor.line = self.cursor.row - 1
  self.cursor.character = misc.to_utfindex(self.cursor_line, self.cursor.col)
  self.cursor_before_line = string.sub(self.cursor_line, 1, self.cursor.col - 1)
  self.cursor_after_line = string.sub(self.cursor_line, self.cursor.col)
  self.aborted = false
  return self
end

context.abort = function(self)
  self.aborted = true
end

---Return context creation reason.
---@return cmp.ContextReason
context.get_reason = function(self)
  return self.option.reason
end

---Get keyword pattern offset
---@return integer
context.get_offset = function(self, keyword_pattern)
  return self.cache:ensure({ 'get_offset', keyword_pattern, self.cursor_before_line }, function()
    return pattern.offset([[\%(]] .. keyword_pattern .. [[\)\m$]], self.cursor_before_line) or self.cursor.col
  end)
end

---Return if this context is changed from previous context or not.
---@return boolean
context.changed = function(self, ctx)
  local curr = self

  if curr.bufnr ~= ctx.bufnr then
    return true
  end
  if curr.cursor.row ~= ctx.cursor.row then
    return true
  end
  if curr.cursor.col ~= ctx.cursor.col then
    return true
  end
  if curr:get_reason() == types.cmp.ContextReason.Manual then
    return true
  end

  return false
end

---Shallow clone
context.clone = function(self)
  local cloned = {}
  for k, v in pairs(self) do
    cloned[k] = v
  end
  return cloned
end

return context


================================================
FILE: lua/cmp/context_spec.lua
================================================
local spec = require('cmp.utils.spec')

local context = require('cmp.context')

describe('context', function()
  before_each(spec.before)

  describe('new', function()
    it('middle of text', function()
      vim.fn.setline('1', 'function! s:name() abort')
      vim.bo.filetype = 'vim'
      vim.fn.execute('normal! fm')
      local ctx = context.new()
      assert.are.equal(ctx.filetype, 'vim')
      assert.are.equal(ctx.cursor.row, 1)
      assert.are.equal(ctx.cursor.col, 15)
      assert.are.equal(ctx.cursor_line, 'function! s:name() abort')
    end)

    it('tab indent', function()
      vim.fn.setline('1', '\t\tab')
      vim.bo.filetype = 'vim'
      vim.fn.execute('normal! fb')
      local ctx = context.new()
      assert.are.equal(ctx.filetype, 'vim')
      assert.are.equal(ctx.cursor.row, 1)
      assert.are.equal(ctx.cursor.col, 4)
      assert.are.equal(ctx.cursor_line, '\t\tab')
    end)
  end)
end)


================================================
FILE: lua/cmp/core.lua
================================================
local debug = require('cmp.utils.debug')
local str = require('cmp.utils.str')
local char = require('cmp.utils.char')
local feedkeys = require('cmp.utils.feedkeys')
local async = require('cmp.utils.async')
local keymap = require('cmp.utils.keymap')
local context = require('cmp.context')
local source = require('cmp.source')
local view = require('cmp.view')
local misc = require('cmp.utils.misc')
local config = require('cmp.config')
local types = require('cmp.types')
local api = require('cmp.utils.api')
local event = require('cmp.utils.event')

---@class cmp.Core
---@field public suspending boolean
---@field public view cmp.View
---@field public sources cmp.Source[]
---@field public context cmp.Context
---@field public event cmp.Event
local core = {}

core.new = function()
  local self = setmetatable({}, { __index = core })
  self.suspending = false
  self.sources = {}
  self.context = context.new()
  self.event = event.new()
  self.view = view.new()
  self.view.event:on('keymap', function(...)
    self:on_keymap(...)
  end)
  for _, event_name in ipairs({ 'complete_done', 'menu_opened', 'menu_closed' }) do
    self.view.event:on(event_name, function(evt)
      self.event:emit(event_name, evt)
    end)
  end
  return self
end

---Register source
---@param s cmp.Source
core.register_source = function(self, s)
  self.sources[s.id] = s
end

---Unregister source
---@param source_id integer
---@return cmp.Source?
core.unregister_source = function(self, source_id)
  local s = self.sources[source_id]
  self.sources[source_id] = nil
  return s
end

---Get new context
---@param option? cmp.ContextOption
---@return cmp.Context
core.get_context = function(self, option)
  self.context:abort()
  local prev = self.context:clone()
  prev.prev_context = nil
  prev.cache = nil
  local ctx = context.new(prev, option)
  self:set_context(ctx)
  return self.context
end

---Set new context
---@param ctx cmp.Context
core.set_context = function(self, ctx)
  self.context = ctx
end

---Suspend completion
core.suspend = function(self)
  self.suspending = true
  -- It's needed to avoid conflicting with autocmd debouncing.
  return vim.schedule_wrap(function()
    self.suspending = false
  end)
end

---Get sources that sorted by priority
---@param filter? cmp.SourceStatus[]|fun(s: cmp.Source): boolean
---@return cmp.Source[]
core.get_sources = function(self, filter)
  local f = function(s)
    if type(filter) == 'table' then
      return vim.tbl_contains(filter, s.status)
    elseif type(filter) == 'function' then
      return filter(s)
    end
    return true
  end

  local sources = {}
  for _, c in pairs(config.get().sources) do
    for _, s in pairs(self.sources) do
      if c.name == s.name then
        if s:is_available() and f(s) then
          table.insert(sources, s)
        end
      end
    end
  end
  return sources
end

---Return registered sources.
---@return cmp.Source[]
core.get_registered_sources = function(self)
  return self.sources
end

---Keypress handler
core.on_keymap = function(self, keys, fallback)
  local mode = api.get_mode()
  for key, mapping in pairs(config.get().mapping) do
    if keymap.equals(key, keys) and mapping[mode] then
      return mapping[mode](fallback)
    end
  end

  --Commit character. NOTE: This has a lot of cmp specific implementation to make more user-friendly.
  local chars = keymap.t(keys)
  local e = self.view:get_active_entry()
  if e and vim.tbl_contains(config.get().confirmation.get_commit_characters(e:get_commit_characters()), chars) then
    local is_printable = char.is_printable(string.byte(chars, 1))
    self:confirm(e, {
      behavior = is_printable and 'insert' or 'replace',
      commit_character = chars,
    }, function()
      local ctx = self:get_context()
      local word = e.word
      if string.sub(ctx.cursor_before_line, -#word, ctx.cursor.col - 1) == word and is_printable then
        fallback()
      else
        self:reset()
      end
    end)
    return
  end

  fallback()
end

---Prepare completion
core.prepare = function(self)
  for keys, mapping in pairs(config.get().mapping) do
    for mode in pairs(mapping) do
      keymap.listen(mode, keys, function(...)
        self:on_keymap(...)
      end)
    end
  end
end

---Check auto-completion
core.on_change = function(self, trigger_event)
  local ignore = false
  ignore = ignore or self.suspending
  ignore = ignore or (vim.fn.pumvisible() == 1 and (vim.v.completed_item).word)
  ignore = ignore or not self.view:ready()
  if ignore then
    self:get_context({ reason = types.cmp.ContextReason.Auto })
    return
  end
  self:autoindent(trigger_event, function()
    local ctx = self:get_context({ reason = types.cmp.ContextReason.Auto })
    debug.log(('ctx: `%s`'):format(ctx.cursor_before_line))
    if ctx:changed(ctx.prev_context) then
      self.view:on_change()
      debug.log('changed')

      if vim.tbl_contains(config.get().completion.autocomplete or {}, trigger_event) then
        self:complete(ctx)
      else
        self.filter.timeout = self.view:visible() and config.get().performance.throttle or 0
        self:filter()
      end
    else
      debug.log('unchanged')
    end
  end)
end

---Cursor moved.
core.on_moved = function(self)
  local ignore = false
  ignore = ignore or self.suspending
  ignore = ignore or (vim.fn.pumvisible() == 1 and (vim.v.completed_item).word)
  ignore = ignore or not self.view:visible()
  if ignore then
    return
  end
  self:filter()
end

---Returns the suffix of the specified `line`.
---
---Contains `%s`: returns everything after the last `%s` in `line`
---Else:          returns `line` unmodified
---@param line string
---@return string suffix
local function find_line_suffix(line)
  return line:match('%S*$') --[[@as string]]
end

---Check autoindent
---@param trigger_event cmp.TriggerEvent
---@param callback function
core.autoindent = function(self, trigger_event, callback)
  if trigger_event ~= types.cmp.TriggerEvent.TextChanged then
    return callback()
  end
  if not api.is_insert_mode() then
    return callback()
  end

  -- Check prefix
  local cursor_before_line = api.get_cursor_before_line()
  local prefix = find_line_suffix(cursor_before_line) or ''
  if #prefix == 0 then
    return callback()
  end

  -- Reset current completion if indentkeys matched.
  for _, key in ipairs(vim.split(vim.bo.indentkeys, ',')) do
    if vim.tbl_contains({ '=' .. prefix, '0=' .. prefix }, key) then
      self:reset()
      self:set_context(context.empty())
      break
    end
  end

  callback()
end

---Complete common string for current completed entries.
core.complete_common_string = function(self)
  if not self.view:visible() or self.view:get_selected_entry() then
    return false
  end

  config.set_onetime({
    sources = config.get().sources,
    matching = {
      disallow_prefix_unmatching = true,
      disallow_partial_matching = true,
      disallow_fuzzy_matching = true,
    },
  })

  self:filter()
  self.filter:sync(1000)

  config.set_onetime({})

  local cursor = api.get_cursor()
  local offset = self.view:get_offset() or cursor[2]
  local common_string
  for _, e in ipairs(self.view:get_entries()) do
    local vim_item = e:get_vim_item(offset)
    if not common_string then
      common_string = vim_item.word
    else
      common_string = str.get_common_string(common_string, vim_item.word)
    end
  end
  local cursor_before_line = api.get_cursor_before_line()
  local pretext = cursor_before_line:sub(offset)
  if common_string and #common_string > #pretext then
    feedkeys.call(keymap.backspace(pretext) .. common_string, 'n')
    return true
  end
  return false
end

---Invoke completion
---@param ctx cmp.Context
core.complete = function(self, ctx)
  if not api.is_suitable_mode() then
    return
  end

  self:set_context(ctx)

  -- Invoke completion sources.
  local sources = self:get_sources()
  for _, s in ipairs(sources) do
    local callback
    callback = (function(s_)
      return function()
        local new = context.new(ctx)
        if s_.incomplete and new:changed(s_.context) then
          s_:complete(new, callback)
        else
          if not self.view:get_active_entry() then
            self.filter.stop()
            self.filter.timeout = config.get().performance.debounce
            self:filter()
          end
        end
      end
    end)(s)
    s:complete(ctx, callback)
  end

  if not self.view:get_active_entry() then
    self.filter.timeout = self.view:visible() and config.get().performance.throttle or 1
    self:filter()
  end
end

---Update completion menu
local async_filter = async.wrap(function(self)
  self.filter.timeout = config.get().performance.throttle

  -- Check invalid condition.
  local ignore = false
  ignore = ignore or not api.is_suitable_mode()
  if ignore then
    return
  end

  -- Check fetching sources.
  local sources = {}
  for _, s in ipairs(self:get_sources({ source.SourceStatus.FETCHING, source.SourceStatus.COMPLETED })) do
    -- Reserve filter call for timeout.
    if not s.incomplete and config.get().performance.fetching_timeout > s:get_fetching_time() then
      self.filter.timeout = config.get().performance.fetching_timeout - s:get_fetching_time()
      self:filter()
      if #sources == 0 then
        return
      end
    end
    table.insert(sources, s)
  end

  local ctx = self:get_context()

  -- Display completion results.
  local did_open = self.view:open(ctx, sources)
  local fetching = #self:get_sources(function(s)
    return s.status == source.SourceStatus.FETCHING
  end)

  -- Check onetime config.
  if not did_open and fetching == 0 then
    config.set_onetime({})
  end
end)
core.filter = async.throttle(async_filter, config.get().performance.throttle)

---Confirm completion.
---@param e cmp.Entry
---@param option cmp.ConfirmOption
---@param callback function
core.confirm = function(self, e, option, callback)
  if not (e and not e.confirmed) then
    if callback then
      callback()
    end
    return
  end
  e.confirmed = true

  debug.log('entry.confirm', e.completion_item)

  async.sync(function(done)
    e:resolve(done)
  end, config.get().performance.confirm_resolve_timeout)

  local release = self:suspend()

  -- Close menus.
  self.view:close()

  feedkeys.call(keymap.indentkeys(), 'n')
  feedkeys.call('', 'n', function()
    -- Emulate `<C-y>` behavior to save `.` register.
    local ctx = context.new()
    local keys = {}
    table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))
    table.insert(keys, e.word)
    table.insert(keys, keymap.undobreak())
    feedkeys.call(table.concat(keys, ''), 'in')
  end)
  feedkeys.call('', 'n', function()
    -- Restore the line at the time of request.
    local ctx = context.new()
    if api.is_cmdline_mode() then
      local keys = {}
      table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))
      table.insert(keys, string.sub(e.context.cursor_before_line, e.offset))
      feedkeys.call(table.concat(keys, ''), 'in')
    else
      vim.cmd([[silent! undojoin]])
      -- This logic must be used nvim_buf_set_text.
      -- If not used, the snippet engine's placeholder will be broken.
      vim.api.nvim_buf_set_text(0, e.context.cursor.row - 1, e.offset - 1, ctx.cursor.row - 1, ctx.cursor.col - 1, {
        e.context.cursor_before_line:sub(e.offset),
      })
      vim.api.nvim_win_set_cursor(0, { e.context.cursor.row, e.context.cursor.col - 1 })
    end
  end)
  feedkeys.call('', 'n', function()
    -- Apply additionalTextEdits.
    local ctx = context.new()
    if #(e.completion_item.additionalTextEdits or {}) == 0 then
      e:resolve(function()
        local new = context.new()
        local text_edits = e.completion_item.additionalTextEdits or {}
        if #text_edits == 0 then
          return
        end

        local has_cursor_line_text_edit = (function()
          local minrow = math.min(ctx.cursor.row, new.cursor.row)
          local maxrow = math.max(ctx.cursor.row, new.cursor.row)
          for _, te in ipairs(text_edits) do
            local srow = te.range.start.line + 1
            local erow = te.range['end'].line + 1
            if srow <= minrow and maxrow <= erow then
              return true
            end
          end
          return false
        end)()
        if has_cursor_line_text_edit then
          return
        end
        vim.cmd([[silent! undojoin]])
        api.apply_text_edits(text_edits, ctx.bufnr, e.source:get_position_encoding_kind())
      end)
    else
      vim.cmd([[silent! undojoin]])
      api.apply_text_edits(e.completion_item.additionalTextEdits, ctx.bufnr, e.source:get_position_encoding_kind())
    end
  end)
  feedkeys.call('', 'n', function()
    local ctx = context.new()
    local completion_item = misc.copy(e.completion_item)
    if not completion_item.textEdit then
      completion_item.textEdit = {}
      local insertText = completion_item.insertText
      if misc.empty(insertText) then
        insertText = nil
      end
      completion_item.textEdit.newText = insertText or completion_item.word or completion_item.label
    end
    local behavior = option.behavior or config.get().confirmation.default_behavior
    if behavior == types.cmp.ConfirmBehavior.Replace then
      completion_item.textEdit.range = e.replace_range
    else
      completion_item.textEdit.range = e.insert_range
    end

    local diff_before = math.max(0, e.context.cursor.col - (completion_item.textEdit.range.start.character + 1))
    local diff_after = math.max(0, (completion_item.textEdit.range['end'].character + 1) - e.context.cursor.col)
    local new_text = completion_item.textEdit.newText
    completion_item.textEdit.range.start.line = ctx.cursor.line
    completion_item.textEdit.range.start.character = (ctx.cursor.col - 1) - diff_before
    completion_item.textEdit.range['end'].line = ctx.cursor.line
    completion_item.textEdit.range['end'].character = (ctx.cursor.col - 1) + diff_after
    if api.is_insert_mode() then
      if false then
        --To use complex expansion debug.
        vim.print({ -- luacheck: ignore
          item = e.completion_item,
          diff_before = diff_before,
          diff_after = diff_after,
          new_text = new_text,
          text_edit_new_text = completion_item.textEdit.newText,
          range_start = completion_item.textEdit.range.start.character,
          range_end = completion_item.textEdit.range['end'].character,
          original_range_start = e.completion_item.textEdit.range.start.character,
          original_range_end = e.completion_item.textEdit.range['end'].character,
          cursor_line = ctx.cursor_line,
          cursor_col0 = ctx.cursor.col - 1,
        })
      end
      local is_snippet = completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet
      if is_snippet then
        completion_item.textEdit.newText = ''
      end
      api.apply_text_edits({ completion_item.textEdit }, ctx.bufnr, 'utf-8')

      local texts = vim.split(completion_item.textEdit.newText, '\n')
      vim.api.nvim_win_set_cursor(0, {
        completion_item.textEdit.range.start.line + #texts,
        (#texts == 1 and (completion_item.textEdit.range.start.character + #texts[1]) or #texts[#texts]),
      })
      if is_snippet then
        config.get().snippet.expand({
          body = new_text,
          insert_text_mode = completion_item.insertTextMode,
        })
      end
    else
      local keys = {}
      table.insert(keys, keymap.backspace(ctx.cursor_line:sub(completion_item.textEdit.range.start.character + 1, ctx.cursor.col - 1)))
      table.insert(keys, keymap.delete(ctx.cursor_line:sub(ctx.cursor.col, completion_item.textEdit.range['end'].character)))
      table.insert(keys, new_text)
      feedkeys.call(table.concat(keys, ''), 'in')
    end
  end)
  feedkeys.call(keymap.indentkeys(vim.bo.indentkeys), 'n')
  feedkeys.call('', 'n', function()
    e:execute(vim.schedule_wrap(function()
      release()
      self.event:emit('confirm_done', {
        entry = e,
        commit_character = option.commit_character,
      })
      if callback then
        callback()
      end
    end))
  end)
end

---Reset current completion state
core.reset = function(self)
  for _, s in pairs(self.sources) do
    s:reset()
  end
  self.context = context.empty()
end

return core


================================================
FILE: lua/cmp/core_spec.lua
================================================
local spec = require('cmp.utils.spec')
local feedkeys = require('cmp.utils.feedkeys')
local types = require('cmp.types')
local core = require('cmp.core')
local source = require('cmp.source')
local keymap = require('cmp.utils.keymap')
local api = require('cmp.utils.api')

describe('cmp.core', function()
  describe('confirm', function()
    ---@param request string
    ---@param filter string
    ---@param completion_item lsp.CompletionItem
    ---@param option? { position_encoding_kind: lsp.PositionEncodingKind }
    ---@return table
    local confirm = function(request, filter, completion_item, option)
      option = option or {}

      local c = core.new()
      local s = source.new('spec', {
        get_position_encoding_kind = function()
          return option.position_encoding_kind or types.lsp.PositionEncodingKind.UTF16
        end,
        complete = function(_, _, callback)
          callback({ completion_item })
        end,
      })
      c:register_source(s)
      feedkeys.call(request, 'n', function()
        c:complete(c:get_context({ reason = types.cmp.ContextReason.Manual }))
        vim.wait(5000, function()
          return #c.sources[s.id].entries > 0
        end)
      end)
      feedkeys.call(filter, 'n', function()
        c:confirm(c.sources[s.id].entries[1], {}, function() end)
      end)
      local state = {}
      feedkeys.call('', 'x', function()
        feedkeys.call('', 'n', function()
          if api.is_cmdline_mode() then
            state.buffer = { api.get_current_line() }
          else
            state.buffer = vim.api.nvim_buf_get_lines(0, 0, -1, false)
          end
          state.cursor = api.get_cursor()
        end)
      end)
      return state
    end

    describe('insert-mode', function()
      before_each(spec.before)

      it('label', function()
        local state = confirm('iA', 'IU', {
          label = 'AIUEO',
        })
        assert.are.same(state.buffer, { 'AIUEO' })
        assert.are.same(state.cursor, { 1, 5 })
      end)

      it('insertText', function()
        local state = confirm('iA', 'IU', {
          label = 'AIUEO',
          insertText = '_AIUEO_',
        })
        assert.are.same(state.buffer, { '_AIUEO_' })
        assert.are.same(state.cursor, { 1, 7 })
      end)

      it('textEdit', function()
        local state = confirm(keymap.t('i***AEO***<Left><Left><Left><Left><Left>'), 'IU', {
          label = 'AIUEO',
          textEdit = {
            range = {
              start = {
                line = 0,
                character = 3,
              },
              ['end'] = {
                line = 0,
                character = 6,
              },
            },
            newText = 'foo\nbar\nbaz',
          },
        })
        assert.are.same(state.buffer, { '***foo', 'bar', 'baz***' })
        assert.are.same(state.cursor, { 3, 3 })
      end)

      it('#1552', function()
        local state = confirm(keymap.t('ios.'), '', {
          filterText = 'IsPermission',
          insertTextFormat = 2,
          label = 'IsPermission',
          textEdit = {
            newText = 'IsPermission($0)',
            range = {
              ['end'] = {
                character = 3,
                line = 0,
              },
              start = {
                character = 3,
                line = 0,
              },
            },
          },
        })
        assert.are.same(state.buffer, { 'os.IsPermission()' })
        assert.are.same(state.cursor, { 1, 16 })
      end)

      it('insertText & snippet', function()
        local state = confirm('iA', 'IU', {
          label = 'AIUEO',
          insertText = 'AIUEO($0)',
          insertTextFormat = types.lsp.InsertTextFormat.Snippet,
        })
        assert.are.same(state.buffer, { 'AIUEO()' })
        assert.are.same(state.cursor, { 1, 6 })
      end)

      it('textEdit & snippet', function()
        local state = confirm(keymap.t('i***AEO***<Left><Left><Left><Left><Left>'), 'IU', {
          label = 'AIUEO',
          insertTextFormat = types.lsp.InsertTextFormat.Snippet,
          textEdit = {
            range = {
              start = {
                line = 0,
                character = 3,
              },
              ['end'] = {
                line = 0,
                character = 6,
              },
            },
            newText = 'foo\nba$0r\nbaz',
          },
        })
        assert.are.same(state.buffer, { '***foo', 'bar', 'baz***' })
        assert.are.same(state.cursor, { 2, 2 })
      end)

      local char = '🗿'
      for _, case in ipairs({
        {
          encoding = types.lsp.PositionEncodingKind.UTF8,
          char_size = #char,
        },
        {
          encoding = types.lsp.PositionEncodingKind.UTF16,
          char_size = select(2, vim.str_utfindex(char)),
        },
        {
          encoding = types.lsp.PositionEncodingKind.UTF32,
          char_size = select(1, vim.str_utfindex(char)),
        },
      }) do
        it('textEdit & multibyte: ' .. case.encoding, function()
          local state = confirm(keymap.t('i%s:%s%s:%s<Left><Left><Left>'):format(char, char, char, char), char, {
            label = char .. char .. char,
            textEdit = {
              range = {
                start = {
                  line = 0,
                  character = case.char_size + #':',
                },
                ['end'] = {
                  line = 0,
                  character = case.char_size + #':' + case.char_size + case.char_size,
                },
              },
              newText = char .. char .. char .. char .. char,
            },
          }, {
            position_encoding_kind = case.encoding,
          })
          vim.print({ state = state, case = case })
          assert.are.same(state.buffer, { ('%s:%s%s%s%s%s:%s'):format(char, char, char, char, char, char, char) })
          assert.are.same(state.cursor, { 1, #('%s:%s%s%s%s%s'):format(char, char, char, char, char, char) })
        end)
      end
    end)

    describe('cmdline-mode', function()
      before_each(spec.before)

      it('label', function()
        local state = confirm(':A', 'IU', {
          label = 'AIUEO',
        })
        assert.are.same(state.buffer, { 'AIUEO' })
        assert.are.same(state.cursor[2], 5)
      end)

      it('insertText', function()
        local state = confirm(':A', 'IU', {
          label = 'AIUEO',
          insertText = '_AIUEO_',
        })
        assert.are.same(state.buffer, { '_AIUEO_' })
        assert.are.same(state.cursor[2], 7)
      end)

      it('textEdit', function()
        local state = confirm(keymap.t(':***AEO***<Left><Left><Left><Left><Left>'), 'IU', {
          label = 'AIUEO',
          textEdit = {
            range = {
              start = {
                line = 0,
                character = 3,
              },
              ['end'] = {
                line = 0,
                character = 6,
              },
            },
            newText = 'AIUEO',
          },
        })
        assert.are.same(state.buffer, { '***AIUEO***' })
        assert.are.same(state.cursor[2], 6)
      end)
    end)
  end)
end)


================================================
FILE: lua/cmp/entry.lua
================================================
local cache = require('cmp.utils.cache')
local char = require('cmp.utils.char')
local misc = require('cmp.utils.misc')
local str = require('cmp.utils.str')
local snippet = require('cmp.utils.snippet')
local config = require('cmp.config')
local types = require('cmp.types')
local matcher = require('cmp.matcher')
local ok, lspkind = pcall(require, 'lspkind')

local function get_icon(kind)
  if ok then
    local icon = lspkind.symbol_map[kind]
    return icon
  end

  return ''
end

---@class cmp.Entry
---@field public id integer
---@field public cache cmp.Cache
---@field public match_cache cmp.Cache
---@field public score integer
---@field public exact boolean
---@field public matches table
---@field public context cmp.Context
---@field public source cmp.Source
---@field public source_offset integer
---@field public source_insert_range lsp.Range
---@field public source_replace_range lsp.Range
---@field public completion_item lsp.CompletionItem
---@field public item_defaults? lsp.internal.CompletionItemDefaults
---@field public resolved_completion_item lsp.CompletionItem|nil
---@field public resolved_callbacks fun()[]
---@field public resolving boolean
---@field public confirmed boolean
---@field public insert_range lsp.Range
---@field public replace_range lsp.Range
---@field public offset integer
---@field public word string
---@field public filter_text string
---@field private match_view_args_ret {input:string, word:string, option:cmp.MatchingConfig, matches:table[]}
local entry = {}
entry.__index = entry

---Create new entry
---@param ctx cmp.Context
---@param source cmp.Source
---@param completion_item lsp.CompletionItem
---@param item_defaults? lsp.internal.CompletionItemDefaults
---@return cmp.Entry
entry.new = function(ctx, source, completion_item, item_defaults)
  local self = setmetatable({}, entry)
  self.id = misc.id('entry.new')
  self.cache = cache.new()
  self.match_cache = cache.new()
  self.score = 0
  self.exact = false
  self.matches = {}
  self.context = ctx
  self.source = source
  self.offset = source.request_offset
  self.source_offset = source.request_offset
  self.source_insert_range = source.default_insert_range
  self.source_replace_range = source.default_replace_range
  self.item_defaults = item_defaults
  self.resolved_completion_item = nil
  self.resolved_callbacks = {}
  self.resolving = false
  self.confirmed = false
  self:_set_completion_item(completion_item)
  return self
end

---@package
entry._set_completion_item = function(self, completion_item)
  if not self.completion_item then
    self.completion_item = self:fill_defaults(completion_item, self.item_defaults)
  else
    -- @see https://github.com/microsoft/vscode/blob/85eea4a9b2ccc99615e970bf2181edbc1781d0f9/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts#L588
    -- @see https://github.com/microsoft/vscode/blob/85eea4a9b2ccc99615e970bf2181edbc1781d0f9/src/vs/base/common/objects.ts#L89
    -- @see https://github.com/microsoft/vscode/blob/a00f2e64f4fa9a1f774875562e1e9697d7138ed3/src/vs/editor/contrib/suggest/browser/suggest.ts#L147
    for k, v in pairs(completion_item) do
      self.completion_item[k] = v or self.completion_item[k]
    end
  end

  local item = self.completion_item

  ---Create filter text
  self.filter_text = item.filterText or str.trim(item.label)

  -- TODO: the order below is important
  if item.textEdit then
    self.insert_range = self:convert_range_encoding(item.textEdit.insert or item.textEdit.range)
    self.replace_range = self:convert_range_encoding(item.textEdit.replace or item.textEdit.range)
  end

  self.word = self:_get_word()
  self.offset = self:_get_offset()

  if not self.insert_range then
    self.insert_range = {
      start = {
        line = self.context.cursor.row - 1,
        character = self.offset - 1,
      },
      ['end'] = self.source_insert_range['end'],
    }
  end

  if not self.replace_range or ((self.context.cursor.col - 1) == self.replace_range['end'].character) then
    self.replace_range = {
      start = {
        line = self.source_replace_range.start.line,
        character = self.offset - 1,
      },
      ['end'] = self.source_replace_range['end'],
    }
  end
end

---@deprecated use entry.offset instead
entry.get_offset = function(self)
  return self.offset
end

---Make offset value
---@package
---@return integer
entry._get_offset = function(self)
  local offset = self.source_offset
  if self.completion_item.textEdit then
    local range = self.insert_range
    if range then
      local start = math.min(range.start.character + 1, offset)
      for idx = start, self.source_offset do
        local byte = string.byte(self.context.cursor_line, idx)
        if byte == nil or not char.is_white(byte) then
          return idx
        end
      end
      return offset
    end
  else
    -- NOTE
    -- The VSCode does not implement this but it's useful if the server does not care about word patterns.
    -- We should care about this performance.
    local word = self.word
    for idx = self.source_offset - 1, self.source_offset - #word, -1 do
      if char.is_semantic_index(self.context.cursor_line, idx) then
        local c = string.byte(self.context.cursor_line, idx)
        if char.is_white(c) then
          break
        end
        local match = true
        for i = 1, self.source_offset - idx do
          local c1 = string.byte(word, i)
          local c2 = string.byte(self.context.cursor_line, idx + i - 1)
          if not c1 or not c2 or c1 ~= c2 then
            match = false
            break
          end
        end
        if match then
          offset = math.min(offset, idx)
        end
      end
    end
  end
  return offset
end

---@deprecated use entry.word instead
entry.get_word = function(self)
  return self.word
end

---Create word for vim.CompletedItem
---NOTE: This method doesn't clear the cache after completionItem/resolve.
---@package
---@return string
entry._get_word = function(self)
  --NOTE: This is nvim-cmp specific implementation.
  local completion_item = self.completion_item
  if completion_item.word then
    return completion_item.word
  end

  local word
  if completion_item.textEdit and not misc.empty(completion_item.textEdit.newText) then
    word = str.trim(completion_item.textEdit.newText)
    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
      word = tostring(snippet.parse(word))
    end
    local overwrite = self:get_overwrite()
    if 0 < overwrite[2] or completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
      word = str.get_word(word, string.byte(self.context.cursor_after_line, 1), overwrite[1] or 0)
    end
  elseif not misc.empty(completion_item.insertText) then
    word = str.trim(completion_item.insertText)
    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
      word = str.get_word(tostring(snippet.parse(word)))
    end
  else
    word = str.trim(completion_item.label)
  end
  return str.oneline(word)
end

---Get overwrite information
---@return integer[]
entry.get_overwrite = function(self)
  return self.cache:ensure('get_overwrite', entry._get_overwrite, self)
end

---@package
entry._get_overwrite = function(self)
  if self.completion_item.textEdit then
    local range = self.insert_range
    if range then
      local vim_start = range.start.character + 1
      local vim_end = range['end'].character + 1
      local before = self.context.cursor.col - vim_start
      local after = vim_end - self.context.cursor.col
      return { before, after }
    end
  end
  return { 0, 0 }
end

---@package
entry.get_filter_text = function(self)
  return self.filter_text
end

---Get LSP's insert text
---@return string
entry.get_insert_text = function(self)
  return self.cache:ensure('get_insert_text', entry._get_insert_text, self)
end

---@package
entry._get_insert_text = function(self)
  local completion_item = self.completion_item
  local word
  if completion_item.textEdit then
    word = str.trim(completion_item.textEdit.newText)
    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
      word = str.remove_suffix(str.remove_suffix(word, '$0'), '${0}')
    end
  elseif completion_item.insertText then
    word = str.trim(completion_item.insertText)
    if completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
      word = str.remove_suffix(str.remove_suffix(word, '$0'), '${0}')
    end
  else
    word = str.trim(completion_item.label)
  end
  return word
end

---Return the item is deprecated or not.
---@return boolean
entry.is_deprecated = function(self)
  return self.completion_item.deprecated or vim.tbl_contains(self.completion_item.tags or {}, types.lsp.CompletionItemTag.Deprecated)
end

---Return view information.
---@param suggest_offset integer
---@param entries_buf integer The buffer this entry will be rendered into.
---@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 } }
entry.get_view = function(self, suggest_offset, entries_buf)
  local item = self:get_vim_item(suggest_offset)
  return self.cache:ensure('get_view:' .. tostring(entries_buf), entry._get_view, self, item, entries_buf)
end

---@package
entry._get_view = function(self, item, entries_buf)
  local view = {}
  -- The result of vim.fn.strdisplaywidth depends on which buffer it was
  -- called in because it reads the values of the option 'tabstop' when
  -- rendering <Tab> characters.
  vim.api.nvim_buf_call(entries_buf, function()
    view.abbr = {}
    view.abbr.text = item.abbr or ''
    view.abbr.bytes = #view.abbr.text
    view.abbr.width = vim.fn.strdisplaywidth(view.abbr.text)
    view.abbr.hl_group = item.abbr_hl_group or (self:is_deprecated() and 'CmpItemAbbrDeprecated' or 'CmpItemAbbr')
    view.icon = {}
    view.icon.text = item.icon or get_icon(types.lsp.CompletionItemKind[self:get_kind()])
    view.icon.bytes = #view.icon.text
    view.icon.width = vim.fn.strdisplaywidth(view.icon.text)
    view.icon.hl_group = item.icon_hl_group or (('CmpItemKind' .. (types.lsp.CompletionItemKind[self:get_kind()] or '') .. 'Icon') or 'CmpItemKind')
    view.kind = {}
    view.kind.text = item.kind or ''
    view.kind.bytes = #view.kind.text
    view.kind.width = vim.fn.strdisplaywidth(view.kind.text)
    view.kind.hl_group = item.kind_hl_group or ('CmpItemKind' .. (types.lsp.CompletionItemKind[self:get_kind()] or ''))
    view.menu = {}
    view.menu.text = item.menu or ''
    view.menu.bytes = #view.menu.text
    view.menu.width = vim.fn.strdisplaywidth(view.menu.text)
    view.menu.hl_group = item.menu_hl_group or 'CmpItemMenu'
    view.dup = item.dup
  end)
  return view
end

---Make vim.CompletedItem
---@param suggest_offset integer
---@return vim.CompletedItem
entry.get_vim_item = function(self, suggest_offset)
  return self.cache:ensure('get_vim_item:' .. tostring(suggest_offset), entry._get_vim_item, self, suggest_offset)
end

---@package
entry._get_vim_item = function(self, suggest_offset)
  local completion_item = self.completion_item
  local word = self.word
  local abbr = str.oneline(completion_item.label)

  -- ~ indicator
  local is_expandable = false
  local expandable_indicator = config.get().formatting.expandable_indicator
  if #(completion_item.additionalTextEdits or {}) > 0 then
    is_expandable = true
  elseif completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
    is_expandable = self:get_insert_text() ~= word
  elseif completion_item.kind == types.lsp.CompletionItemKind.Snippet then
    is_expandable = true
  end
  if expandable_indicator and is_expandable then
    abbr = abbr .. '~'
  end

  -- append delta text
  if suggest_offset < self.offset then
    word = string.sub(self.context.cursor_before_line, suggest_offset, self.offset - 1) .. word
  end

  -- labelDetails.
  local menu = nil
  if completion_item.labelDetails then
    menu = ''
    if completion_item.labelDetails.detail then
      menu = menu .. completion_item.labelDetails.detail
    end
    if completion_item.labelDetails.description then
      menu = menu .. completion_item.labelDetails.description
    end
  end

  -- remove duplicated string.
  if self.offset ~= self.context.cursor.col then
    for i = 1, #word do
      if str.has_prefix(self.context.cursor_after_line, string.sub(word, i, #word)) then
        word = string.sub(word, 1, i - 1)
        break
      end
    end
  end

  local cmp_opts = completion_item.cmp or {}

  local vim_item = {
    word = word,
    abbr = abbr,
    icon = cmp_opts.icon or get_icon(types.lsp.CompletionItemKind[self:get_kind()]),
    icon_hl_group = cmp_opts.icon_hl_group,
    kind = cmp_opts.kind_text or types.lsp.CompletionItemKind[self:get_kind()] or types.lsp.CompletionItemKind[1],
    kind_hl_group = cmp_opts.kind_hl_group,
    menu = menu,
    dup = completion_item.dup or 1,
  }
  if config.get().formatting.format then
    vim_item = config.get().formatting.format(self, vim_item)
  end
  vim_item.word = str.oneline(vim_item.word or '')
  vim_item.abbr = str.oneline(vim_item.abbr or '')
  vim_item.icon = str.oneline(vim_item.icon or '')
  vim_item.kind = str.oneline(vim_item.kind or '')
  vim_item.menu = str.oneline(vim_item.menu or '')
  vim_item.equal = 1
  vim_item.empty = 1

  return vim_item
end

---Get commit characters
---@return string[]
entry.get_commit_characters = function(self)
  return self.completion_item.commitCharacters or {}
end

---@deprecated use entry.insert_range instead
entry.get_insert_range = function(self)
  return self.insert_range
end

---@deprecated use entry.replace_range instead
entry.get_replace_range = function(self)
  return self.replace_range
end

---Match line.
---@param input string
---@param matching_config cmp.MatchingConfig
---@return { score: integer, matches: table[] }
entry.match = function(self, input, matching_config)
  -- https://www.lua.org/pil/11.6.html
  -- do not use '..' to allocate multiple strings
  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)
  local matched = self.match_cache:get(cache_key)
  if matched then
    if self.match_view_args_ret and self.match_view_args_ret.input ~= input then
      self.match_view_args_ret.input = input
      self.match_view_args_ret.word = matched._word
      self.match_view_args_ret.matches = matched.matches
    end
    return matched
  end
  matched = self:_match(input, matching_config)
  self.match_cache:set(cache_key, matched)
  return matched
end

---@package
entry._match = function(self, input, matching_config)
  local completion_item = self.completion_item
  local option = {
    disallow_fuzzy_matching = matching_config.disallow_fuzzy_matching,
    disallow_partial_fuzzy_matching = matching_config.disallow_partial_fuzzy_matching,
    disallow_partial_matching = matching_config.disallow_partial_matching,
    disallow_prefix_unmatching = matching_config.disallow_prefix_unmatching,
    disallow_symbol_nonprefix_matching = matching_config.disallow_symbol_nonprefix_matching,
    synonyms = {
      self.word,
      self.completion_item.label,
    },
  }

  local score, matches, filter_text
  local checked = {} ---@type table<string, boolean>

  filter_text = self.filter_text
  checked[filter_text] = true
  score, matches = matcher.match(input, filter_text, option)

  -- Support the language server that doesn't respect VSCode's behaviors.
  if score == 0 then
    if completion_item.textEdit and not misc.empty(completion_item.textEdit.newText) then
      local diff = self.source_offset - self.offset
      if diff > 0 then
        local prefix = string.sub(self.context.cursor_line, self.offset, self.offset + diff)
        local accept = nil
        accept = accept or string.match(prefix, '^[^%a]+$')
        accept = accept or string.find(completion_item.textEdit.newText, prefix, 1, true)
        if accept then
          filter_text = prefix .. filter_text
          if not checked[filter_text] then
            checked[filter_text] = true
            score, matches = matcher.match(input, filter_text, option)
          end
        end
      end
    end
  end

  -- Fix highlight if filterText is not the same to vim_item.abbr.
  if score > 0 then
    self.match_view_args_ret = {
      input = input,
      word = filter_text,
      option = option,
      matches = matches,
    }
  end

  return { score = score, matches = matches, _word = filter_text }
end

---@param view string
entry.get_view_matches = function(self, view)
  if self.match_view_args_ret then
    if self.match_view_args_ret.word == view then
      return self.match_view_args_ret.matches
    end
    self.match_view_args_ret.word = view
    local input = self.match_view_args_ret.input
    local diff = self.source_offset - self.offset
    if diff > 0 then
      input = input:sub(1 + diff)
    end
    local _, matches = matcher.match(input, view, self.match_view_args_ret.option)
    self.match_view_args_ret.matches = matches
    return matches
  end
end

---@deprecated use entry.completion_item instead
entry.get_completion_item = function(self)
  return self.completion_item
end

---Create documentation
---@return string[]
entry.get_documentation = function(self)
  local item = self.completion_item

  local documents = {}

  -- detail
  if item.detail and item.detail ~= '' then
    local ft = self.context.filetype
    local dot_index = string.find(ft, '%.')
    if dot_index ~= nil then
      ft = string.sub(ft, 0, dot_index - 1)
    end
    table.insert(documents, {
      kind = types.lsp.MarkupKind.Markdown,
      value = ('```%s\n%s\n```'):format(ft, str.trim(item.detail)),
    })
  end

  local documentation = item.documentation
  if type(documentation) == 'string' and documentation ~= '' then
    local value = str.trim(documentation)
    if value ~= '' then
      table.insert(documents, {
        kind = types.lsp.MarkupKind.PlainText,
        value = value,
      })
    end
  elseif type(documentation) == 'table' and not misc.empty(documentation.value) then
    local value = str.trim(documentation.value)
    if value ~= '' then
      table.insert(documents, {
        kind = documentation.kind,
        value = value,
      })
    end
  end

  return vim.lsp.util.convert_input_to_markdown_lines(documents)
end

---Get completion item kind
---@return lsp.CompletionItemKind
entry.get_kind = function(self)
  return self.completion_item.kind or types.lsp.CompletionItemKind.Text
end

---Execute completion item's command.
---@param callback fun()
entry.execute = function(self, callback)
  self.source:execute(self.completion_item, callback)
end

---Resolve completion item.
---@param callback fun()
entry.resolve = function(self, callback)
  if self.resolved_completion_item then
    return callback()
  end
  table.insert(self.resolved_callbacks, callback)

  if not self.resolving then
    self.resolving = true
    self.source:resolve(self.completion_item, function(completion_item)
      self.resolving = false
      if not completion_item then
        return
      end
      self:_set_completion_item(completion_item)
      self.resolved_completion_item = self.completion_item
      self.cache:clear()
      for _, c in ipairs(self.resolved_callbacks) do
        c()
      end
    end)
  end
end

---@param completion_item lsp.CompletionItem
---@param defaults? lsp.internal.CompletionItemDefaults
---@return lsp.CompletionItem
entry.fill_defaults = function(_, completion_item, defaults)
  defaults = defaults or {}

  if defaults.data then
    completion_item.data = completion_item.data or defaults.data
  end

  if defaults.commitCharacters then
    completion_item.commitCharacters = completion_item.commitCharacters or defaults.commitCharacters
  end

  if defaults.insertTextFormat then
    completion_item.insertTextFormat = completion_item.insertTextFormat or defaults.insertTextFormat
  end

  if defaults.insertTextMode then
    completion_item.insertTextMode = completion_item.insertTextMode or defaults.insertTextMode
  end

  if defaults.editRange then
    if not completion_item.textEdit then
      if defaults.editRange.insert then
        completion_item.textEdit = {
          insert = defaults.editRange.insert,
          replace = defaults.editRange.replace,
          newText = completion_item.textEditText or completion_item.label,
        }
      else
        completion_item.textEdit = {
          range = defaults.editRange, --[[@as lsp.Range]]
          newText = completion_item.textEditText or completion_item.label,
        }
      end
    end
  end

  return completion_item
end

---Convert the oneline range encoding.
entry.convert_range_encoding = function(self, range)
  local from_encoding = self.source.position_encoding
  local cache_key = string.format('entry.convert_range_encoding:%d:%d:%s', range.start.character, range['end'].character, from_encoding)
  local res = self.context.cache:get(cache_key)
  if res then
    return res
  end
  res = {
    start = types.lsp.Position.to_utf8(self.context.cursor_line, range.start, from_encoding),
    ['end'] = types.lsp.Position.to_utf8(self.context.cursor_line, range['end'], from_encoding),
  }
  self.context.cache:set(cache_key, res)
  return res
end

---Return true if the entry is invalid.
entry.is_invalid = function(self)
  local is_invalid = false
  is_invalid = is_invalid or misc.empty(self.completion_item.label)
  if self.completion_item.textEdit then
    local range = self.completion_item.textEdit.range or self.completion_item.textEdit.insert
    is_invalid = is_invalid or range.start.line ~= range['end'].line or range.start.line ~= self.context.cursor.line
  end
  return is_invalid
end

return entry


================================================
FILE: lua/cmp/entry_spec.lua
================================================
local spec = require('cmp.utils.spec')

local entry = require('cmp.entry')

describe('entry', function()
  before_each(spec.before)

  it('one char', function()
    local state = spec.state('@.', 1, 3)
    state.input('@')
    local e = entry.new(state.manual(), state.source(), {
      label = '@',
    })
    assert.are.equal(e.offset, 3)
    assert.are.equal(e:get_vim_item(e.offset).word, '@')
  end)

  it('word length (no fix)', function()
    local state = spec.state('a.b', 1, 4)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = 'b',
    })
    assert.are.equal(e.offset, 5)
    assert.are.equal(e:get_vim_item(e.offset).word, 'b')
  end)

  it('word length (fix)', function()
    local state = spec.state('a.b', 1, 4)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = 'b.',
    })
    assert.are.equal(e.offset, 3)
    assert.are.equal(e:get_vim_item(e.offset).word, 'b.')
  end)

  it('semantic index (no fix)', function()
    local state = spec.state('a.bc', 1, 5)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = 'c.',
    })
    assert.are.equal(e.offset, 6)
    assert.are.equal(e:get_vim_item(e.offset).word, 'c.')
  end)

  it('semantic index (fix)', function()
    local state = spec.state('a.bc', 1, 5)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = 'bc.',
    })
    assert.are.equal(e.offset, 3)
    assert.are.equal(e:get_vim_item(e.offset).word, 'bc.')
  end)

  it('[vscode-html-language-server] 1', function()
    local state = spec.state('    </>', 1, 7)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = '/div',
      textEdit = {
        range = {
          start = {
            line = 0,
            character = 0,
          },
          ['end'] = {
            line = 0,
            character = 6,
          },
        },
        newText = '  </div',
      },
    })
    assert.are.equal(e.offset, 5)
    assert.are.equal(e:get_vim_item(e.offset).word, '</div')
  end)

  it('[clangd] 1', function()
    --NOTE: clangd does not return `.foo` as filterText but we should care about it.
    --nvim-cmp does care it by special handling in entry.lua.
    local state = spec.state('foo', 1, 4)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      insertText = '->foo',
      label = ' foo',
      textEdit = {
        newText = '->foo',
        range = {
          start = {
            character = 3,
            line = 1,
          },
          ['end'] = {
            character = 4,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(4).word, '->foo')
    assert.are.equal(e.filter_text, 'foo')
  end)

  it('[typescript-language-server] 1', function()
    local state = spec.state('Promise.resolve()', 1, 18)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      label = 'catch',
    })
    -- The offset will be 18 in this situation because the server returns `[Symbol]` as candidate.
    assert.are.equal(e:get_vim_item(18).word, '.catch')
    assert.are.equal(e.filter_text, 'catch')
  end)

  it('[typescript-language-server] 2', function()
    local state = spec.state('Promise.resolve()', 1, 18)
    state.input('.')
    local e = entry.new(state.manual(), state.source(), {
      filterText = '.Symbol',
      label = 'Symbol',
      textEdit = {
        newText = '[Symbol]',
        range = {
          ['end'] = {
            character = 18,
            line = 0,
          },
          start = {
            character = 17,
            line = 0,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(18).word, '[Symbol]')
    assert.are.equal(e.filter_text, '.Symbol')
  end)

  it('[lua-language-server] 1', function()
    local state = spec.state("local m = require'cmp.confi", 1, 28)
    local e

    -- press g
    state.input('g')
    e = entry.new(state.manual(), state.source(), {
      insertTextFormat = 2,
      label = 'cmp.config',
      textEdit = {
        newText = 'cmp.config',
        range = {
          ['end'] = {
            character = 27,
            line = 1,
          },
          start = {
            character = 18,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(19).word, 'cmp.config')
    assert.are.equal(e.filter_text, 'cmp.config')

    -- press '
    state.input("'")
    e = entry.new(state.manual(), state.source(), {
      insertTextFormat = 2,
      label = 'cmp.config',
      textEdit = {
        newText = 'cmp.config',
        range = {
          ['end'] = {
            character = 27,
            line = 1,
          },
          start = {
            character = 18,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(19).word, 'cmp.config')
    assert.are.equal(e.filter_text, 'cmp.config')
  end)

  it('[lua-language-server] 2', function()
    local state = spec.state("local m = require'cmp.confi", 1, 28)
    local e

    -- press g
    state.input('g')
    e = entry.new(state.manual(), state.source(), {
      insertTextFormat = 2,
      label = 'lua.cmp.config',
      textEdit = {
        newText = 'lua.cmp.config',
        range = {
          ['end'] = {
            character = 27,
            line = 1,
          },
          start = {
            character = 18,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(19).word, 'lua.cmp.config')
    assert.are.equal(e.filter_text, 'lua.cmp.config')

    -- press '
    state.input("'")
    e = entry.new(state.manual(), state.source(), {
      insertTextFormat = 2,
      label = 'lua.cmp.config',
      textEdit = {
        newText = 'lua.cmp.config',
        range = {
          ['end'] = {
            character = 27,
            line = 1,
          },
          start = {
            character = 18,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(19).word, 'lua.cmp.config')
    assert.are.equal(e.filter_text, 'lua.cmp.config')
  end)

  it('[intelephense] 1', function()
    local state = spec.state('\t\t', 1, 4)

    -- press g
    state.input('$')
    local e = entry.new(state.manual(), state.source(), {
      kind = 6,
      label = '$this',
      sortText = '$this',
      textEdit = {
        newText = '$this',
        range = {
          ['end'] = {
            character = 3,
            line = 1,
          },
          start = {
            character = 2,
            line = 1,
          },
        },
      },
    })
    assert.are.equal(e:get_vim_item(e.offset).word, '$this')
    assert.are.equal(e.filter_text, '$this')
  end)

  it('[odin-language-server] 1', function()
    local state = spec.state('\t\t', 1, 4)

    -- press g
    state.input('s')
    local e = entry.new(state.manual(), state.source(), {
      additionalTextEdits = {},
      command = {
        arguments = {},
        command = '',
        title = '',
      },
      deprecated = false,
      detail = 'string',
      documentation = '',
      insertText = '',
      insertTextFormat = 1,
      kind = 14,
      label = 'string',
      tags = {},
    })
    assert.are.equal(e:get_vim_item(e.offset).word, 'string')
  end)

  it('[#47] word should not contain \\n character', function()
    local state = spec.state('', 1, 1)

    -- press g
    state.input('_')
    local e = entry.new(state.manual(), state.source(), {
      kind = 6,
      label = '__init__',
      insertTextFormat = 1,
      insertText = '__init__(self) -> None:\n  pass',
    })
    assert.are.equal(e:get_vim_item(e.offset).word, '__init__(self) -> None:')
    assert.are.equal(e.filter_text, '__init__')
  end)

  -- I can't understand this test case...
  -- it('[#1533] keyword pattern that include whitespace', function()
  --   local state = spec.state(' ', 1, 2)
  --   local state_source = state.source()

  --   state_source.get_keyword_pattern = function(_)
  --     return '.'
  --   end

  --   state.input(' ')
  --   local e = entry.new(state.manual(), state_source, {
  --     filterText = "constructor() {\n     ... st = 'test';\n  ",
  --     kind = 1,
  --     label = "constructor() {\n     ... st = 'test';\n  }",
  --     textEdit = {
  --       newText = "constructor() {\n    this.test = 'test';\n  }",
  --       range = {
  --         ['end'] = {
  --           character = 2,
  --           line = 2,
  --         },
  --         start = {
  --           character = 0,
  --           line = 2,
  --         },
  --       },
  --     },
  --   })
  --   assert.are.equal(e:get_offset(), 2)
  --   assert.are.equal(e:get_vim_item(e:get_offset()).word, 'constructor() {')
  -- end)

  it('[#1533] clang regression test', function()
    local state = spec.state('jsonReader', 3, 11)
    local state_source = state.source()

    state.input('.')
    local e = entry.new(state.manual(), state_source, {
      filterText = 'getPath()',
      kind = 1,
      label = 'getPath()',
      textEdit = {
        newText = 'getPath()',
        range = {
          ['end'] = {
            character = 11,
            col = 12,
            line = 2,
            row = 3,
          },
          start = {
            character = 11,
            line = 2,
          },
        },
      },
    })
    assert.are.equal(e.offset, 12)
    assert.are.equal(e:get_vim_item(e.offset).word, 'getPath()')
  end)
end)


================================================
FILE: lua/cmp/init.lua
================================================
local core = require('cmp.core')
local source = require('cmp.source')
local config = require('cmp.config')
local feedkeys = require('cmp.utils.feedkeys')
local autocmd = require('cmp.utils.autocmd')
local keymap = require('cmp.utils.keymap')
local misc = require('cmp.utils.misc')
local async = require('cmp.utils.async')

local cmp = {}

cmp.core = core.new()

---Expose types
for k, v in pairs(require('cmp.types.cmp')) do
  cmp[k] = v
end
cmp.lsp = require('cmp.types.lsp')
cmp.vim = require('cmp.types.vim')

---Expose event
cmp.event = cmp.core.event

---Export mapping for special case
cmp.mapping = require('cmp.config.mapping')

---Export default config presets
cmp.config = {}
cmp.config.disable = misc.none
cmp.config.compare = require('cmp.config.compare')
cmp.config.sources = require('cmp.config.sources')
cmp.config.mapping = require('cmp.config.mapping')
cmp.config.window = require('cmp.config.window')

---Sync asynchronous process.
cmp.sync = function(callback)
  return function(...)
    cmp.core.filter:sync(1000)
    if callback then
      return callback(...)
    end
  end
end

---Suspend completion.
cmp.suspend = function()
  return cmp.core:suspend()
end

---Register completion sources
---@param name string
---@param s cmp.Source
---@return integer
cmp.register_source = function(name, s)
  local src = source.new(name, s)
  cmp.core:register_source(src)
  vim.api.nvim_exec_autocmds('User', {
    pattern = 'CmpRegisterSource',
    data = {
      source_id = src.id,
    },
  })
  return src.id
end

---Unregister completion source
---@param id integer
cmp.unregister_source = function(id)
  local s = cmp.core:unregister_source(id)
  if s then
    vim.api.nvim_exec_autocmds('User', {
      pattern = 'CmpUnregisterSource',
      data = {
        source_id = id,
      },
    })
  end
end

---Get registered sources.
---@return cmp.Source[]
cmp.get_registered_sources = function()
  return cmp.core:get_registered_sources()
end

---Get current configuration.
---@return cmp.ConfigSchema
cmp.get_config = function()
  return require('cmp.config').get()
end

---Invoke completion manually
---@param option cmp.CompleteParams
cmp.complete = cmp.sync(function(option)
  option = option or {}
  config.set_onetime(option.config)
  cmp.core:complete(cmp.core:get_context({ reason = option.reason or cmp.ContextReason.Manual }))
  return true
end)

---Complete common string in current entries.
cmp.complete_common_string = cmp.sync(function()
  return cmp.core:complete_common_string()
end)

---Return view is visible or not.
cmp.visible = cmp.sync(function()
  return cmp.core.view:visible() or vim.fn.pumvisible() == 1
end)

---Get what number candidates are currently selected.
---If not selected, nil is returned.
cmp.get_selected_index = cmp.sync(function()
  return cmp.core.view:get_selected_index()
end)

---Get current selected entry or nil
cmp.get_selected_entry = cmp.sync(function()
  return cmp.core.view:get_selected_entry()
end)

---Get current active entry or nil
cmp.get_active_entry = cmp.sync(function()
  return cmp.core.view:get_active_entry()
end)

---Get current all entries
cmp.get_entries = cmp.sync(function()
  return cmp.core.view:get_entries()
end)

---Close current completion
cmp.close = cmp.sync(function()
  if cmp.core.view:visible() then
    local release = cmp.core:suspend()
    cmp.core.view:close()
    cmp.core:reset()
    vim.schedule(release)
    return true
  else
    return false
  end
end)

---Abort current completion
cmp.abort = cmp.sync(function()
  if cmp.core.view:visible() then
    local release = cmp.core:suspend()
    cmp.core.view:abort()
    cmp.core:reset()
    vim.schedule(release)
    return true
  else
    return false
  end
end)

---Select next item if possible
cmp.select_next_item = cmp.sync(function(option)
  option = option or {}
  option.behavior = option.behavior or cmp.SelectBehavior.Insert
  option.count = option.count or 1

  if cmp.core.view:visible() then
    local release = cmp.core:suspend()
    cmp.core.view:select_next_item(option)
    vim.schedule(release)
    return true
  elseif vim.fn.pumvisible() == 1 then
    if option.behavior == cmp.SelectBehavior.Insert then
      feedkeys.call(keymap.t(string.rep('<C-n>', option.count)), 'in')
    else
      feedkeys.call(keymap.t(string.rep('<Down>', option.count)), 'in')
    end
    return true
  end
  return false
end)

---Select prev item if possible
cmp.select_prev_item = cmp.sync(function(option)
  option = option or {}
  option.behavior = option.behavior or cmp.SelectBehavior.Insert
  option.count = option.count or 1

  if cmp.core.view:visible() then
    local release = cmp.core:suspend()
    cmp.core.view:select_prev_item(option)
    vim.schedule(release)
    return true
  elseif vim.fn.pumvisible() == 1 then
    if option.behavior == cmp.SelectBehavior.Insert then
      feedkeys.call(keymap.t(string.rep('<C-p>', option.count)), 'in')
    else
      feedkeys.call(keymap.t(string.rep('<Up>', option.count)), 'in')
    end
    return true
  end
  return false
end)

---Scrolling documentation window if possible
cmp.scroll_docs = cmp.sync(function(delta)
  if cmp.core.view.docs_view:visible() then
    cmp.core.view:scroll_docs(delta)
    return true
  else
    return false
  end
end)

---Whether the documentation window is visible or not.
cmp.visible_docs = cmp.sync(function()
  return cmp.core.view.docs_view:visible()
end)

---Opens the documentation window.
cmp.open_docs = cmp.sync(function()
  if not cmp.visible_docs() then
    cmp.core.view:open_docs()
    return true
  else
    return false
  end
end)

---Closes the documentation window.
cmp.close_docs = cmp.sync(function()
  if cmp.visible_docs() then
    cmp.core.view:close_docs()
    return true
  else
    return false
  end
end)

---Confirm completion
cmp.confirm = cmp.sync(function(option, callback)
  option = option or {}
  option.select = option.select or false
  option.behavior = option.behavior or cmp.get_config().confirmation.default_behavior or cmp.ConfirmBehavior.Insert
  callback = callback or function() end

  if cmp.core.view:visible() then
    local e = cmp.core.view:get_selected_entry()
    if not e and option.select then
      e = cmp.core.view:get_first_entry()
    end
    if e then
      cmp.core:confirm(e, {
        behavior = option.behavior,
      }, function()
        callback()
        cmp.core:complete(cmp.core:get_context({ reason = cmp.ContextReason.TriggerOnly }))
      end)
      return true
    end
  elseif vim.fn.pumvisible() == 1 then
    local index = vim.fn.complete_info({ 'selected' }).selected
    if index == -1 and option.select then
      index = 0
    end
    if index ~= -1 then
      vim.api.nvim_select_popupmenu_item(index, true, true, {})
      return true
    end
  end
  return false
end)

---Show status
cmp.status = function()
  local kinds = {}
  kinds.available = {}
  kinds.unavailable = {}
  kinds.installed = {}
  kinds.invalid = {}
  local names = {}
  for _, s in pairs(cmp.core.sources) do
    names[s.name] = true

    if config.get_source_config(s.name) then
      if s:is_available() then
        table.insert(kinds.available, s:get_debug_name())
      else
        table.insert(kinds.unavailable, s:get_debug_name())
      end
    else
      table.insert(kinds.installed, s:get_debug_name())
    end
  end
  for _, s in ipairs(config.get().sources) do
    if not names[s.name] then
      table.insert(kinds.invalid, s.name)
    end
  end

  if #kinds.available > 0 then
    vim.api.nvim_echo({ { '\n', 'Normal' } }, false, {})
    vim.api.nvim_echo({ { '# ready source names\n', 'Special' } }, false, {})
    for _, name in ipairs(kinds.available) do
      vim.api.nvim_echo({ { ('- %s\n'):format(name), 'Normal' } }, false, {})
    end
  end

  if #kinds.unavailable > 0 then
    vim.api.nvim_echo({ { '\n', 'Normal' } }, false, {})
    vim.api.nvim_echo({ { '# unavailable source names\n', 'Comment' } }, false, {})
    for _, name in ipairs(kinds.unavailable) do
      vim.api.nvim_echo({ { ('- %s\n'):format(name), 'Normal' } }, false, {})
    end
  end

  if #kinds.installed > 0 then
    vim.api.nvim_echo({ { '\n', 'Normal' } }, false, {})
    vim.api.nvim_echo({ { '# unused source names\n', 'WarningMsg' } }, false, {})
    for _, name in ipairs(kinds.installed) do
      vim.api.nvim_echo({ { ('- %s\n'):format(name), 'Normal' } }, false, {})
    end
  end

  if #kinds.invalid > 0 then
    vim.api.nvim_echo({ { '\n', 'Normal' } }, false, {})
    vim.api.nvim_echo({ { '# unknown source names\n', 'ErrorMsg' } }, false, {})
    for _, name in ipairs(kinds.invalid) do
      vim.api.nvim_echo({ { ('- %s\n'):format(name), 'Normal' } }, false, {})
    end
  end
end

---Ensures that cmp is the last receiver of the events specified.
---@param events string[]
cmp.resubscribe = function(events)
  autocmd.resubscribe(events)
end

---@type cmp.Setup
cmp.setup = setmetatable({
  global = function(c)
    config.set_global(c)
  end,
  filetype = function(filetype, c)
    config.set_filetype(c, filetype)
  end,
  buffer = function(c)
    config.set_buffer(c, vim.api.nvim_get_current_buf())
  end,
  cmdline = function(type, c)
    config.set_cmdline(c, type)
  end,
}, {
  __call = function(self, c)
    self.global(c)
  end,
})

-- In InsertEnter autocmd, vim will detects mode=normal unexpectedly.
local on_insert_enter = function()
  if config.enabled() then
    cmp.config.compare.scopes:update()
    cmp.config.compare.locality:update()
    cmp.core:prepare()
    cmp.core:on_change('InsertEnter')
  end
end
autocmd.subscribe({ 'CmdlineEnter' }, async.debounce_next_tick(on_insert_enter))
autocmd.subscribe({ 'InsertEnter' }, async.debounce_next_tick_by_keymap(on_insert_enter))

-- async.throttle is needed for performance. The mapping `:<C-u>...<CR>` will fire `CmdlineChanged` for each character.
local on_text_changed = function()
  if config.enabled() then
    cmp.core:on_change('TextChanged')
  end
end
autocmd.subscribe({ 'TextChangedI', 'TextChangedP' }, on_text_changed)
autocmd.subscribe('CmdlineChanged', async.debounce_next_tick(on_text_changed))

autocmd.subscribe('CursorMovedI', function()
  if config.enabled() then
    cmp.core:on_moved()
  else
    cmp.core:reset()
    cmp.core.view:close()
  end
end)

-- If make this asynchronous, the completion menu will not close when the command output is displayed.
autocmd.subscribe({ 'InsertLeave', 'CmdlineLeave', 'CmdwinEnter' }, function()
  cmp.core:reset()
  cmp.core.view:close()
end)

cmp.event:on('complete_done', function(evt)
  if evt.entry then
    cmp.config.compare.recently_used:add_entry(evt.entry)
  end
  cmp.config.compare.scopes:update()
  cmp.config.compare.locality:update()
end)

cmp.event:on('confirm_done', function(evt)
  if evt.entry then
    cmp.config.compare.recently_used:add_entry(evt.entry)
  end
end)

return cmp


================================================
FILE: lua/cmp/matcher.lua
================================================
local char = require('cmp.utils.char')

local matcher = {}

matcher.WORD_BOUNDALY_ORDER_FACTOR = 10

matcher.PREFIX_FACTOR = 8
matcher.NOT_FUZZY_FACTOR = 6

---@type function
matcher.debug = function(...)
  return ...
end

--- score
--
-- ### The score
--
--   The `score` is `matched char count` generally.
--
--   But cmp will fix the score with some of the below points so the actual score is not `matched char count`.
--
--   1. Word boundary order
--
--     cmp prefers the match that near by word-beggining.
--
--   2. Strict case
--
--     cmp prefers strict match than ignorecase match.
--
--
-- ### Matching specs.
--
--   1. Prefix matching per word boundary
--
--     `bora`         -> `border-radius` # imaginary score: 4
--      ^^~~              ^^     ~~
--
--   2. Try sequential match first
--
--     `woroff`       -> `word_offset`   # imaginary score: 6
--      ^^^~~~            ^^^  ~~~
--
--     * The `woroff`'s second `o` should not match `word_offset`'s first `o`
--
--   3. Prefer early word boundary
--
--     `call`         -> `call`          # imaginary score: 4.1
--      ^^^^              ^^^^
--     `call`         -> `condition_all` # imaginary score: 4
--      ^~~~              ^         ~~~
--
--   4. Prefer strict match
--
--     `Buffer`       -> `Buffer`        # imaginary score: 6.1
--      ^^^^^^            ^^^^^^
--     `buffer`       -> `Buffer`        # imaginary score: 6
--      ^^^^^^            ^^^^^^
--
--   5. Use remaining characters for substring match
--
--     `fmodify`        -> `fnamemodify`   # imaginary score: 1
--      ^~~~~~~             ^    ~~~~~~
--
--   6. Avoid unexpected match detection
--
--     `candlesingle` -> candle#accept#single
--      ^^^^^^~~~~~~     ^^^^^^        ~~~~~~
--      * The `accept`'s `a` should not match to `candle`'s `a`
--
--   7. Avoid false positive matching
--
--     `,` -> print,
--                 ~
--      * Typically, the middle match with symbol characters only is false positive. should be ignored.
--        This doesn't work for command line completions like ":b foo_" which we like to match
--        "lib/foo_bar.txt". The option disallow_symbol_nonprefix_matching controls this and defaults
--        to preventing matches like these. The documentation recommends it for command line completion.
--
--
---Match entry
---@param input string
---@param word string
---@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 }
---@return integer, table
matcher.match = function(input, word, option)
  option = option or {}

  -- Empty input
  if #input == 0 then
    return matcher.PREFIX_FACTOR + matcher.NOT_FUZZY_FACTOR, {}
  end

  -- Ignore if input is long than word
  if #input > #word then
    return 0, {}
  end

  -- Check prefix matching.
  if option.disallow_prefix_unmatching then
    if not char.match(string.byte(input, 1), string.byte(word, 1)) then
      return 0, {}
    end
  end

  -- Gather matched regions
  local matches = {}
  local input_start_index = 1
  local input_end_index = 1
  local word_index = 1
  local word_bound_index = 1
  local no_symbol_match = false
  while input_end_index <= #input and word_index <= #word do
    local m = matcher.find_match_region(input, input_start_index, input_end_index, word, word_index)
    if m and input_end_index <= m.input_match_end then
      m.index = word_bound_index
      input_start_index = m.input_match_start + 1
      input_end_index = m.input_match_end + 1
      no_symbol_match = no_symbol_match or m.no_symbol_match
      word_index = char.get_next_semantic_index(word, m.word_match_end)
      table.insert(matches, m)
    else
      word_index = char.get_next_semantic_index(word, word_index)
    end
    word_bound_index = word_bound_index + 1
  end

  -- Check partial matching.
  if option.disallow_partial_matching and #matches > 1 then
    return 0, {}
  end

  if #matches == 0 then
    if not option.disallow_fuzzy_matching and not option.disallow_prefix_unmatching and not option.disallow_partial_fuzzy_matching then
      if matcher.fuzzy(input, word, matches, option) then
        return 1, matches
      end
    end
    return 0, {}
  end

  matcher.debug(word, matches)

  -- Add prefix bonus
  local prefix = false
  if matches[1].input_match_start == 1 and matches[1].word_match_start == 1 then
    prefix = true
  else
    for _, synonym in ipairs(option.synonyms or {}) do
      prefix = true
      local o = 1
      for i = matches[1].input_match_start, matches[1].input_match_end do
        if not char.match(string.byte(synonym, o), string.byte(input, i)) then
          prefix = false
          break
        end
        o = o + 1
      end
      if prefix then
        break
      end
    end
  end

  if no_symbol_match and not prefix then
    if option.disallow_symbol_nonprefix_matching then
      return 0, {}
    end
  end

  -- Compute prefix match score
  local score = prefix and matcher.PREFIX_FACTOR or 0
  local offset = prefix and matches[1].index - 1 or 0
  local idx = 1
  for _, m in ipairs(matches) do
    local s = 0
    for i = math.max(idx, m.input_match_start), m.input_match_end do
      s = s + 1
      idx = i
    end
    idx = idx + 1
    if s > 0 then
      s = s * (1 + m.strict_ratio)
      s = s * (1 + math.max(0, matcher.WORD_BOUNDALY_ORDER_FACTOR - (m.index - offset)) / matcher.WORD_BOUNDALY_ORDER_FACTOR)
      score = score + s
    end
  end

  -- Check remaining input as fuzzy
  if matches[#matches].input_match_end < #input then
    if not option.disallow_fuzzy_matching then
      if not option.disallow_partial_fuzzy_matching or prefix then
        if matcher.fuzzy(input, word, matches, option) then
          return score, matches
        end
      end
    end
    return 0, {}
  end

  return score + matcher.NOT_FUZZY_FACTOR, matches
end

--- fuzzy
matcher.fuzzy = function(input, word, matches, option)
  local input_index = matches[#matches] and (matches[#matches].input_match_end + 1) or 1

  -- Lately specified middle of text.
  for i = 1, #matches - 1 do
    local curr_match = matches[i]
    local next_match = matches[i + 1]
    local word_offset = 0
    local word_index = char.get_next_semantic_index(word, curr_match.word_match_end)
    while word_offset + word_index < next_match.word_match_start and input_index <= #input do
      if char.match(string.byte(word, word_index + word_offset), string.byte(input, input_index)) then
        input_index = input_index + 1
        word_offset = word_offset + 1
      else
        word_index = char.get_next_semantic_index(word, word_index + word_offset)
        word_offset = 0
      end
    end
  end

  -- Remaining text fuzzy match.
  local matched = false
  local word_offset = 0
  local word_index = matches[#matches] and (matches[#matches].word_match_end + 1) or 1
  local input_match_start = -1
  local input_match_end = -1
  local word_match_start = -1
  local strict_count = 0
  local match_count = 0
  while word_offset + word_index <= #word and input_index <= #input do
    local c1, c2 = string.byte(word, word_index + word_offset), string.byte(input, input_index)
    if char.match(c1, c2) then
      if not matched then
        input_match_start = input_index
        word_match_start = word_index + word_offset
      end
      matched = true
      input_index = input_index + 1
      strict_count = strict_count + (c1 == c2 and 1 or 0)
      match_count = match_count + 1
    else
      if option.disallow_fullfuzzy_matching then
        break
      else
        if matched then
          table.insert(matches, {
            input_match_start = input_match_start,
            input_match_end = input_index - 1,
            word_match_start = word_match_start,
            word_match_end = word_index + word_offset - 1,
            strict_ratio = strict_count / match_count,
            fuzzy = true,
          })
        end
      end
      matched = false
    end
    word_offset = word_offset + 1
  end

  if matched and input_index > #input then
    table.insert(matches, {
      input_match_start = input_match_start,
      input_match_end = input_match_end,
      word_match_start = word_match_start,
      word_match_end = word_index + word_offset - 1,
      strict_ratio = strict_count / match_count,
      fuzzy = true,
    })
    return true
  end
  return false
end

--- find_match_region
matcher.find_match_region = function(input, input_start_index, input_end_index, word, word_index)
  -- determine input position ( woroff -> word_offset )
  while input_start_index < input_end_index do
    if char.match(string.byte(input, input_end_index), string.byte(word, word_index)) then
      break
    end
    input_end_index = input_end_index - 1
  end

  -- Can't determine input position
  if input_end_index < input_start_index then
    return nil
  end

  local input_match_start = -1
  local input_index = input_end_index
  local word_offset = 0
  local strict_count = 0
  local match_count = 0
  local no_symbol_match = false
  while input_index <= #input and word_index + word_offset <= #word do
    local c1 = string.byte(input, input_index)
    local c2 = string.byte(word, word_index + word_offset)
    if char.match(c1, c2) then
      -- Match start.
      if input_match_start == -1 then
        input_match_start = input_index
      end

      strict_count = strict_count + (c1 == c2 and 1 or 0)
      match_count = match_count + 1
      word_offset = word_offset + 1
      no_symbol_match = no_symbol_match or char.is_symbol(c1)
    else
      -- Match end (partial region)
      if input_match_start ~= -1 then
        return {
          input_match_start = input_match_start,
          input_match_end = input_index - 1,
          word_match_start = word_index,
          word_match_end = word_index + word_offset - 1,
          strict_ratio = strict_count / match_count,
          no_symbol_match = no_symbol_match,
          fuzzy = false,
        }
      else
        return nil
      end
    end
    input_index = input_index + 1
  end

  -- Match end (whole region)
  if input_match_start ~= -1 then
    return {
      input_match_start = input_match_start,
      input_match_end = input_index - 1,
      word_match_start = word_index,
      word_match_end = word_index + word_offset - 1,
      strict_ratio = strict_count / match_count,
      no_symbol_match = no_symbol_match,
      fuzzy = false,
    }
  end

  return nil
end

return matcher


================================================
FILE: lua/cmp/matcher_spec.lua
================================================
local spec = require('cmp.utils.spec')
local default_config = require('cmp.config.default')

local matcher = require('cmp.matcher')

describe('matcher', function()
  before_each(spec.before)

  it('match', function()
    local config = default_config()
    assert.is.truthy(matcher.match('', 'a', config.matching) >= 1)
    assert.is.truthy(matcher.match('a', 'a', config.matching) >= 1)
    assert.is.truthy(matcher.match('ab', 'a', config.matching) == 0)
    assert.is.truthy(matcher.match('ab', 'ab', config.matching) > matcher.match('ab', 'a_b', config.matching))
    assert.is.truthy(matcher.match('ab', 'a_b_c', config.matching) > matcher.match('ac', 'a_b_c', config.matching))

    assert.is.truthy(matcher.match('bora', 'border-radius', config.matching) >= 1)
    assert.is.truthy(matcher.match('woroff', 'word_offset', config.matching) >= 1)
    assert.is.truthy(matcher.match('call', 'call', config.matching) > matcher.match('call', 'condition_all', config.matching))
    assert.is.truthy(matcher.match('Buffer', 'Buffer', config.matching) > matcher.match('Buffer', 'buffer', config.matching))
    assert.is.truthy(matcher.match('luacon', 'lua_context', config.matching) > matcher.match('luacon', 'LuaContext', config.matching))
    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', config.matching) >= 1)
    assert.is.truthy(matcher.match('candlesingle', 'candle#accept#single', config.matching) >= 1)

    assert.is.truthy(matcher.match('vi', 'void#', config.matching) >= 1)
    assert.is.truthy(matcher.match('vo', 'void#', config.matching) >= 1)
    assert.is.truthy(matcher.match('var_', 'var_dump', config.matching) >= 1)
    assert.is.truthy(matcher.match('conso', 'console', config.matching) > matcher.match('conso', 'ConstantSourceNode', config.matching))
    assert.is.truthy(matcher.match('usela', 'useLayoutEffect', config.matching) > matcher.match('usela', 'useDataLayer', config.matching))
    assert.is.truthy(matcher.match('my_', 'my_awesome_variable', config.matching) > matcher.match('my_', 'completion_matching_strategy_list', config.matching))
    assert.is.truthy(matcher.match('2', '[[2021', config.matching) >= 1)

    assert.is.truthy(matcher.match(',', 'pri,', config.matching) == 0)
    assert.is.truthy(matcher.match('/', '/**', config.matching) >= 1)

    assert.is.truthy(matcher.match('true', 'v:true', { synonyms = { 'true' } }, config.matching) == matcher.match('true', 'true', config.matching))
    assert.is.truthy(matcher.match('g', 'get', { synonyms = { 'get' } }, config.matching) > matcher.match('g', 'dein#get', { 'dein#get' }, config.matching))

    assert.is.truthy(matcher.match('Unit', 'net.UnixListener', { disallow_partial_fuzzy_matching = true }, config.matching) == 0)
    assert.is.truthy(matcher.match('Unit', 'net.UnixListener', { disallow_partial_fuzzy_matching = false }, config.matching) >= 1)

    assert.is.truthy(matcher.match('emg', 'error_msg', config.matching) >= 1)
    assert.is.truthy(matcher.match('sasr', 'saved_splitright', config.matching) >= 1)

    -- TODO: #1420 test-case
    -- assert.is.truthy(matcher.match('asset_', '????') >= 0)

    local score, matches
    score, matches = matcher.match('tail', 'HCDetails', {
      disallow_fuzzy_matching = false,
      disallow_partial_matching = false,
      disallow_prefix_unmatching = false,
      disallow_partial_fuzzy_matching = false,
      disallow_symbol_nonprefix_matching = true,
    })
    assert.is.truthy(score >= 1)
    assert.equals(matches[1].word_match_start, 5)

    score = matcher.match('tail', 'HCDetails', {
      disallow_fuzzy_matching = false,
      disallow_partial_matching = false,
      disallow_prefix_unmatching = false,
      disallow_partial_fuzzy_matching = true,
      disallow_symbol_nonprefix_matching = true,
    })
    assert.is.truthy(score == 0)
  end)

  it('disallow_fuzzy_matching', function()
    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = true }) == 0)
    assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = false }) >= 1)
  end)

  it('disallow_fullfuzzy_matching', function()
    assert.is.truthy(matcher.match('svd', 'saved_splitright', { disallow_fullfuzzy_matching = true }) == 0)
    assert.is.truthy(matcher.match('svd', 'saved_splitright', { disallow_fullfuzzy_matching = false }) >= 1)
  end)

  it('disallow_partial_matching', function()
    assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = true }) == 0)
    assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = false }) >= 1)
    assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = true }) >= 1)
    assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = false }) >= 1)
  end)

  it('disallow_prefix_unmatching', function()
    assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = true }) == 0)
    assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = false }) >= 1)
  end)

  it('disallow_symbol_nonprefix_matching', function()
    assert.is.truthy(matcher.match('foo_', 'b foo_bar', { disallow_symbol_nonprefix_matching = true }) == 0)
    assert.is.truthy(matcher.match('foo_', 'b foo_bar', { disallow_symbol_nonprefix_matching = false }) >= 1)
  end)

  it('debug', function()
    matcher.debug = function(...)
      print(vim.inspect({ ... }))
    end
    -- print(vim.inspect({
    --   a = matcher.match('true', 'v:true', { 'true' }),
    --   b = matcher.match('true', 'true'),
    -- }))
  end)
end)


================================================
FILE: lua/cmp/source.lua
================================================
local context = require('cmp.context')
local config = require('cmp.config')
local entry = require('cmp.entry')
local debug = require('cmp.utils.debug')
local misc = require('cmp.utils.misc')
local cache = require('cmp.utils.cache')
local types = require('cmp.types')
local async = require('cmp.utils.async')
local pattern = require('cmp.utils.pattern')
local char = require('cmp.utils.char')

---@class cmp.Source
---@field public id integer
---@field public name string
---@field public source any
---@field public cache cmp.Cache
---@field public revision integer
---@field public response? lsp.CompletionResponse|nil
---@field public incomplete boolean
---@field public is_triggered_by_symbol boolean
---@field public entries cmp.Entry[]
---@field public offset integer
---@field public request_offset integer
---@field public context cmp.Context
---@field public completion_context lsp.CompletionContext|nil
---@field public status cmp.SourceStatus
---@field public complete_dedup function
---@field public default_replace_range lsp.Range
---@field public default_insert_range lsp.Range
---@field public position_encoding lsp.PositionEncodingKind
local source = {}

---@alias cmp.SourceStatus 1 | 2 | 3
source.SourceStatus = {}
source.SourceStatus.WAITING = 1
source.SourceStatus.FETCHING = 2
source.SourceStatus.COMPLETED = 3

---@return cmp.Source
source.new = function(name, s)
  local self = setmetatable({}, { __index = source })
  self.id = misc.id('cmp.source.new')
  self.name = name
  self.source = s
  self.cache = cache.new()
  self.complete_dedup = async.dedup()
  self.revision = 0
  self.position_encoding = self:get_position_encoding_kind()
  self:reset()
  return self
end

---Reset current completion state
source.reset = function(self)
  self.cache:clear()
  self.revision = self.revision + 1
  self.context = context.empty()
  self.is_triggered_by_symbol = false
  self.incomplete = false
  self.entries = {}
  self.offset = -1
  self.request_offset = -1
  self.completion_context = nil
  self.status = source.SourceStatus.WAITING
  self.complete_dedup(function() end)
end

---Return source config
---@return cmp.SourceConfig
source.get_source_config = function(self)
  return config.get_source_config(self.name) or {}
end

---Return matching config
---@return cmp.MatchingConfig
source.get_matching_config = function()
  return config.get().matching
end

---Get fetching time
source.get_fetching_time = function(self)
  if self.status == source.SourceStatus.FETCHING then
    return vim.loop.now() - self.context.time
  end
  return 100 * 1000 -- return pseudo time if source isn't fetching.
end

---Return filtered entries
---@param ctx cmp.Context
---@return cmp.Entry[]
source.get_entries = function(self, ctx)
  if self.offset == -1 then
    return {}
  end

  local target_entries = self.entries

  if not self.incomplete then
    local prev = self.cache:get({ 'get_entries', tostring(self.revision) })
    if prev and ctx.cursor.row == prev.ctx.cursor.row and self.offset == prev.offset then
      -- only use prev entries when cursor is moved forward.
      -- and the pattern offset is the same.
      if prev.ctx.cursor.col <= ctx.cursor.col then
        target_entries = prev.entries
      end
    end
  end

  local entry_filter = self:get_entry_filter()

  local inputs = {}
  ---@type cmp.Entry[]
  local entries = {}
  local matching_config = self:get_matching_config()
  local filtering_context_budget = config.get().performance.filtering_context_budget / 1000

  local stime = (vim.uv or vim.loop).hrtime() / 1000000
  for _, e in ipairs(target_entries) do
    local o = e.offset
    if not inputs[o] then
      inputs[o] = string.sub(ctx.cursor_before_line, o)
    end

    local match = e:match(inputs[o], matching_config)
    e.score = match.score
    e.exact = false
    if e.score >= 1 then
      e.matches = match.matches
      e.exact = e.filter_text == inputs[o] or e.word == inputs[o]

      if entry_filter(e, ctx) then
        entries[#entries + 1] = e
      end
    end

    local etime = (vim.uv or vim.loop).hrtime() / 1000000
    if etime - stime > filtering_context_budget then
      async.yield()
      if ctx.aborted then
        async.abort()
      end
      stime = etime
    end
  end

  if not self.incomplete then
    self.cache:set({ 'get_entries', tostring(self.revision) }, { entries = entries, ctx = ctx, offset = self.offset })
  end

  return entries
end

---Get default insert range (UTF8 byte index).
---@package
---@return lsp.Range
source._get_default_insert_range = function(self)
  return {
    start = {
      line = self.context.cursor.row - 1,
      character = self.offset - 1,
    },
    ['end'] = {
      line = self.context.cursor.row - 1,
      character = self.context.cursor.col - 1,
    },
  }
end

---Get default replace range (UTF8 byte index).
---@package
---@return lsp.Range
source._get_default_replace_range = function(self)
  local _, e = pattern.offset('^' .. '\\%(' .. self:get_keyword_pattern() .. '\\)', string.sub(self.context.cursor_line, self.offset))
  return {
    start = {
      line = self.context.cursor.row - 1,
      character = self.offset,
    },
    ['end'] = {
      line = self.context.cursor.row - 1,
      character = (e and self.offset + e - 2 or self.context.cursor.col - 1),
    },
  }
end

---@deprecated use source.default_insert_range instead
source.get_default_insert_range = function(self)
  return self.default_insert_range
end

---@deprecated use source.default_replace_range instead
source.get_default_replae_range = function(self)
  return self.default_replace_range
end

---Return source name.
source.get_debug_name = function(self)
  local name = self.name
  if self.source.get_debug_name then
    name = self.source:get_debug_name()
  end
  return name
end

---Return the source is available or not.
source.is_available = function(self)
  if self.source.is_available then
    return self.source:is_available()
  end
  return true
end

---Get trigger_characters
---@return string[]
source.get_trigger_characters = function(self)
  local c = self:get_source_config()
  if c.trigger_characters then
    return c.trigger_characters
  end

  local trigger_characters = {}
  if self.source.get_trigger_characters then
    trigger_characters = self.source:get_trigger_characters(misc.copy(c)) or {}
  end
  if config.get().completion.get_trigger_characters then
    return config.get().completion.get_trigger_characters(trigger_characters)
  end
  return trigger_characters
end

---Get keyword_pattern
---@return string
source.get_keyword_pattern = function(self)
  local c = self:get_source_config()
  if c.keyword_pattern then
    return c.keyword_pattern
  end
  if self.source.get_keyword_pattern then
    local keyword_pattern = self.source:get_keyword_pattern(misc.copy(c))
    if keyword_pattern then
      return keyword_pattern
    end
  end
  return config.get().completion.keyword_pattern
end

---Get keyword_length
---@return integer
source.get_keyword_length = function(self)
  local c = self:get_source_config()
  if c.keyword_length then
    return c.keyword_length
  end
  return config.get().completion.keyword_length or 1
end

---Get filter
--@return fun(entry: cmp.Entry, context: cmp.Context): boolean
source.get_entry_filter = function(self)
  local c = self:get_source_config()
  if c.entry_filter then
    return c.entry_filter --[[@as fun(entry: cmp.Entry, context: cmp.Context): boolean]]
  end
  return function(_, _)
    return true
  end
end

---Get lsp.PositionEncodingKind
---@return lsp.PositionEncodingKind
source.get_position_encoding_kind = function(self)
  if self.source.get_position_encoding_kind then
    return self.source:get_position_encoding_kind()
  end
  return types.lsp.PositionEncodingKind.UTF16
end

---Invoke completion
---@param ctx cmp.Context
---@param callback function
---@return boolean? Return true if not trigger completion.
source.complete = function(self, ctx, callback)
  local offset = ctx:get_offset(self:get_keyword_pattern())

  -- NOTE: This implementation is nvim-cmp specific.
  -- We trigger new completion after core.confirm but we check only the symbol trigger_character in this case.
  local before_char = string.sub(ctx.cursor_before_line, -1)
  if ctx:get_reason() == types.cmp.ContextReason.TriggerOnly then
    before_char = string.match(ctx.cursor_before_line, '(.)%s*$')
    if not before_char or not char.is_symbol(string.byte(before_char)) then
      before_char = ''
    end
  end

  local completion_context
  if ctx:get_reason() == types.cmp.ContextReason.Manual then
    completion_context = {
      triggerKind = types.lsp.CompletionTriggerKind.Invoked,
    }
  elseif vim.tbl_contains(self:get_trigger_characters(), before_char) then
    completion_context = {
      triggerKind = types.lsp.CompletionTriggerKind.TriggerCharacter,
      triggerCharacter = before_char,
    }
  elseif ctx:get_reason() ~= types.cmp.ContextReason.TriggerOnly then
    if offset < ctx.cursor.col and self:get_keyword_length() <= (ctx.cursor.col - offset) then
      if self.incomplete and self.context.cursor.col ~= ctx.cursor.col and self.status ~= source.SourceStatus.FETCHING then
        completion_context = {
          triggerKind = types.lsp.CompletionTriggerKind.TriggerForIncompleteCompletions,
        }
      elseif not vim.tbl_contains({ self.request_offset, self.offset }, offset) then
        completion_context = {
          triggerKind = types.lsp.CompletionTriggerKind.Invoked,
        }
      end
    else
      self:reset() -- Should clear current completion if the TriggerKind isn't TriggerCharacter or Manual and keyword length does not enough.
    end
  else
    self:reset() -- Should clear current completion if ContextReason is TriggerOnly and the triggerCharacter isn't matched
  end

  -- Does not perform completions.
  if not completion_context then
    return
  end

  if completion_context.triggerKind == types.lsp.CompletionTriggerKind.TriggerCharacter then
    self.is_triggered_by_symbol = char.is_symbol(string.byte(completion_context.triggerCharacter))
  end

  debug.log(self:get_debug_name(), 'request', offset, vim.inspect(completion_context))
  local prev_status = self.status
  self.status = source.SourceStatus.FETCHING
  self.offset = offset
  self.request_offset = offset
  self.context = ctx
  self.default_replace_range = self:_get_default_replace_range()
  self.default_insert_range = self:_get_default_insert_range()
  self.position_encoding = self:get_position_encoding_kind()
  self.completion_context = completion_context
  self.source:complete(
    vim.tbl_extend('keep', misc.copy(self:get_source_config()), {
      offset = self.offset,
      context = ctx,
      completion_context = completion_context,
    }),
    self.complete_dedup(vim.schedule_wrap(function(response)
      if self.context ~= ctx then
        return
      end
      ---@type lsp.CompletionResponse
      response = response or {}
      self.response = response

      self.incomplete = response.isIncomplete or false

      if #(response.items or response) > 0 then
        debug.log(self:get_debug_name(), 'retrieve', #(response.items or response))
        local old_offset = self.offset
        local old_entries = self.entries

        self.status = source.SourceStatus.COMPLETED
        self.entries = {}
        for _, item in ipairs(response.items or response) do
          if item.label then
            local e = entry.new(ctx, self, item, response.itemDefaults)
            if not e:is_invalid() then
              table.insert(self.entries, e)
              self.offset = math.min(self.offset, e.offset)
            end
          end
        end
        self.revision = self.revision + 1
        if #self.entries == 0 then
          self.offset = old_offset
          self.entries = old_entries
          self.revision = self.revision + 1
        end
      else
        -- The completion will be invoked when pressing <CR> if the trigger characters contain the <Space>.
        -- If the server returns an empty response in such a case, should invoke the keyword completion on the next keypress.
        if offset == ctx.cursor.col then
          self:reset()
        end
        self.status = prev_status
      end
      callback()
    end))
  )
  return true
end

---Resolve CompletionItem
---@param item lsp.CompletionItem
---@param callback fun(item: lsp.CompletionItem)
source.resolve = function(self, item, callback)
  if not self.source.resolve then
    return callback(item)
  end
  self.source:resolve(item, function(resolved_item)
    callback(resolved_item or item)
  end)
end

---Execute command
---@param item lsp.CompletionItem
---@param callback fun()
source.execute = function(self, item, callback)
  if not self.source.execute then
    return callback()
  end
  self.source:execute(item, function()
    callback()
  end)
end

return source


================================================
FILE: lua/cmp/source_spec.lua
================================================
local config = require('cmp.config')
local spec = require('cmp.utils.spec')

local source = require('cmp.source')

describe('source', function()
  before_each(spec.before)

  describe('keyword length', function()
    it('not enough', function()
      config.set_buffer({
        completion = {
          keyword_length = 3,
        },
      }, vim.api.nvim_get_current_buf())

      local state = spec.state('', 1, 1)
      local s = source.new('spec', {
        complete = function(_, _, callback)
          callback({ { label = 'spec' } })
        end,
      })
      assert.is.truthy(not s:complete(state.input('a'), function() end))
    end)

    it('enough', function()
      config.set_buffer({
        completion = {
          keyword_length = 3,
        },
      }, vim.api.nvim_get_current_buf())

      local state = spec.state('', 1, 1)
      local s = source.new('spec', {
        complete = function(_, _, callback)
          callback({ { label = 'spec' } })
        end,
      })
      assert.is.truthy(s:complete(state.input('aiu'), function() end))
    end)

    it('enough -> not enough', function()
      config.set_buffer({
        completion = {
          keyword_length = 3,
        },
      }, vim.api.nvim_get_current_buf())

      local state = spec.state('', 1, 1)
      local s = source.new('spec', {
        complete = function(_, _, callback)
          callback({ { label = 'spec' } })
        end,
      })
      assert.is.truthy(s:complete(state.input('aiu'), function() end))
      assert.is.truthy(not s:complete(state.backspace(), function() end))
    end)

    it('continue', function()
      config.set_buffer({
        completion = {
          keyword_length = 3,
        },
      }, vim.api.nvim_get_current_buf())

      local state = spec.state('', 1, 1)
      local s = source.new('spec', {
        complete = function(_, _, callback)
          callback({ { label = 'spec' } })
        end,
      })
      assert.is.truthy(s:complete(state.input('aiu'), function() end))
      assert.is.truthy(not s:complete(state.input('eo'), function() end))
    end)
  end)

  describe('isIncomplete', function()
    it('isIncomplete=true', function()
      local state = spec.state('', 1, 1)
      local s = source.new('spec', {
        complete = function(_, _, callback)
          callback({
            items = { { label = 'spec' } },
            isIncomplete = true,
          })
        end,
      })
      vim.wait(100, function()
        return s.status == source.SourceStatus.COMPLETED
      end, 100, false)
      assert.is.truthy(s:complete(state.input('s'), function() end))
      vim.wait(100, function()
        return s.status == source.SourceStatus.COMPLETED
      end, 100, false)
      assert.is.truthy(s:complete(state.input('p'), function() end))
      vim.wait(100, function()
        return s.status == source.SourceStatus.COMPLETED
      end, 100, false)
      assert.is.truthy(s:complete(state.input('e'), function() end))
      vim.wait(100, function()
        return s.status == source.SourceStatus.COMPLETED
      end, 100, false)
      assert.is.truthy(s:complete(state.input('c'), function() end))
      vim.wait(100, function()
        return s.status == source.SourceStatus.COMPLETED
      end, 100, false)
    end)
  end)
end)


================================================
FILE: lua/cmp/types/cmp.lua
================================================
local cmp = {}

---@alias cmp.ConfirmBehavior 'insert' | 'replace'
cmp.ConfirmBehavior = {
  Insert = 'insert',
  Replace = 'replace',
}

---@alias cmp.SelectBehavior 'insert' | 'select'
cmp.SelectBehavior = {
  Insert = 'insert',
  Select = 'select',
}

---@alias cmp.ContextReason 'auto' | 'manual' | 'triggerOnly' | 'none'
cmp.ContextReason = {
  Auto = 'auto',
  Manual = 'manual',
  TriggerOnly = 'triggerOnly',
  None = 'none',
}

---@alias cmp.TriggerEvent 'InsertEnter' | 'TextChanged'
cmp.TriggerEvent = {
  InsertEnter = 'InsertEnter',
  TextChanged = 'TextChanged',
}

---@alias cmp.PreselectMode 'item' | 'None'
cmp.PreselectMode = {
  Item = 'item',
  None = 'none',
}

---@alias cmp.ItemField 'abbr' | 'icon' | 'kind' | 'menu'
cmp.ItemField = {
  Abbr = 'abbr',
  Icon = 'icon',
  Kind = 'kind',
  Menu = 'menu',
}

---@class cmp.ContextOption
---@field public reason cmp.ContextReason|nil

---@class cmp.ConfirmOption
---@field public behavior cmp.ConfirmBehavior
---@field public commit_character? string

---@class cmp.SelectOption
---@field public behavior cmp.SelectBehavior

---@class cmp.SnippetExpansionParams
---@field public body string
---@field public insert_text_mode integer

---@class cmp.CompleteParams
---@field public reason? cmp.ContextReason
---@field public config? cmp.ConfigSchema

---@class cmp.SetupProperty
---@field public buffer fun(c: cmp.ConfigSchema)
---@field public global fun(c: cmp.ConfigSchema)
---@field public cmdline fun(type: string|string[], c: cmp.ConfigSchema)
---@field public filetype fun(type: string|string[], c: cmp.ConfigSchema)

---@alias cmp.Setup cmp.SetupProperty | fun(c: cmp.ConfigSchema)

---@class cmp.SourceApiParams: cmp.SourceConfig

---@class cmp.SourceCompletionApiParams : cmp.SourceConfig
---@field public offset integer
---@field public context cmp.Context
---@field public completion_context lsp.CompletionContext

---@alias  cmp.MappingFunction fun(fallback: function): nil

---@class cmp.MappingClass
---@field public i nil|cmp.MappingFunction
---@field public c nil|cmp.MappingFunction
---@field public x nil|cmp.MappingFunction
---@field public s nil|cmp.MappingFunction

---@alias cmp.Mapping cmp.MappingFunction | cmp.MappingClass

---@class cmp.ConfigSchema
---@field private revision? integer
---@field public enabled? boolean | fun(): boolean
---@field public performance? cmp.PerformanceConfig
---@field public preselect? cmp.PreselectMode
---@field public completion? cmp.CompletionConfig
---@field public window? cmp.WindowConfig|nil
---@field public confirmation? cmp.ConfirmationConfig
---@field public matching? cmp.MatchingConfig
---@field public sorting? cmp.SortingConfig
---@field public formatting? cmp.FormattingConfig
---@field public snippet? cmp.SnippetConfig
---@field public mapping? table<string, cmp.Mapping>
---@field public sources? cmp.SourceConfig[]
---@field public view? cmp.ViewConfig
---@field public experimental? cmp.ExperimentalConfig

---@class cmp.PerformanceConfig
---@field public debounce integer
---@field public throttle integer
---@field public fetching_timeout integer
---@field public filtering_context_budget integer
---@field public confirm_resolve_timeout integer
---@field public async_budget integer Maximum time (in ms) an async function is allowed to run during one step of the event loop.
---@field public max_view_entries integer

---@class cmp.CompletionConfig
---@field public autocomplete? cmp.TriggerEvent[]|false
---@field public completeopt? string
---@field public get_trigger_characters? fun(trigger_characters: string[]): string[]
---@field public keyword_length? integer
---@field public keyword_pattern? string

---@class cmp.WindowConfig
---@field public completion? cmp.CompletionWindowOptions
---@field public documentation? cmp.DocumentationWindowOptions|vim.NIL

---@class cmp.WindowOptions
---@field public border? string|string[]
---@field public winhighlight? string
---@field public winblend? number
---@field public zindex? integer|nil

---@class cmp.CompletionWindowOptions: cmp.WindowOptions
---@field public scrolloff? integer|nil
---@field public col_offset? integer|nil
---@field public side_padding? integer|nil
---@field public scrollbar? boolean|true
---@field public max_height? integer|nil

---@class cmp.DocumentationWindowOptions: cmp.WindowOptions
---@field public max_height? integer|nil
---@field public max_width? integer|nil
---@field public scrolloff integer|nil
---@field public scrollbar boolean|true
---@field public col_offset integer|nil

---@class cmp.ConfirmationConfig
---@field public default_behavior cmp.ConfirmBehavior
---@field public get_commit_characters fun(commit_characters: string[]): string[]

---@class cmp.MatchingConfig
---@field public disallow_fuzzy_matching boolean
---@field public disallow_fullfuzzy_matching boolean
---@field public disallow_partial_fuzzy_matching boolean
---@field public disallow_partial_matching boolean
---@field public disallow_prefix_unmatching boolean
---@field public disallow_symbol_nonprefix_matching boolean

---@class cmp.SortingConfig
---@field public priority_weight integer
---@field public comparators cmp.Comparator[]

---@class cmp.FormattingConfig
---@field public fields? cmp.ItemField[]
---@field public expandable_indicator? boolean
---@field public format? fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem

---@class cmp.SnippetConfig
---@field public expand fun(args: cmp.SnippetExpansionParams)

---@class cmp.ExperimentalConfig
---@field public ghost_text cmp.GhostTextConfig|boolean

---@class cmp.GhostTextConfig
---@field hl_group string

---@class cmp.SourceConfig
---@field public name string
---@field public option table|nil
---@field public priority integer|nil
---@field public trigger_characters string[]|nil
---@field public keyword_pattern string|nil
---@field public keyword_length integer|nil
---@field public max_item_count integer|nil
---@field public group_index integer|nil
---@field public entry_filter nil|function(entry: cmp.Entry, ctx: cmp.Context): boolean

---@class cmp.ViewConfig
---@field public entries? cmp.EntriesViewConfig
---@field public docs? cmp.DocsViewConfig

---@alias cmp.EntriesViewConfig cmp.CustomEntriesViewConfig|cmp.NativeEntriesViewConfig|cmp.WildmenuEntriesViewConfig|string

---@class cmp.CustomEntriesViewConfig
---@field name 'custom'
---@field selection_order 'top_down'|'near_cursor'
---@field vertical_positioning 'auto'|'above'|'below'
---@field follow_cursor boolean

---@class cmp.NativeEntriesViewConfig
---@field name 'native'

---@class cmp.WildmenuEntriesViewConfig
---@field name 'wildmenu'
---@field separator string|nil

---@class cmp.DocsViewConfig
---@field public auto_open boolean

return cmp


================================================
FILE: lua/cmp/types/init.lua
================================================
local types = {}

types.cmp = require('cmp.types.cmp')
types.lsp = require('cmp.types.lsp')
types.vim = require('cmp.types.vim')

return types


================================================
FILE: lua/cmp/types/lsp.lua
================================================
local misc = require('cmp.utils.misc')

---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/
---@class lsp
local lsp = {}

---@enum lsp.PositionEncodingKind
lsp.PositionEncodingKind = {
  UTF8 = 'utf-8',
  UTF16 = 'utf-16',
  UTF32 = 'utf-32',
}

lsp.Position = {
  ---Convert lsp.Position to vim.Position
  ---@param buf integer
  ---@param position lsp.Position
  --
  ---@return vim.Position
  to_vim = function(buf, position)
    if not vim.api.nvim_buf_is_loaded(buf) then
      vim.fn.bufload(buf)
    end
    local lines = vim.api.nvim_buf_get_lines(buf, position.line, position.line + 1, false)
    if #lines > 0 then
      return {
        row = position.line + 1,
        col = misc.to_vimindex(lines[1], position.character),
      }
    end
    return {
      row = position.line + 1,
      col = position.character + 1,
    }
  end,
  ---Convert vim.Position to lsp.Position
  ---@param buf integer
  ---@param position vim.Position
  ---@return lsp.Position
  to_lsp = function(buf, position)
    if not vim.api.nvim_buf_is_loaded(buf) then
      vim.fn.bufload(buf)
    end
    local lines = vim.api.nvim_buf_get_lines(buf, position.row - 1, position.row, false)
    if #lines > 0 then
      return {
        line = position.row - 1,
        character = misc.to_utfindex(lines[1], position.col),
      }
    end
    return {
      line = position.row - 1,
      character = position.col - 1,
    }
  end,

  ---Convert position to utf8 from specified encoding.
  ---@param text string
  ---@param position lsp.Position
  ---@param from_encoding? lsp.PositionEncodingKind
  ---@return lsp.Position
  to_utf8 = function(text, position, from_encoding)
    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16
    if from_encoding == lsp.PositionEncodingKind.UTF8 then
      return position
    end

    local ok, byteindex = pcall(vim.str_byteindex, text, position.character, from_encoding == lsp.PositionEncodingKind.UTF16)
    if not ok then
      return position
    end
    return { line = position.line, character = byteindex }
  end,

  ---Convert position to utf16 from specified encoding.
  ---@param text string
  ---@param position lsp.Position
  ---@param from_encoding? lsp.PositionEncodingKind
  ---@return lsp.Position
  to_utf16 = function(text, position, from_encoding)
    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16
    if from_encoding == lsp.PositionEncodingKind.UTF16 then
      return position
    end

    local utf8 = lsp.Position.to_utf8(text, position, from_encoding)
    for index = utf8.character, 0, -1 do
      local ok, utf16index = pcall(function()
        return select(2, vim.str_utfindex(text, index))
      end)
      if ok then
        return { line = utf8.line, character = utf16index }
      end
    end
    return position
  end,

  ---Convert position to utf32 from specified encoding.
  ---@param text string
  ---@param position lsp.Position
  ---@param from_encoding? lsp.PositionEncodingKind
  ---@return lsp.Position
  to_utf32 = function(text, position, from_encoding)
    from_encoding = from_encoding or lsp.PositionEncodingKind.UTF16
    if from_encoding == lsp.PositionEncodingKind.UTF32 then
      return position
    end

    local utf8 = lsp.Position.to_utf8(text, position, from_encoding)
    for index = utf8.character, 0, -1 do
      local ok, utf32index = pcall(function()
        return select(1, vim.str_utfindex(text, index))
      end)
      if ok then
        return { line = utf8.line, character = utf32index }
      end
    end
    return position
  end,
}

lsp.Range = {
  ---Convert lsp.Range to vim.Range
  ---@param buf integer
  ---@param range lsp.Range
  ---@return vim.Range
  to_vim = function(buf, range)
    return {
      start = lsp.Position.to_vim(buf, range.start),
      ['end'] = lsp.Position.to_vim(buf, range['end']),
    }
  end,

  ---Convert vim.Range to lsp.Range
  ---@param buf integer
  ---@param range vim.Range
  ---@return lsp.Range
  to_lsp = function(buf, range)
    return {
      start = lsp.Position.to_lsp(buf, range.start),
      ['end'] = lsp.Position.to_lsp(buf, range['end']),
    }
  end,
}

---@alias lsp.CompletionTriggerKind 1 | 2 | 3
lsp.CompletionTriggerKind = {
  Invoked = 1,
  TriggerCharacter = 2,
  TriggerForIncompleteCompletions = 3,
}

---@alias lsp.InsertTextFormat 1 | 2
lsp.InsertTextFormat = {}
lsp.InsertTextFormat.PlainText = 1
lsp.InsertTextFormat.Snippet = 2

---@alias lsp.InsertTextMode 1 | 2
lsp.InsertTextMode = {
  AsIs = 1,
  AdjustIndentation = 2,
}

---@alias lsp.MarkupKind 'plaintext' | 'markdown'
lsp.MarkupKind = {
  PlainText = 'plaintext',
  Markdown = 'markdown',
}

---@alias lsp.CompletionItemTag 1
lsp.CompletionItemTag = {
  Deprecated = 1,
}

---@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
lsp.CompletionItemKind = {
  Text = 1,
  Method = 2,
  Function = 3,
  Constructor = 4,
  Field = 5,
  Variable = 6,
  Class = 7,
  Interface = 8,
  Module = 9,
  Property = 10,
  Unit = 11,
  Value = 12,
  Enum = 13,
  Keyword = 14,
  Snippet = 15,
  Color = 16,
  File = 17,
  Reference = 18,
  Folder = 19,
  EnumMember = 20,
  Constant = 21,
  Struct = 22,
  Event = 23,
  Operator = 24,
  TypeParameter = 25,
}
for _, k in ipairs(vim.tbl_keys(lsp.CompletionItemKind)) do
  local v = lsp.CompletionItemKind[k]
  lsp.CompletionItemKind[v] = k
end

---@class lsp.internal.CompletionItemDefaults
---@field public commitCharacters? string[]
---@field public editRange? lsp.Range | { insert: lsp.Range, replace: lsp.Range }
---@field public insertTextFormat? lsp.InsertTextFormat
---@field public insertTextMode? lsp.InsertTextMode
---@field public data? any

---@class lsp.CompletionContext
---@field public triggerKind lsp.CompletionTriggerKind
---@field public triggerCharacter string|nil

---@class lsp.CompletionList
---@field public isIncomplete boolean
---@field public itemDefaults? lsp.internal.CompletionItemDefaults
---@field public items lsp.CompletionItem[]

---@alias lsp.CompletionResponse lsp.CompletionList|lsp.CompletionItem[]

---@class lsp.MarkupContent
---@field public kind lsp.MarkupKind
---@field public value string

---@class lsp.Position
---@field public line integer
---@field public character integer

---@class lsp.Range
---@field public start lsp.Position
---@field public end lsp.Position

---@class lsp.Command
---@field public title string
---@field public command string
---@field public arguments any[]|nil

---@class lsp.TextEdit
---@field public range lsp.Range|nil
---@field public newText string

---@alias lsp.InsertReplaceTextEdit lsp.internal.InsertTextEdit|lsp.internal.ReplaceTextEdit

---@class lsp.internal.InsertTextEdit
---@field public insert lsp.Range
---@field public newText string

---@class lsp.internal.ReplaceTextEdit
---@field public replace lsp.Range
---@field public newText string

---@class lsp.CompletionItemLabelDetails
---@field public detail? string
---@field public description? string

---@class lsp.internal.CmpCompletionExtension
---@field public icon string
---@field public icon_hl_group string
---@field public kind_text string
---@field public kind_hl_group string

---@class lsp.CompletionItem
---@field public label string
---@field public labelDetails? lsp.CompletionItemLabelDetails
---@field public kind? lsp.CompletionItemKind
---@field public tags? lsp.CompletionItemTag[]
---@field public detail? string
---@field public documentation? lsp.MarkupContent|string
---@field public deprecated? boolean
---@field public preselect? boolean
---@field public sortText? string
---@field public filterText? string
---@field public insertText? string
---@field public insertTextFormat? lsp.InsertTextFormat
---@field public insertTextMode? lsp.InsertTextMode
---@field public textEdit? lsp.TextEdit|lsp.InsertReplaceTextEdit
---@field public textEditText? string
---@field public additionalTextEdits? lsp.TextEdit[]
---@field public commitCharacters? string[]
---@field public command? lsp.Command
---@field public data? any
---@field public cmp? lsp.internal.CmpCompletionExtension
---
---TODO: Should send the issue for upstream?
---@field public word string|nil
---@field public dup boolean|nil

return lsp


================================================
FILE: lua/cmp/types/lsp_spec.lua
================================================
local spec = require('cmp.utils.spec')
local lsp = require('cmp.types.lsp')

describe('types.lsp', function()
  before_each(spec.before)
  describe('Position', function()
    vim.fn.setline('1', {
      'あいうえお',
      'かきくけこ',
      'さしすせそ',
    })
    local vim_position, lsp_position

    local bufnr = vim.api.nvim_get_current_buf()
    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 3 })
    assert.are.equal(vim_position.row, 2)
    assert.are.equal(vim_position.col, 10)
    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)
    assert.are.equal(lsp_position.line, 1)
    assert.are.equal(lsp_position.character, 3)

    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 0 })
    assert.are.equal(vim_position.row, 2)
    assert.are.equal(vim_position.col, 1)
    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)
    assert.are.equal(lsp_position.line, 1)
    assert.are.equal(lsp_position.character, 0)

    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 5 })
    assert.are.equal(vim_position.row, 2)
    assert.are.equal(vim_position.col, 16)
    lsp_position = lsp.Position.to_lsp(bufnr, vim_position)
    assert.are.equal(lsp_position.line, 1)
    assert.are.equal(lsp_position.character, 5)

    -- overflow (lsp -> vim)
    vim_position = lsp.Position.to_vim(bufnr, { line = 1, character = 6 })
    assert.are.e
Download .txt
gitextract_18jfbkey/

├── .githooks/
│   └── pre-commit
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   └── workflows/
│       ├── format.yaml
│       ├── integration.yaml
│       └── release-please.yaml
├── .gitignore
├── .luacheckrc
├── LICENSE
├── Makefile
├── README.md
├── autoload/
│   └── cmp.vim
├── doc/
│   └── cmp.txt
├── init.sh
├── lua/
│   └── cmp/
│       ├── config/
│       │   ├── compare.lua
│       │   ├── context.lua
│       │   ├── default.lua
│       │   ├── mapping.lua
│       │   ├── sources.lua
│       │   └── window.lua
│       ├── config.lua
│       ├── context.lua
│       ├── context_spec.lua
│       ├── core.lua
│       ├── core_spec.lua
│       ├── entry.lua
│       ├── entry_spec.lua
│       ├── init.lua
│       ├── matcher.lua
│       ├── matcher_spec.lua
│       ├── source.lua
│       ├── source_spec.lua
│       ├── types/
│       │   ├── cmp.lua
│       │   ├── init.lua
│       │   ├── lsp.lua
│       │   ├── lsp_spec.lua
│       │   └── vim.lua
│       ├── utils/
│       │   ├── api.lua
│       │   ├── api_spec.lua
│       │   ├── async.lua
│       │   ├── async_spec.lua
│       │   ├── autocmd.lua
│       │   ├── binary.lua
│       │   ├── binary_spec.lua
│       │   ├── buffer.lua
│       │   ├── cache.lua
│       │   ├── char.lua
│       │   ├── debug.lua
│       │   ├── event.lua
│       │   ├── feedkeys.lua
│       │   ├── feedkeys_spec.lua
│       │   ├── highlight.lua
│       │   ├── keymap.lua
│       │   ├── keymap_spec.lua
│       │   ├── misc.lua
│       │   ├── misc_spec.lua
│       │   ├── options.lua
│       │   ├── pattern.lua
│       │   ├── snippet.lua
│       │   ├── spec.lua
│       │   ├── str.lua
│       │   ├── str_spec.lua
│       │   └── window.lua
│       ├── view/
│       │   ├── custom_entries_view.lua
│       │   ├── docs_view.lua
│       │   ├── ghost_text_view.lua
│       │   ├── native_entries_view.lua
│       │   └── wildmenu_entries_view.lua
│       ├── view.lua
│       └── vim_source.lua
├── nvim-cmp-scm-1.rockspec
├── plugin/
│   └── cmp.lua
├── stylua.toml
└── utils/
    └── vimrc.vim
Condensed preview — 73 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (344K chars).
[
  {
    "path": ".githooks/pre-commit",
    "chars": 201,
    "preview": "#!/bin/sh\n\nDIR=\"$(dirname $(dirname $( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )))\"\n\ncd $DIR\nmake p"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1955,
    "preview": "name: Bug Report\ndescription: Report a problem in nvim-cmp\nlabels: [bug]\nbody:\n  - type: checkboxes\n    id: faq-prerequi"
  },
  {
    "path": ".github/workflows/format.yaml",
    "chars": 481,
    "preview": "name: format\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - '**.lua'\n\njobs:\n  postprocessing:\n    runs-on: u"
  },
  {
    "path": ".github/workflows/integration.yaml",
    "chars": 708,
    "preview": "name: integration\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  integratio"
  },
  {
    "path": ".github/workflows/release-please.yaml",
    "chars": 310,
    "preview": "---\npermissions:\n  contents: write\n  pull-requests: write\n\nname: Release Please\n\non:\n  workflow_dispatch:\n  push:\n    br"
  },
  {
    "path": ".gitignore",
    "chars": 33,
    "preview": "doc/tags\nutils/stylua\n.DS_Store\n\n"
  },
  {
    "path": ".luacheckrc",
    "chars": 110,
    "preview": "globals = { 'vim', 'describe', 'it', 'before_each', 'after_each', 'assert', 'async' }\nmax_line_length = false\n"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2021 hrsh7th\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "Makefile",
    "chars": 202,
    "preview": ".PHONY: lint\nlint:\n\tluacheck ./lua\n\n.PHONY: test\ntest:\n\tvusted --output=gtest ./lua\n\n.PHONY: pre-commit\npre-commit:\n\tlua"
  },
  {
    "path": "README.md",
    "chars": 5029,
    "preview": "# nvim-cmp\n\nA completion engine plugin for neovim written in Lua.\nCompletion sources are installed from external reposit"
  },
  {
    "path": "autoload/cmp.vim",
    "chars": 2117,
    "preview": "let s:bridge_id = 0\nlet s:sources = {}\n\n\"\n\" cmp#register_source\n\"\nfunction! cmp#register_source(name, source) abort\n  le"
  },
  {
    "path": "doc/cmp.txt",
    "chars": 37170,
    "preview": "*nvim-cmp* *cmp*\n\nA completion plugin for neovim coded in Lua.\n\n========================================================"
  },
  {
    "path": "init.sh",
    "chars": 161,
    "preview": "DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nrm $DIR/.git/hooks/*\ncp $DIR/.githooks/* $DIR/."
  },
  {
    "path": "lua/cmp/config/compare.lua",
    "chars": 9029,
    "preview": "local types = require('cmp.types')\nlocal cache = require('cmp.utils.cache')\n\n---@type cmp.Comparator[]\nlocal compare = {"
  },
  {
    "path": "lua/cmp/config/context.lua",
    "chars": 1708,
    "preview": "local api = require('cmp.utils.api')\n\nlocal context = {}\n\n---Check if cursor is in syntax group\n---@param group string |"
  },
  {
    "path": "lua/cmp/config/default.lua",
    "chars": 3306,
    "preview": "local compare = require('cmp.config.compare')\nlocal types = require('cmp.types')\nlocal window = require('cmp.config.wind"
  },
  {
    "path": "lua/cmp/config/mapping.lua",
    "chars": 5093,
    "preview": "local types = require('cmp.types')\nlocal misc = require('cmp.utils.misc')\nlocal keymap = require('cmp.utils.keymap')\n\nlo"
  },
  {
    "path": "lua/cmp/config/sources.lua",
    "chars": 217,
    "preview": "return function(...)\n  local sources = {}\n  for i, group in ipairs({ ... }) do\n    for _, source in ipairs(group) do\n   "
  },
  {
    "path": "lua/cmp/config/window.lua",
    "chars": 833,
    "preview": "local window = {}\n\nwindow.bordered = function(opts)\n  opts = opts or {}\n  return {\n    border = opts.border or window.ge"
  },
  {
    "path": "lua/cmp/config.lua",
    "chars": 6634,
    "preview": "local mapping = require('cmp.config.mapping')\nlocal cache = require('cmp.utils.cache')\nlocal keymap = require('cmp.utils"
  },
  {
    "path": "lua/cmp/context.lua",
    "chars": 3125,
    "preview": "local misc = require('cmp.utils.misc')\nlocal pattern = require('cmp.utils.pattern')\nlocal types = require('cmp.types')\nl"
  },
  {
    "path": "lua/cmp/context_spec.lua",
    "chars": 926,
    "preview": "local spec = require('cmp.utils.spec')\n\nlocal context = require('cmp.context')\n\ndescribe('context', function()\n  before_"
  },
  {
    "path": "lua/cmp/core.lua",
    "chars": 16344,
    "preview": "local debug = require('cmp.utils.debug')\nlocal str = require('cmp.utils.str')\nlocal char = require('cmp.utils.char')\nloc"
  },
  {
    "path": "lua/cmp/core_spec.lua",
    "chars": 7172,
    "preview": "local spec = require('cmp.utils.spec')\nlocal feedkeys = require('cmp.utils.feedkeys')\nlocal types = require('cmp.types')"
  },
  {
    "path": "lua/cmp/entry.lua",
    "chars": 22252,
    "preview": "local cache = require('cmp.utils.cache')\nlocal char = require('cmp.utils.char')\nlocal misc = require('cmp.utils.misc')\nl"
  },
  {
    "path": "lua/cmp/entry_spec.lua",
    "chars": 9582,
    "preview": "local spec = require('cmp.utils.spec')\n\nlocal entry = require('cmp.entry')\n\ndescribe('entry', function()\n  before_each(s"
  },
  {
    "path": "lua/cmp/init.lua",
    "chars": 10843,
    "preview": "local core = require('cmp.core')\nlocal source = require('cmp.source')\nlocal config = require('cmp.config')\nlocal feedkey"
  },
  {
    "path": "lua/cmp/matcher.lua",
    "chars": 10613,
    "preview": "local char = require('cmp.utils.char')\n\nlocal matcher = {}\n\nmatcher.WORD_BOUNDALY_ORDER_FACTOR = 10\n\nmatcher.PREFIX_FACT"
  },
  {
    "path": "lua/cmp/matcher_spec.lua",
    "chars": 5585,
    "preview": "local spec = require('cmp.utils.spec')\nlocal default_config = require('cmp.config.default')\n\nlocal matcher = require('cm"
  },
  {
    "path": "lua/cmp/source.lua",
    "chars": 12882,
    "preview": "local context = require('cmp.context')\nlocal config = require('cmp.config')\nlocal entry = require('cmp.entry')\nlocal deb"
  },
  {
    "path": "lua/cmp/source_spec.lua",
    "chars": 3286,
    "preview": "local config = require('cmp.config')\nlocal spec = require('cmp.utils.spec')\n\nlocal source = require('cmp.source')\n\ndescr"
  },
  {
    "path": "lua/cmp/types/cmp.lua",
    "chars": 6730,
    "preview": "local cmp = {}\n\n---@alias cmp.ConfirmBehavior 'insert' | 'replace'\ncmp.ConfirmBehavior = {\n  Insert = 'insert',\n  Replac"
  },
  {
    "path": "lua/cmp/types/init.lua",
    "chars": 143,
    "preview": "local types = {}\n\ntypes.cmp = require('cmp.types.cmp')\ntypes.lsp = require('cmp.types.lsp')\ntypes.vim = require('cmp.typ"
  },
  {
    "path": "lua/cmp/types/lsp.lua",
    "chars": 8304,
    "preview": "local misc = require('cmp.utils.misc')\n\n---@see https://microsoft.github.io/language-server-protocol/specifications/spec"
  },
  {
    "path": "lua/cmp/types/lsp_spec.lua",
    "chars": 1696,
    "preview": "local spec = require('cmp.utils.spec')\nlocal lsp = require('cmp.types.lsp')\n\ndescribe('types.lsp', function()\n  before_e"
  },
  {
    "path": "lua/cmp/types/vim.lua",
    "chars": 677,
    "preview": "---@class vim.CompletedItem\n---@field public word string\n---@field public abbr string|nil\n---@field public icon string|n"
  },
  {
    "path": "lua/cmp/utils/api.lua",
    "chars": 2375,
    "preview": "local api = {}\n\nlocal CTRL_V = vim.api.nvim_replace_termcodes('<C-v>', true, true, true)\nlocal CTRL_S = vim.api.nvim_rep"
  },
  {
    "path": "lua/cmp/utils/api_spec.lua",
    "chars": 2233,
    "preview": "local spec = require('cmp.utils.spec')\nlocal keymap = require('cmp.utils.keymap')\nlocal feedkeys = require('cmp.utils.fe"
  },
  {
    "path": "lua/cmp/utils/async.lua",
    "chars": 6719,
    "preview": "local feedkeys = require('cmp.utils.feedkeys')\nlocal config = require('cmp.config')\n\nlocal async = {}\n\n---@class cmp.Asy"
  },
  {
    "path": "lua/cmp/utils/async_spec.lua",
    "chars": 1579,
    "preview": "local async = require('cmp.utils.async')\n\ndescribe('utils.async', function()\n  it('throttle', function()\n    local count"
  },
  {
    "path": "lua/cmp/utils/autocmd.lua",
    "chars": 1801,
    "preview": "local debug = require('cmp.utils.debug')\n\nlocal autocmd = {}\n\nautocmd.group = vim.api.nvim_create_augroup('___cmp___', {"
  },
  {
    "path": "lua/cmp/utils/binary.lua",
    "chars": 715,
    "preview": "local binary = {}\n\n---Insert item to list to ordered index\n---@param list any[]\n---@param item any\n---@param func fun(a:"
  },
  {
    "path": "lua/cmp/utils/binary_spec.lua",
    "chars": 1048,
    "preview": "local binary = require('cmp.utils.binary')\n\ndescribe('utils.binary', function()\n  it('insort', function()\n    local func"
  },
  {
    "path": "lua/cmp/utils/buffer.lua",
    "chars": 531,
    "preview": "local buffer = {}\n\nbuffer.cache = {}\n\n---@return integer buf\nbuffer.get = function(name)\n  local buf = buffer.cache[name"
  },
  {
    "path": "lua/cmp/utils/cache.lua",
    "chars": 1142,
    "preview": "---@class cmp.Cache\n---@field public entries any\nlocal cache = {}\n\ncache.new = function()\n  local self = setmetatable({}"
  },
  {
    "path": "lua/cmp/utils/char.lua",
    "chars": 2456,
    "preview": "local _\n\nlocal alpha = {}\n_ = string.gsub('abcdefghijklmnopqrstuvwxyz', '.', function(char)\n  alpha[string.byte(char)] ="
  },
  {
    "path": "lua/cmp/utils/debug.lua",
    "chars": 380,
    "preview": "local debug = {}\n\ndebug.flag = false\n\n---Print log\n---@vararg any\ndebug.log = function(...)\n  if debug.flag then\n    loc"
  },
  {
    "path": "lua/cmp/utils/event.lua",
    "chars": 1101,
    "preview": "---@class cmp.Event\n---@field private events table<string, function[]>\nlocal event = {}\n\n---Create vents\nevent.new = fun"
  },
  {
    "path": "lua/cmp/utils/feedkeys.lua",
    "chars": 1867,
    "preview": "local keymap = require('cmp.utils.keymap')\nlocal misc = require('cmp.utils.misc')\n\nlocal feedkeys = {}\n\nfeedkeys.call = "
  },
  {
    "path": "lua/cmp/utils/feedkeys_spec.lua",
    "chars": 1617,
    "preview": "local spec = require('cmp.utils.spec')\nlocal keymap = require('cmp.utils.keymap')\n\nlocal feedkeys = require('cmp.utils.f"
  },
  {
    "path": "lua/cmp/utils/highlight.lua",
    "chars": 623,
    "preview": "local highlight = {}\n\nhighlight.keys = {\n  'fg',\n  'bg',\n  'bold',\n  'italic',\n  'reverse',\n  'standout',\n  'underline',"
  },
  {
    "path": "lua/cmp/utils/keymap.lua",
    "chars": 7300,
    "preview": "local misc = require('cmp.utils.misc')\nlocal buffer = require('cmp.utils.buffer')\nlocal api = require('cmp.utils.api')\n\n"
  },
  {
    "path": "lua/cmp/utils/keymap_spec.lua",
    "chars": 6347,
    "preview": "local spec = require('cmp.utils.spec')\nlocal api = require('cmp.utils.api')\nlocal feedkeys = require('cmp.utils.feedkeys"
  },
  {
    "path": "lua/cmp/utils/misc.lua",
    "chars": 5598,
    "preview": "local misc = {}\n\nlocal islist = vim.islist or vim.tbl_islist\n\n---Create once callback\n---@param callback function\n---@re"
  },
  {
    "path": "lua/cmp/utils/misc_spec.lua",
    "chars": 1533,
    "preview": "local spec = require('cmp.utils.spec')\n\nlocal misc = require('cmp.utils.misc')\n\ndescribe('misc', function()\n  before_eac"
  },
  {
    "path": "lua/cmp/utils/options.lua",
    "chars": 737,
    "preview": "local M = {}\n\n-- Set window option without triggering the OptionSet event\n---@param window number\n---@param name string\n"
  },
  {
    "path": "lua/cmp/utils/pattern.lua",
    "chars": 493,
    "preview": "local pattern = {}\n\npattern._regexes = {}\n\npattern.regex = function(p)\n  if not pattern._regexes[p] then\n    pattern._re"
  },
  {
    "path": "lua/cmp/utils/snippet.lua",
    "chars": 10413,
    "preview": "local misc = require('cmp.utils.misc')\n\nlocal P = {}\n\n---Take characters until the target characters (The escape sequenc"
  },
  {
    "path": "lua/cmp/utils/spec.lua",
    "chars": 2360,
    "preview": "local context = require('cmp.context')\nlocal source = require('cmp.source')\nlocal types = require('cmp.types')\nlocal con"
  },
  {
    "path": "lua/cmp/utils/str.lua",
    "chars": 4596,
    "preview": "local char = require('cmp.utils.char')\n\nlocal str = {}\n\nlocal INVALIDS = {}\nINVALIDS[string.byte(\"'\")] = true\nINVALIDS[s"
  },
  {
    "path": "lua/cmp/utils/str_spec.lua",
    "chars": 2830,
    "preview": "local str = require('cmp.utils.str')\n\ndescribe('utils.str', function()\n  it('get_word', function()\n    assert.are.equal("
  },
  {
    "path": "lua/cmp/utils/window.lua",
    "chars": 9868,
    "preview": "local misc = require('cmp.utils.misc')\nlocal opt = require('cmp.utils.options')\nlocal buffer = require('cmp.utils.buffer"
  },
  {
    "path": "lua/cmp/view/custom_entries_view.lua",
    "chars": 16849,
    "preview": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal feedkeys = require('cmp.util"
  },
  {
    "path": "lua/cmp/view/docs_view.lua",
    "chars": 4409,
    "preview": "local window = require('cmp.utils.window')\nlocal config = require('cmp.config')\n\n---@class cmp.DocsView\n---@field public"
  },
  {
    "path": "lua/cmp/view/ghost_text_view.lua",
    "chars": 3971,
    "preview": "local config = require('cmp.config')\nlocal misc = require('cmp.utils.misc')\nlocal snippet = require('cmp.utils.snippet')"
  },
  {
    "path": "lua/cmp/view/native_entries_view.lua",
    "chars": 5107,
    "preview": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal keymap = require('cmp.utils."
  },
  {
    "path": "lua/cmp/view/wildmenu_entries_view.lua",
    "chars": 8104,
    "preview": "local event = require('cmp.utils.event')\nlocal autocmd = require('cmp.utils.autocmd')\nlocal feedkeys = require('cmp.util"
  },
  {
    "path": "lua/cmp/view.lua",
    "chars": 9039,
    "preview": "local config = require('cmp.config')\nlocal async = require('cmp.utils.async')\nlocal event = require('cmp.utils.event')\nl"
  },
  {
    "path": "lua/cmp/vim_source.lua",
    "chars": 1188,
    "preview": "local misc = require('cmp.utils.misc')\n\nlocal vim_source = {}\n\n---@param id integer\n---@param args any[]\nvim_source.on_c"
  },
  {
    "path": "nvim-cmp-scm-1.rockspec",
    "chars": 628,
    "preview": "local MODREV, SPECREV = 'scm', '-1'\nrockspec_format = '3.0'\npackage = 'nvim-cmp'\nversion = MODREV .. SPECREV\n\ndescriptio"
  },
  {
    "path": "plugin/cmp.lua",
    "chars": 3023,
    "preview": "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('["
  },
  {
    "path": "stylua.toml",
    "chars": 93,
    "preview": "indent_type = \"Spaces\"\nindent_width = 2\ncolumn_width = 1200\nquote_style = \"AutoPreferSingle\"\n"
  },
  {
    "path": "utils/vimrc.vim",
    "chars": 1143,
    "preview": "if has('vim_starting')\n  set encoding=utf-8\nendif\nscriptencoding utf-8\n\nif &compatible\n  set nocompatible\nendif\n\nlet s:p"
  }
]

About this extraction

This page contains the full source code of the hrsh7th/nvim-cmp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 73 files (320.6 KB), approximately 85.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!