hrsh7th / cmp-cmdline

nvim-cmp source for vim's cmdline
MIT License
533 stars 40 forks source link

"E141: No file name for buffer nn" on attempt to exit #11

Open hirevue-brent opened 2 years ago

hirevue-brent commented 2 years ago

Disclaimer: I'm not super familiar with lua or vimscript or the inner workings of vim. I just use vim a lot.

I recently setup neovim with nvim-cmp (love it! great work!). However, when I enable cmp-cmdline I can no longer exit vim with :wqa. I get the following error:

E141: No file name for buffer nn

Then I essentially have to :q! everything individually until I hit the last buffer which appears to be the options listed for cmp-cmdline.

I wanted to guess that the buffer wasn't set to nofile, but I cannot find where that would even happen. Is very possible its outside this plugin completely. However, when I disable this plugin I have no problem.

Steps to Repro:

  1. Set-up nvim-cmp using the recommended configuration here: https://github.com/hrsh7th/nvim-cmp
  2. Open two files (using :vs or :spl)
  3. Try to exit using :wqa (or :qa or :qa!)

Notice

E141: No file name for buffer 2 is shown

Expected

Vim would exit

hrsh7th commented 2 years ago

I can't reproduce this issue.

https://user-images.githubusercontent.com/629908/139784957-bfd480bf-76f5-44e8-881f-8acf9c017d74.mp4

hirevue-brent commented 2 years ago

It seems to be an issue with the following config (I have the lines commented out that cause me trouble):

-- Use cmdline & path source for ':'.
cmp.setup.cmdline(':', {             
  sources = cmp.config.sources({     
    { name = 'path' }                
  -- }, {                            
  --  { name = 'cmdline' }           
  })                                 
})                                   

My minimal init.vim looks like this (just like the suggestion on the website, but with things commented out to avoid problem):

call plug#begin('~/.config/nvim/plugged')            

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'                              

Plug 'hrsh7th/cmp-vsnip'                             
Plug 'hrsh7th/vim-vsnip'                             

call plug#end()                                      

set completeopt=menu,menuone,noselect                

lua <<EOF                                                                                                                              
  -- Setup nvim-cmp.                                                                                                                   
  local cmp = require'cmp'                                                                                                             

  cmp.setup({                                                                                                                          
    snippet = {                                                                                                                        
      expand = function(args)                                                                                                          
        vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.                                                                     
        -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users.                                                            
        -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.                                                               
        -- require'snippy'.expand_snippet(args.body) -- For `snippy` users.                                                            
      end,                                                                                                                             
    },                                                                                                                                 
    mapping = {                                                                                                                        
      ['<C-d>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }),                                                              
      ['<C-f>'] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }),                                                               
      ['<C-Space>'] = cmp.mapping(cmp.mapping.complete(), { 'i', 'c' }),                                                               
      ['<C-y>'] = cmp.config.disable, -- If you want to remove the default `<C-y>` mapping, You can specify `cmp.config.disable` value.
      ['<C-e>'] = cmp.mapping({                                                                                                        
        i = cmp.mapping.abort(),                                                                                                       
        c = cmp.mapping.close(),                                                                                                       
      }),                                                                                                                              
      ['<CR>'] = cmp.mapping.confirm({ select = true }),                                                                               
    },                                                                                                                                 
    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' },                                                                                                             
    })                                                                                                                                 
  })                                                                                                                                   

  -- Use buffer source for `/`.                                                                                                        
  cmp.setup.cmdline('/', {                                                                                                             
    sources = {                                                                                                                        
      { name = 'buffer' }                                                                                                              
    }                                                                                                                                  
  })                                                                                                                                   

  -- Use cmdline & path source for ':'.                                                                                                
  cmp.setup.cmdline(':', {                                                                                                             
    sources = cmp.config.sources({                                                                                                     
      { name = 'path' }                                                                                                                
    -- }, {                                                                                                                            
    --  { name = 'cmdline' }                                                                                                           
    })                                                                                                                                 
  })                                                                                                                                   

  -- Setup lspconfig.                                                                                                                  
  local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())                        
  capabilities.textDocument.completion.completionItem.snippetSupport = true                                                            
  require('lspconfig').tsserver.setup {                                                                                                
    capabilities = capabilities                                                                                                        
  }                                                                                                                                    
  require('lspconfig').html.setup {                                                                                                    
    capabilities = capabilities                                                                                                        
  }                                                                                                                                    
EOF                                                                                                                                    
pda commented 2 years ago

I have the same issue with cmp, but not specifically cmp-cmdline. I'll post it here for now because I suspect it's the same underlying issue in cmp. Please let me know if you'd rather I open a new issue elsewhere.

When I :wall as part of my workflow, I get the same E141 re trying to save a buffer without a filename:

E141: No file name for buffer 24

:ls / :buffers does not list it, but :ls! / :buffers! does:

24u h + "[No Name]"                    line 1

I see the u means “unlisted buffers”, which explains why it doesn't list normally. And h is hidden (backgrounded), + is modified, and it has no filename.

When I switch to it with :b24 I see content like this:

func(s string) int
─────────────────────────────────────────────
"Safe" parseint for parsing ANSI instructions

That's the second-level popover describing an auto-complete option from LSP, for example:

image

On this occasion it's Go's gopls, but I have the same issue with all the LSP backends I use (e.g. Go, Ruby, Rust). I think the only unusual part of my workflow is frequent :wall while triggering async tests.

I'm happy to do more debugging, but I'm not very familiar with nvim/cmp internals.

Here's the LSP/cmp relevant part of my init.lua ``` ---------- -- plugins local lspconfig = require("lspconfig") local capabilities = require("cmp_nvim_lsp").update_capabilities(vim.lsp.protocol.make_client_capabilities()) for _, server in ipairs({ "gopls", "rust_analyzer", "yamlls", "solargraph" }) do lspconfig[server].setup { capabilities = capabilities } end -- vim-go require("lspconfig").gopls.setup{} vim.g.go_auto_sameids = 1 -- highlight other instances of identifier under cursor vim.g.go_updatetime = 200 -- delay (ms) for sameids, type_info etc (default 800) vim.g.go_gopls_complete_unimported = 1 -- include suggestions from unimported packages -- fzf vim.api.nvim_set_keymap("n", "", ":Files", {}) -- vim-asm_ca65 vim.g.asm_ca65_wdc = true vim.cmd "filetype plugin indent on" vim.cmd [[ augroup filetypedetect autocmd BufEnter *.s setlocal filetype=asm_ca65 colorcolumn=17,41 autocmd BufEnter *.s highlight ColorColumn ctermbg=232 autocmd BufLeave *.s highlight ColorColumn ctermbg=0 augroup END ]] -- rust.vim vim.g.rustfmt_autosave = true require("lspconfig").rust_analyzer.setup{} -- redhat-developer/yaml-language-store require("lspconfig").yamlls.setup { settings = { yaml = { {schemaStore = {enable = true}}, }, } } -- ruby LSP require("lspconfig").solargraph.setup {} vim.opt.completeopt = "menu,menuone,noselect" local cmp = require("cmp") local luasnip = require("luasnip") cmp.setup({ snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = { [""] = cmp.mapping.select_prev_item(), [""] = cmp.mapping.select_next_item(), [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.complete(), [""] = cmp.mapping.close(), -- [""] = cmp.mapping.confirm { -- behavior = cmp.ConfirmBehavior.Replace, -- select = true, -- }, [''] = function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.expand_or_jumpable() then luasnip.expand_or_jump() else fallback() end end, [''] = function(fallback) if cmp.visible() then cmp.select_prev_item() elseif luasnip.jumpable(-1) then luasnip.jump(-1) else fallback() end end, }, sources = cmp.config.sources({ { name = "nvim_lsp" }, { name = 'luasnip' }, { name = "buffer", option = { get_bufnrs = function() return vim.api.nvim_list_bufs() end }}, }) }) ```
pda commented 2 years ago

Hrmm, I can't reproduce it after restarting vim, but this does hit me most days.

My guess is that an unlisted scratch buffer is somehow losing its buftype=nofile Scratch status, and so it becomes a normal dirty buffer that requires saving before close. I don't know how it would be losing buftype=nofile though. Maybe some kind of race condition?

I had been trying to fix it by :bd 24 the buffer, but finding it stuck again the next time an auto-complete pops up. Having read the :bdelete manual more closely, that makes sense:

Actually, the buffer isn't completely deleted, it is removed from the buffer list |unlisted-buffer| and option values, variables and mappings/abbreviations for the buffer are cleared.

So for an already-unlisted buffer, all it was doing was clearing the unsaved changes, but presumably the same buffer gets reused for the next completion, and it remains in non-scratch state.

I suppose :bwipeout would be the more effective workaround short of restarting neovim.

I'll keep looking around and see what I can figure out.

(And apologies in advance if it turns out to be a problem unrelated to cmp…)