roxma / nvim-completion-manager

:warning: PLEASE USE https://github.com/ncm2/ncm2 INSTEAD
MIT License
917 stars 49 forks source link

Reduce work of nvim-completion-manager when using Multiple Cursors #107

Closed thalesmello closed 7 years ago

thalesmello commented 7 years ago

I use multiple cursors with the following setup:

let g:multi_cursor_exit_from_insert_mode = 0
let g:multi_cursor_exit_from_visual_mode = 0

So that, when I press Esc from insert mode, I'm back to normal mode so that I can manipulate text.

After I installed nvim-completion-manager, it started hanging after I pressed Esc.

If I press <c-c>, Neovim enters a weird state and shows the following error messages (appears to be in an infinite loop):

cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):
cm completion source cm-ultisnips exception caught: Vim(return):Traceback (most recent call last):

In order to solve the issue, I ended up using:

function! Multiple_cursors_before()
  if get(g:, 'cm_smart_enable', 1) == 1
    call cm#disable_for_buffer()
  endif
endfunction

function! Multiple_cursors_after()
  if get(g:, 'cm_smart_enable', 1) == 1
    call cm#enable_for_buffer()
  endif
endfunction

Shouldn't the problem be solved by the events MultipleCursorsPre and MultipleCursorsPost?

roxma commented 7 years ago

I cannot reproduce the hanging issue with this minimal vimrc.

set nocompatible
syntax on
filetype plugin indent on
set encoding=utf-8 fileencodings=ucs-bom,utf-8,gbk,gb18030,latin1 termencoding=utf-8

call plug#begin(expand('<sfile>:h') . '/plugged/')
Plug 'roxma/nvim-completion-manager'
Plug 'terryma/vim-multiple-cursors'
let g:multi_cursor_exit_from_insert_mode = 0
let g:multi_cursor_exit_from_visual_mode = 0
call plug#end()
thalesmello commented 7 years ago

@roxma Investigating this issue a little better, I've concluded there is no bug, but maybe performance optimizations that could be take place.

After being a little bit more patient, I've noticed that it's not an infinite loop after I try to quit insert mode, but it's actually a bunch of commands that are slowing vim down.

I tried to reproduce my init.vim as minimally as possible.

Take a look:

set runtimepath=/usr/local/Cellar/neovim/HEAD-eb40b7e_1/share/nvim/runtime

set runtimepath+=~/.vim/plugged/ultisnips
set runtimepath+=~/.vim/plugged/vim-multiple-cursors
set runtimepath+=~/.vim/plugged/ale
set runtimepath+=~/.vim/plugged/vim-trailing-whitespace
" Multichange tries to replace multiple cursors. I end up using both.
set runtimepath+=~/.vim/plugged/multichange.vim
set runtimepath+=~/.vim/plugged/nvim-completion-manager

let g:multi_cursor_exit_from_insert_mode = 0
let g:multi_cursor_exit_from_visual_mode = 0
let g:multichange_mapping        = 'mc'
let g:multichange_motion_mapping = 'c'

When I have these many plugins enabled at the same time, multiple cursors start to be a lot more laggish.

The reason why is probably the amount of autocmd that have to be executed.

Take a look at the output of autocmd InsertLeave:

--- Auto-Commands ---
ALECursorGroup  InsertLeave
    *         call ale#cursor#EchoCursorWarning()
InsertLeave
    *         if ShouldMatchWhitespace() | match ExtraWhitespace /\\\@<![\u3000[:space:]]\+$/ | endif
multichange  InsertLeave
    *         call multichange#Substitute()
              call multichange#EchoModeMessage()
cm  InsertLeave
    <buffer=1>
              call s:notify_core_channel('cm_insert_leave')
              call s:change_tick_stop()

Besides the amount my vim configuration has to deal because of all my plugins (I will deal with that later), my question was why setting up triggers for enabling/disabling nvim-completion-manager for the buffer made such a huge difference in the first place.

function! Multiple_cursors_before()
  if get(g:, 'cm_smart_enable', 1) == 1
    call cm#disable_for_buffer()
  endif
endfunction

function! Multiple_cursors_after()
  if get(g:, 'cm_smart_enable', 1) == 1
    call cm#enable_for_buffer()
  endif
endfunction

After investigating the autocmd MultipleCursorsPre and Post implementations, I noticed it uses the g:_cm_lock flag to control wether completion should popup or not. However, it looks like it's still doing work because of nvim-completion-manager's autocmds.

Comparing it to cm#disable_for_buffer(), it actually destroys the autocmd, so nvim-completion-manager does nothing.

Is there any particular reason the g:_cm_lock flag is used rather disabling nvim altogether?

roxma commented 7 years ago

Is there any particular reason the g:_cm_lock flag is used rather disabling nvim altogether?

If a user's vimrc contains code for disabling/enabling NCM. NCM will not know about this, and it may be enabled/disabled by accident.

g:_cm_lock is a way of decoupling, a use case controls its own bit. NCM only works when g:_cm_lock == 0.

However, it looks like it's still doing work because of nvim-completion-manager's autocmds.

Yes. Currently the lock only supresses the popup menu, NCM is still calculating candidates. Mayby it should supress more actions for performance.