Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't cycle through history with Ctrl-N and Ctrl-P in command-line mode #108

Open
yangmillstheory opened this issue Jan 29, 2024 · 10 comments

Comments

@yangmillstheory
Copy link

Hello,

I can't use Ctrl-P to cycle through my ex-command history, I think due to some poor interaction with this plugin. For example, when type : and then repeatedly hit Ctrl-P, this happens:

Screen.Recording.2024-01-29.at.22.10.29.mov

When I comment out the cmdline source (see the top right pane), instead I can do:

Screen.Recording.2024-01-29.at.22.11.56.mov

Here's some information about my setup:

:set
--- Options ---
  cmdheight=0         cursorcolumn        filetype=lua        lines=63            relativenumber    noshowmode            suffixesadd=.lua    termguicolors
  columns=166         cursorline          helplang=en       noloadplugins         scroll=10           showtabline=2     noswapfile            window=62
  comments=:--        expandtab           inccommand=split    number              shiftwidth=2        softtabstop=2       tabstop=2         nowrap
  commentstring=-- %s
  completeopt=menu,menuone,noselect
  define=\<function\|\<local\%(\s\+function\)\=
  fileencoding=utf-8
  formatexpr=v:lua.vim.lsp.formatexpr
  formatoptions=jcroql
  includeexpr=tr(v:fname,'.','/')
  indentexpr=GetLuaIndent()
  indentkeys=0{,0},0),0],:,0#,!^F,o,O,e,0=end,0=until
  omnifunc=v:lua.vim.lsp.omnifunc
  operatorfunc=<SNR>14_go
  packpath=~/code/nvim-macos/share/nvim/runtime
  runtimepath=~/.config/nvim,~/.local/share/nvim/lazy/lazy.nvim,~/.local/share/nvim/lazy/indent-blankline.nvim,~/.local/share/nvim/lazy/fzf,~/.local/share/nvim/lazy/v
im-obsession,~/.local/share/nvim/lazy/cmp-nvim-lsp,~/.local/share/nvim/lazy/lspkind.nvim,~/.local/share/nvim/lazy/trouble.nvim,~/.local/share/nvim/lazy/nvim-cmp,~/.lo
cal/share/nvim/lazy/vim-vsnip,~/.local/share/nvim/lazy/nvim-treesitter,~/.local/share/nvim/lazy/vim-tmux-navigator,~/.local/share/nvim/lazy/vim-commentary,~/.local/sh
are/nvim/lazy/cmp-path,~/.local/share/nvim/lazy/nvim-lspconfig,~/.local/share/nvim/lazy/bufferline.nvim,~/.local/share/nvim/lazy/cmp-buffer,~/.local/share/nvim/lazy/c
mp-vsnip,~/.local/share/nvim/lazy/nvim-surround,~/.local/share/nvim/lazy/vim-move,~/.local/share/nvim/lazy/cmp-cmdline,~/.local/share/nvim/lazy/kanagawa.nvim,~/.local
/share/nvim/lazy/cmp-emoji,~/.local/share/nvim/lazy/nvim-autopairs,~/.local/share/nvim/lazy/cmp-nvim-lua,~/.local/share/nvim/lazy/nvim-web-devicons,~/.local/share/nvi
m/lazy/lualine.nvim,~/.local/share/nvim/lazy/vim-snipe,~/.local/share/nvim/lazy/fzf.vim,~/.local/share/nvim/lazy/vim-tmux,~/code/nvim-macos/share/nvim/runtime,~/code/
nvim-macos/share/nvim/runtime/pack/dist/opt/matchit,~/code/nvim-macos/lib/nvim,~/.local/share/nvim/lazy/indent-blankline.nvim/after,~/.local/share/nvim/lazy/cmp-nvim-
lsp/after,~/.local/share/nvim/lazy/cmp-path/after,~/.local/share/nvim/lazy/cmp-buffer/after,~/.local/share/nvim/lazy/cmp-vsnip/after,~/.local/share/nvim/lazy/cmp-cmdl
ine/after,~/.local/share/nvim/lazy/cmp-emoji/after,~/.local/share/nvim/lazy/cmp-nvim-lua/after,~/.config/nvim/after,~/.local/state/nvim/lazy/readme
  shortmess=linFTotfOcx
  spelloptions=noplainbuffer
  statusline=%#lualine_a_command# COMMAND %#lualine_transitional_lualine_a_command_to_lualine_b_command#%#lualine_b_command# [$] %#lualine_transitional_lualine_b_com
mand_to_lualine_c_normal#%<%#lualine_c_normal# nvim/.config/nvim/lua/settings.lua %#lualine_c_normal#%=%#lualine_c_normal# utf-8 %#lualine_c_normal#  %#lualine_x_
filetype_DevIconLua_command# %#lualine_c_normal# lua %#lualine_transitional_lualine_b_command_to_lualine_c_normal#%#lualine_b_command# 64%% %#lualine_transitional_l
ualine_a_command_to_lualine_b_command#%#lualine_a_command#  18:1
  tagfunc=v:lua.vim.lsp.tagfunc
  tabline=%!v:lua.nvim_bufferline()
  wildignore=*.o,*.a,*.obj,__pycache__
Press ENTER or type command to continue

Also here's the result of :checkhealth:


==============================================================================
lazy: require("lazy.health").check()

lazy.nvim ~
- OK Git installed
- OK no existing packages found by other package managers
- OK packer_compiled.lua not found

==============================================================================
nvim: require("nvim.health").check()

Configuration ~
- OK no issues found

Runtime ~
- OK $VIMRUNTIME: /Users/yangmillstheory/code/nvim-macos/share/nvim/runtime

Performance ~
- OK Build type: Release

Remote Plugins ~
- OK Up to date

terminal ~
- key_backspace (kbs) terminfo entry: `key_backspace=\177`
- key_dc (kdch1) terminfo entry: `key_dc=\E[3~`
- $TERM_PROGRAM="tmux"
- $COLORTERM="truecolor"

tmux ~
- OK escape-time: 1
- OK focus-events: on
- $TERM: alacritty
- ERROR $TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.
  - ADVICE:
    - Set default-terminal in ~/.tmux.conf:
      set-option -g default-terminal "screen-256color"
    - https://github.com/neovim/neovim/wiki/Building-Neovim#optimized-builds

==============================================================================
nvim-treesitter: require("nvim-treesitter.health").check()

Installation ~
- WARNING `tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar, not required for :TSInstall)
- WARNING `node` executable not found (only needed for :TSInstallFromGrammar, not required for :TSInstall)
- OK `git` executable found.
- OK `cc` executable found. Selected from { vim.NIL, "cc", "gcc", "clang", "cl", "zig" }
  Version: Apple clang version 15.0.0 (clang-1500.1.0.2.5)
- OK Neovim was compiled with tree-sitter runtime ABI version 14 (required >=13). Parsers must be compatible with runtime ABI.

OS Info:
{
  machine = "x86_64",
  release = "22.6.0",
  sysname = "Darwin",
  version = "Darwin Kernel Version 22.6.0: Sun Dec 17 22:18:09 PST 2023; root:xnu-8796.141.3.703.2~2/RELEASE_X86_64"
} ~

Parser/Features         H L F I J
  - c                   ✓ ✓ ✓ ✓ ✓
  - cpp                 ✓ ✓ ✓ ✓ ✓
  - go                  ✓ ✓ ✓ ✓ ✓
  - lua                 ✓ ✓ ✓ ✓ ✓
  - python              ✓ ✓ ✓ ✓ ✓
  - query               ✓ ✓ ✓ ✓ ✓
  - vim                 ✓ ✓ ✓ . ✓
  - vimdoc              x . . . ✓

  Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections
         +) multiple parsers found, only one will be used
         x) errors found in the query, try to run :TSUpdate {lang} ~

The following errors have been detected: ~
- ERROR vimdoc(highlights): ...im-macos/share/nvim/runtime/lua/vim/treesitter/query.lua:259: query: invalid node type at position 746 for language vimdoc
  vimdoc(highlights) is concatenated from the following files:
  | [ERROR]:"/Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/queries/vimdoc/highlights.scm", failed to load: ...im-macos/share/nvim/runtime/lua/vim/treesitter/query.lua:259: query: invalid node type at position 746 for language vimdoc

==============================================================================
provider: health#provider#check

- ERROR Failed to run healthcheck for "provider" plugin. Exception:
  function health#check[25]..health#provider#check[4]..<SNR>88_check_ruby, line 15
  Vim(let):E117: Unknown function: provider#ruby#Detect

==============================================================================
vim.lsp: require("vim.lsp.health").check()

- LSP log level : WARN
- Log path: /Users/yangmillstheory/.local/state/nvim/lsp.log
- Log size: 0 KB

vim.lsp: Active Clients ~
- lua_ls (id=1, root_dir=/Users/yangmillstheory/code/dotfiles/nvim/.config/nvim/lua/)

==============================================================================
vim.treesitter: require("vim.treesitter.health").check()

- Nvim runtime ABI version: 14
- OK Parser: cpp        ABI: 14, path: /Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/parser/cpp.so
- OK Parser: go         ABI: 14, path: /Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/parser/go.so
- OK Parser: lua        ABI: 14, path: /Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/parser/lua.so
- OK Parser: python     ABI: 14, path: /Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/parser/python.so
- OK Parser: vim        ABI: 14, path: /Users/yangmillstheory/.local/share/nvim/lazy/nvim-treesitter/parser/vim.so
- OK Parser: c          ABI: 14, path: /Users/yangmillstheory/code/nvim-macos/lib/nvim/parser/c.so
- OK Parser: lua        ABI: 14, path: /Users/yangmillstheory/code/nvim-macos/lib/nvim/parser/lua.so
- OK Parser: query      ABI: 14, path: /Users/yangmillstheory/code/nvim-macos/lib/nvim/parser/query.so
- OK Parser: vim        ABI: 14, path: /Users/yangmillstheory/code/nvim-macos/lib/nvim/parser/vim.so
- OK Parser: vimdoc     ABI: 14, path: /Users/yangmillstheory/code/nvim-macos/lib/nvim/parser/vimdoc.so

When I run :cmap, it looks like the binding comes from nvim-cmp:

image

However, commenting out the cmdline source fixes the issue, so I'm filing the bug here. Please let me know how I can help.

@adinhodovic
Copy link

I just did disabled the built in mappings for the cmdline preset.

			cmp.setup.cmdline(":", {
				mapping = cmp.mapping.preset.cmdline({
					-- Use default nvim history scrolling
					["<C-n>"] = {
						c = false,
					},
					["<C-p>"] = {
						c = false,
					},
				}),
				sources = cmp.config.sources({
					{ name = "path" },
				}, {
					{ name = "cmdline" },
				}),
			})

Might solve ur usecase. Nvim Config

@yangmillstheory
Copy link
Author

Thanks @adinhodovic, this looks like it works with no observable downsides. I wonder what the intent of the C-n / C-p mappings that we're overriding were? Because I guess the downside of the override, is that we're losing whatever benefit would be otherwise provided. So far I can't find any.

@adinhodovic
Copy link

Thanks @adinhodovic, this looks like it works with no observable downsides. I wonder what the intent of the C-n / C-p mappings that we're overriding were? Because I guess the downside of the override, is that we're losing whatever benefit would be otherwise provided. So far I can't find any.

I think they're used for scrolling up and down in the completion list, why I'm not sure.

@yangmillstheory
Copy link
Author

I see. In that case I'm still able to use and to cycle forwards and backwards through the completion list, so no harm done.

I do think this is still a bug that should be resolved.

Thanks!

@pogopaule
Copy link

@yangmillstheory, how do you cycle backwards and forwards now? With the mapping proposed by @adinhodovic, I can't do that anymore.

@yangmillstheory
Copy link
Author

yangmillstheory commented Feb 11, 2024

I use <Tab> and <S-Tab>. I thought I mentioned that above, but I guess I didn't.

@maciejzj
Copy link

I've faced a similar problem, but in a slightly different context. I'm used to navigating history with C-N/C-P and also using them for selecting completions in vim popup windows. So, I developed a solution that lets me do both simultaneously.

Here's the trick: With the code snippet below, C-N/C-P will navigate through the command line history (like in shell), unless you start typing a character. When you start typing, C-N/C-P switches to selecting completions instead of navigating history. This way, you get the best of both worlds. The may not be the setup OP is looking for, however, someone may find it useful one day, so I decided to share it here :)

-- For command line
-- This custom mappig setup causes CTRL-P, CTRL-N to fallback to history
-- browsing, unless user has explicitly typed something in the cmdline, then
-- these two activate to browse completion options.
local cmdline_cmp_state = "has_not_typed"
vim.api.nvim_create_autocmd({ "CmdlineEnter" }, {
  command = "lua cmdline_cmp_state = 'has_not_typed'",
})
vim.api.nvim_create_autocmd({ "CmdlineChanged" }, {
  callback = function()
    if cmdline_cmp_state == "has_not_typed" then
      cmdline_cmp_state = "has_typed"
    elseif cmdline_cmp_state == "has_browsed_history" then
      cmdline_cmp_state = "has_not_typed"
    end
  end,
})
local function select_or_fallback(select_action)
  return cmp.mapping(function(fallback)
    if cmdline_cmp_state == "has_typed" and cmp.visible() then
      select_action()
    else
      cmdline_cmp_state = "has_browsed_history"
      cmp.close()
      fallback()
    end
  end, { "i", "c" })
end
cmp.setup.cmdline(":", {
  mapping = cmp.mapping.preset.cmdline({
    ["<C-n>"] = select_or_fallback(cmp.select_next_item),
    ["<C-p>"] = select_or_fallback(cmp.select_prev_item),
  }),
  sources = cmp.config.sources({
    { name = "path" },
  }, {
    { name = "cmdline" },
  }),
})

@plt3
Copy link

plt3 commented Apr 12, 2024

I've faced a similar problem, but in a slightly different context. I'm used to navigating history with C-N/C-P and also using them for selecting completions in vim popup windows. So, I developed a solution that lets me do both simultaneously.

@maciejzj, I was facing this exact same issue and saw your solution, which works great! However, after some digging around I found a simpler configuration that achieves the same thing (for me, at least). Posting it here if it can be useful:

-- Use cmdline & path source for ':'
cmp.setup.cmdline(":", {
	-- C-n/C-p cycle through completions if a character has been typed and through
	-- command history if not (from https://www.reddit.com/r/neovim/comments/v5pfmy/comment/ibb61w3/)
	mapping = cmp.mapping.preset.cmdline({
		["<C-n>"] = { c = cmp.mapping.select_next_item() },
		["<C-p>"] = { c = cmp.mapping.select_prev_item() },
	}),
	sources = cmp.config.sources({
		{ name = "path" },
	}, {
		{ name = "cmdline" },
	}),
})

@maciejzj
Copy link

@plt3 Thanks a lot, this will slim down my init.lua a bit :))

It would be great to see this setup in some more visible place like the README. I guess many users would want this behaviour and it seems rather unobvious how to achieve it (TBH I would have never figured out that specifying that next/prev should explicitly work in "command mode" would result in this behaviour overall and still don't fully get it 🙃).

@aimestereo
Copy link

@plt3 @maciejzj guys, you're the best,

Also, your solution have these attrs:

  • supports all three :, ?,/, not just :
  • preset keymaps doesn't override Up&Down arrows, so it supports Up, Down arrows to search history based on history filtered by already entered substring: i.e. :echo press Up to search through history of commands that starts with echo .

I will also share my snippet that I based on your work: I don't use preset, only set maps that I use.

-- Declare only keys that I actually use
-- Based on https://github.com/hrsh7th/cmp-cmdline/issues/108#issuecomment-2052449375
-- C-n/C-p cycle through completions if a character has been typed and through
-- command history if not (from https://www.reddit.com/r/neovim/comments/v5pfmy/comment/ibb61w3/)
local cmd_mapping = {
  ["<C-Space>"] = { c = cmp.mapping.complete({}) },
  ["<C-n>"] = { c = cmp.mapping.select_next_item() },
  ["<C-p>"] = { c = cmp.mapping.select_prev_item() },
  ["<C-e>"] = { c = cmp.mapping.abort() },
  ["<C-y>"] = {
    c = cmp.mapping.confirm({
      behavior = cmp.ConfirmBehavior.Insert,
      select = true,
    }),
  },
}

-- Use buffer source for `/` and `?`
cmp.setup.cmdline({ "/", "?" }, {
  mapping = cmd_mapping,
  sources = {
    { name = "buffer" },
  },
})

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants