diff --git a/autoload/ensime.vim b/autoload/ensime.vim index c085531..e1fb0f3 100644 --- a/autoload/ensime.vim +++ b/autoload/ensime.vim @@ -39,16 +39,12 @@ function! ensime#au_vim_enter(filename) abort return s:call_plugin('au_vim_enter', [a:filename]) endfunction -function! ensime#au_buf_leave(filename) abort - return s:call_plugin('au_buf_leave', [a:filename]) -endfunction - -function! ensime#au_cursor_hold(filename) abort - return s:call_plugin('au_cursor_hold', [a:filename]) +function! ensime#au_buf_enter(filename) abort + return s:call_plugin('au_buf_enter', [a:filename]) endfunction -function! ensime#au_cursor_moved(filename) abort - return s:call_plugin('au_cursor_moved', [a:filename]) +function! ensime#au_buf_leave(filename) abort + return s:call_plugin('au_buf_leave', [a:filename]) endfunction function! ensime#com_en_no_teardown(args, range) abort @@ -155,6 +151,18 @@ function! ensime#com_en_clients(args, range) abort return s:call_plugin('com_en_clients', [a:args, a:range]) endfunction +function! ensime#au_cursor_hold(filename) abort + return s:call_plugin('au_cursor_hold', [a:filename]) +endfunction + +function! ensime#au_cursor_moved(filename) abort + return s:call_plugin('au_cursor_moved', [a:filename]) +endfunction + +function! ensime#fun_en_tick(timer) abort + return s:call_plugin('fun_en_tick', [a:timer]) +endfunction + function! s:call_plugin(method_name, args) abort " TODO: support nvim rpc if has('nvim') diff --git a/ensime_shared/client.py b/ensime_shared/client.py index 89bd823..a81f72a 100644 --- a/ensime_shared/client.py +++ b/ensime_shared/client.py @@ -602,8 +602,8 @@ def unqueue_and_display(self, filename): self.editor.lazy_display_error(filename) self.unqueue() - def on_cursor_hold(self, filename): - """Handler for event CursorHold.""" + def tick(self, filename): + """Try to connect and display messages in queue.""" if self.connection_attempts < 10: # Trick to connect ASAP when # plugin is started without @@ -611,12 +611,6 @@ def on_cursor_hold(self, filename): self.setup(True, False) self.connection_attempts += 1 self.unqueue_and_display(filename) - self.editor.cursorhold() - - def on_cursor_move(self, filename): - """Handler for event CursorMoved.""" - self.setup(True, False) - self.unqueue_and_display(filename) def vim_enter(self, filename): """Set up EnsimeClient when vim enters. diff --git a/ensime_shared/editor.py b/ensime_shared/editor.py index f3240b4..bc20ca5 100644 --- a/ensime_shared/editor.py +++ b/ensime_shared/editor.py @@ -117,6 +117,11 @@ def menu(self, prompt, choices): return choices[choice - 1] + def is_buffer_ensime_compatible(self): + """Return True if the current buffer is supported by Ensime.""" + current_filetype = self._vim.eval('&filetype') + return current_filetype in ['scala', 'java'] + def set_buffer_options(self, options, bufnr=None): """Set buffer-local options for a buffer, defaulting to current. @@ -200,8 +205,6 @@ def write(self, noautocmd=False): # ftplugin/scala/ensime.vim, custom/dotted filetypes & syntax def initialize(self): """Sets up initial ensime-vim editor settings.""" - self._vim.options['updatetime'] = 1000 - # TODO: This seems wrong, the user setting value is never used anywhere. if 'EnErrorStyle' not in self._vim.vars: self._vim.vars['EnErrorStyle'] = 'EnError' @@ -336,24 +339,6 @@ def indent(line): return ".".join(fqn) - def cursorhold(self): - """Stuff that EnsimeClient does on CursorHold. - - Todo: - This is a temporary placeholder for further refactoring. - - IMO EnsimeClient should not have methods like ``on_cursor_hold``, - it's the wrong layer of abstraction for handlers of editor events. - I'm not sure where these should go yet though -- maybe the Ensime - class since it represents "the Vim plugin" as an entity, iterating - through its list of clients and calling the logic for each. - """ - # Make sure no plugin overrides this - self._vim.options['updatetime'] = 1000 - # Keys with no effect, just retrigger CursorHold - # http://vim.wikia.com/wiki/Timer_to_execute_commands_periodically - self._vim.command(r'call feedkeys("f\e")') - def display_notes(self, notes): """Renders "notes" reported by ENSIME, such as typecheck errors.""" diff --git a/ensime_shared/ensime.py b/ensime_shared/ensime.py index 6013926..c48a1ff 100644 --- a/ensime_shared/ensime.py +++ b/ensime_shared/ensime.py @@ -6,6 +6,7 @@ from .config import ProjectConfig from .editor import Editor from .launcher import EnsimeLauncher +from .ticker import Ticker def execute_with_client(quiet=False, @@ -48,6 +49,7 @@ def __init__(self, vim): # race condition of autocommand handlers being invoked as they're being # defined. self._vim = vim + self._ticker = None self.clients = {} @property @@ -115,9 +117,19 @@ def create_client(self, config_path): launcher = EnsimeLauncher(self._vim, config) if self.using_server_v2: - return EnsimeClientV2(editor, launcher) + client = EnsimeClientV2(editor, launcher) else: - return EnsimeClientV1(editor, launcher) + client = EnsimeClientV1(editor, launcher) + + self._create_ticker() + + return client + + def _create_ticker(self): + """Create and start the periodic ticker.""" + if not self._ticker: + self._ticker = Ticker(self._vim) + def disable_plugin(self): """Disable ensime-vim, in the event of an error we can't usefully @@ -147,6 +159,14 @@ def runtime_paths(self): # TODO: memoize return paths + def tick_clients(self): + """Trigger the periodic tick function in the client.""" + if not self._ticker: + self._create_ticker() + + for client in self.clients.values(): + self._ticker.tick(client) + @execute_with_client() def com_en_toggle_teardown(self, client, args, range=None): client.do_toggle_teardown(None, None) @@ -273,25 +293,29 @@ def au_vim_enter(self, client, filename): def au_vim_leave(self, client, filename): self.teardown() - @execute_with_client() - def au_buf_leave(self, client, filename): - client.buffer_leave(filename) - @execute_with_client() def au_cursor_hold(self, client, filename): - client.on_cursor_hold(filename) + self.tick_clients() - @execute_with_client(quiet=True) + @execute_with_client() def au_cursor_moved(self, client, filename): - client.on_cursor_move(filename) + self.tick_clients() + + def fun_en_tick(self, timer): + self.tick_clients() + + @execute_with_client() + def au_buf_enter(self, client, filename): + # Only set up to trigger the creation of a client + pass + + @execute_with_client() + def au_buf_leave(self, client, filename): + client.buffer_leave(filename) @execute_with_client() def fun_en_complete_func(self, client, findstart_and_base, base=None): """Invokable function from vim and neovim to perform completion.""" - current_filetype = self._vim.eval('&filetype') - if current_filetype not in ['scala', 'java']: - return - if isinstance(findstart_and_base, list): # Invoked by neovim findstart = findstart_and_base[0] diff --git a/ensime_shared/ticker.py b/ensime_shared/ticker.py new file mode 100644 index 0000000..bb79ecc --- /dev/null +++ b/ensime_shared/ticker.py @@ -0,0 +1,36 @@ +REFRESH_TIMER = 1000 + + +class Ticker(object): + + def __init__(self, _vim): + self._vim = _vim + self.has_timers = bool(int(self._vim.eval("has('timers')"))) + + if self.has_timers: + self._timer = None + self._start_refresh_timer() + + def tick(self, client): + filename = client.editor.path() + + # XXX is this necessary ? + if not client.editor.is_buffer_ensime_compatible(): + return + + client.tick(filename) + + if not self.has_timers: + self._repeat_cursor_hold() + + def _repeat_cursor_hold(self): + self._vim.options['updatetime'] = REFRESH_TIMER + self._vim.command('call feedkeys("f\e")') + + def _start_refresh_timer(self): + """Start the Vim timer. """ + if not self._timer: + self._timer = self._vim.eval( + "timer_start({}, 'EnTick', {{'repeat': -1}})" + .format(REFRESH_TIMER) + ) diff --git a/ensime_shared/util.py b/ensime_shared/util.py index a2c4324..2660b6c 100644 --- a/ensime_shared/util.py +++ b/ensime_shared/util.py @@ -41,7 +41,6 @@ def extract_package_name(lines): break return package - @contextmanager def catch(exception, handler=lambda e: None): """If exception runs handler.""" diff --git a/plugin/ensime.vim b/plugin/ensime.vim index f23b67d..219c8e3 100644 --- a/plugin/ensime.vim +++ b/plugin/ensime.vim @@ -61,8 +61,12 @@ augroup ensime autocmd VimLeave *.java,*.scala call ensime#au_vim_leave(expand("")) autocmd VimEnter *.java,*.scala call ensime#au_vim_enter(expand("")) autocmd BufLeave *.java,*.scala call ensime#au_buf_leave(expand("")) - autocmd CursorHold *.java,*.scala call ensime#au_cursor_hold(expand("")) - autocmd CursorMoved *.java,*.scala call ensime#au_cursor_moved(expand("")) + if !has('timers') + autocmd CursorHold *.java,*.scala call ensime#au_cursor_hold(expand("")) + autocmd CursorMoved *.java,*.scala call ensime#au_cursor_moved(expand("")) + else + autocmd BufEnter *.java,*.scala call ensime#au_buf_enter(expand("")) + endif augroup END command! -nargs=* -range EnInstall call ensime#com_en_install([], '') @@ -102,6 +106,10 @@ function! EnCompleteFunc(a, b) abort return ensime#fun_en_complete_func(a:a, a:b) endfunction +function! EnTick(timer) abort + return ensime#fun_en_tick(a:timer) +endfunction + let g:loaded_ensime = 1 " vim:set et sw=4 ts=4 tw=78: diff --git a/rplugin/python/ensime.py b/rplugin/python/ensime.py index af37707..0d10803 100644 --- a/rplugin/python/ensime.py +++ b/rplugin/python/ensime.py @@ -6,7 +6,6 @@ import neovim - def ensime_init_path(): path = os.path.abspath(inspect.getfile(inspect.currentframe())) expected_nvim_path_end = os.path.join('rplugin', 'python', 'ensime.py') @@ -172,13 +171,13 @@ def au_vim_leave(self, *args, **kwargs): def au_buf_leave(self, *args, **kwargs): super(NeovimEnsime, self).au_buf_leave(*args, **kwargs) - @neovim.autocmd('CursorHold', **autocmd_params) - def au_cursor_hold(self, *args, **kwargs): - super(NeovimEnsime, self).au_cursor_hold(*args, **kwargs) + @neovim.autocmd('BufEnter', **autocmd_params) + def au_buf_enter(self, *args, **kwargs): + super(NeovimEnsime, self).au_buf_enter(*args, **kwargs) - @neovim.autocmd('CursorMoved', **autocmd_params) - def au_cursor_moved(self, *args, **kwargs): - super(NeovimEnsime, self).au_cursor_moved(*args, **kwargs) + @neovim.function('EnTick', sync=True) + def tick(self, timer): + super(NeovimEnsime, self).fun_en_tick(timer) @neovim.function('EnCompleteFunc', sync=True) def fun_en_complete_func(self, *args, **kwargs):