如何在Vimscript中实现弹窗(Popup)内的输入(Input)功能?以Coc插件的Coc-rename功能为例
Got it, you're looking for that smooth popup input experience similar to Coc-rename—where a compact window pops up near your cursor, lets you type and edit a value in-place, and applies the change when you confirm with Enter (or cancels with ESC). Let's break down how to build this step by step.
Core Approach
The idea is to use Vim's popup_create() to make a container window, then listen for keyboard input to update the popup's content in real-time. We'll handle basic editing (typing, backspace, cursor movement) and add confirmation/cancellation logic to mimic Coc's behavior.
Step 1: Build the Popup Input Function
First, create a reusable function that generates the popup and manages input:
function! PopupInput(prompt, default) abort " Initialize popup content: prompt line + default input text let l:initial_content = [a:prompt, a:default] let l:popup_id = popup_create(l:initial_content, { \ 'border': ['', '', '', ' ', '', '', '', ''], " Subtle border \ 'highlight': 'NormalFloat', " Use the default float window highlight \ 'line_highlight': [{'line': 0, 'hl': 'Comment'}], " Gray out the prompt line \ 'line': winline() + 1, " Position popup below current line \ 'col': wincol() + len(a:prompt) + 2, " Align input with prompt \ }) let l:input_text = a:default let l:cursor_pos = len(a:default) " Track cursor position in input while 1 let l:char = getchar() " Handle confirmation (Enter) if l:char == "\<CR>" popup_close(l:popup_id) return l:input_text " Handle cancellation (ESC) elseif l:char == "\<ESC>" popup_close(l:popup_id) return '' " Handle backspace elseif l:char == "\<BS>" if l:cursor_pos > 0 let l:input_text = strpart(l:input_text, 0, l:cursor_pos - 1) . strpart(l:input_text, l:cursor_pos) let l:cursor_pos -= 1 endif " Handle left arrow (move cursor left) elseif l:char == "\<Left>" if l:cursor_pos > 0 let l:cursor_pos -= 1 endif " Handle right arrow (move cursor right) elseif l:char == "\<Right>" if l:cursor_pos < len(l:input_text) let l:cursor_pos += 1 endif " Handle printable characters elseif type(l:char) == type(0) && l:char >= 32 && l:char <= 126 let l:input_text = strpart(l:input_text, 0, l:cursor_pos) . nr2char(l:char) . strpart(l:input_text, l:cursor_pos) let l:cursor_pos += 1 endif " Update popup with current input + cursor marker let l:display_text = l:input_text if l:cursor_pos <= len(l:display_text) " Add an underscore to show cursor position let l:display_text = strpart(l:display_text, 0, l:cursor_pos) . '_' . strpart(l:display_text, l:cursor_pos) endif call popup_settext(l:popup_id, [a:prompt, l:display_text]) endwhile endfunction
Step 2: Add a Rename Feature (Like Coc-Rename)
Now let's use this function to build a rename tool that works like Coc-rename. This will grab the current word, let you edit it in the popup, then replace all occurrences:
function! RenameCurrentWord() abort let l:current_word = expand('<cword>') let l:new_name = PopupInput('Rename to:', l:current_word) " Exit if user cancelled or didn't change the word if empty(l:new_name) || l:new_name == l:current_word return endif " Replace all occurrences of the word (add 'c' flag to confirm each change if needed) execute '%s/\<' . escape(l:current_word, '/\') . '\>/' . escape(l:new_name, '/\') . '/g' echo "Renamed all instances of '" . l:current_word . "' to '" . l:new_name . "'" endfunction " Bind to a convenient shortcut (e.g., Leader + rn) nnoremap <silent> <leader>rn :call RenameCurrentWord()<CR>
Step 3: Customize the Experience
You can tweak this to match your preferences:
- Adjust Popup Style: Modify the
borderoption to use fancier borders (e.g.,['╭', '─', '╮', '│', '╯', '─', '╰', '│']for rounded corners) or changehighlightto a custom group. - Live Preview: To mimic Coc's real-time preview, add a call to update the buffer temporarily during input (reset if the user cancels).
- Input Validation: Add checks to restrict input to certain characters (e.g., no spaces for variable names).
- Tab Completion: Integrate Vim's
complete()function to add auto-completion for common values.
This setup gives you a native Vimscript solution that behaves almost identically to Coc-rename, without relying on external plugins.
内容的提问来源于stack exchange,提问作者kroyxlab

