Filename completer now uses threads

This was done by introducing a new ThreadedCompleter class that descends from
Completer. Both JediCompleter and FilenameCompleter descend from
ThreadedCompleter.
This commit is contained in:
Strahinja Val Markovic 2013-04-22 22:19:26 -07:00
parent 8d20637295
commit 196228217f
10 changed files with 144 additions and 94 deletions

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completers.completer import GeneralCompleter
from completers.general_completer import GeneralCompleter
import vim
import vimsupport
import ycm_core

View File

@ -303,24 +303,6 @@ class Completer( object ):
return ''
class GeneralCompleter( Completer ):
"""
A base class for General completers in YCM. A general completer is used in all
filetypes.
Because this is a subclass of Completer class, you should refer to the
Completer class documentation. Do NOT use this class for semantic completers!
Subclass Completer directly.
"""
def __init__( self ):
super( GeneralCompleter, self ).__init__()
def SupportedFiletypes( self ):
return set()
class CompletionsCache( object ):
def __init__( self ):
self.line = -1

View File

@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completers.completer import GeneralCompleter
from completers.threaded_completer import ThreadedCompleter
import vim
import vimsupport
import os
@ -25,14 +25,13 @@ import re
USE_WORKING_DIR = vimsupport.GetBoolValue(
'g:ycm_filepath_completion_use_working_dir' )
class FilenameCompleter( GeneralCompleter ):
class FilenameCompleter( ThreadedCompleter ):
"""
General completer that provides filename and filepath completions.
"""
def __init__(self):
super( FilenameCompleter, self ).__init__()
self._candidates = []
self._path_regex = re.compile("""
# 1 or more 'D:/'-like token or '/' or '~' or './' or '../'
@ -53,20 +52,11 @@ class FilenameCompleter( GeneralCompleter ):
return vim.current.line[ start_column - 1 ] == '/'
def CandidatesForQueryAsyncInner( self, query, start_column ):
self._candidates = []
self.ComputePaths( start_column )
def SupportedFiletypes( self ):
return []
def AsyncCandidateRequestReadyInner( self ):
return True
def CandidatesFromStoredRequestInner( self ):
return self._candidates
def ComputePaths( self, start_column ):
def ComputeCandidates( self, unused_query, start_column ):
def GenerateCandidateForPath( path, path_dir ):
is_dir = os.path.isdir( os.path.join( path_dir, path ) )
return { 'word': path,
@ -86,6 +76,4 @@ class FilenameCompleter( GeneralCompleter ):
except:
paths = []
self._candidates = [ GenerateCandidateForPath( path, path_dir ) for path
in paths ]
self._completions_ready = True
return [ GenerateCandidateForPath( path, path_dir ) for path in paths ]

View File

@ -18,7 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completers.completer import GeneralCompleter
from completers.general_completer import GeneralCompleter
from UltiSnips import UltiSnips_Manager

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
#
# Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completer import Completer
class GeneralCompleter( Completer ):
"""
A base class for General completers in YCM. A general completer is used in all
filetypes.
Because this is a subclass of Completer class, you should refer to the
Completer class documentation. Do NOT use this class for semantic completers!
Subclass Completer directly.
"""
def __init__( self ):
super( GeneralCompleter, self ).__init__()
def SupportedFiletypes( self ):
return set()

View File

@ -19,8 +19,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import vim
from threading import Thread, Event
from completers.completer import Completer
from completers.threaded_completer import ThreadedCompleter
import vimsupport
import sys
@ -39,7 +38,7 @@ except ImportError:
sys.path.pop( 0 )
class JediCompleter( Completer ):
class JediCompleter( ThreadedCompleter ):
"""
A Completer that uses the Jedi completion engine.
https://jedi.readthedocs.org/en/latest/
@ -47,16 +46,6 @@ class JediCompleter( Completer ):
def __init__( self ):
super( JediCompleter, self ).__init__()
self._query_ready = Event()
self._candidates_ready = Event()
self._candidates = None
self._start_completion_thread()
def _start_completion_thread( self ):
self._completion_thread = Thread( target=self.SetCandidates )
self._completion_thread.daemon = True
self._completion_thread.start()
def SupportedFiletypes( self ):
@ -64,47 +53,17 @@ class JediCompleter( Completer ):
return [ 'python' ]
def CandidatesForQueryAsyncInner( self, unused_query, unused_start_column ):
self._candidates = None
self._candidates_ready.clear()
self._query_ready.set()
def ComputeCandidates( self, unused_query, unused_start_column ):
filename = vim.current.buffer.name
line, column = vimsupport.CurrentLineAndColumn()
# Jedi expects lines to start at 1, not 0
line += 1
contents = '\n'.join( vim.current.buffer )
script = Script( contents, line, column, filename )
return [ { 'word': str( completion.word ),
'menu': str( completion.description ),
'info': str( completion.doc ) }
for completion in script.complete() ]
def AsyncCandidateRequestReadyInner( self ):
return WaitAndClear( self._candidates_ready, timeout=0.005 )
def CandidatesFromStoredRequestInner( self ):
return self._candidates or []
def SetCandidates( self ):
while True:
try:
WaitAndClear( self._query_ready )
filename = vim.current.buffer.name
line, column = vimsupport.CurrentLineAndColumn()
# Jedi expects lines to start at 1, not 0
line += 1
contents = '\n'.join( vim.current.buffer )
script = Script( contents, line, column, filename )
self._candidates = [ { 'word': str( completion.word ),
'menu': str( completion.description ),
'info': str( completion.doc ) }
for completion in script.complete() ]
except:
self._query_ready.clear()
self._candidates = []
self._candidates_ready.set()
def WaitAndClear( event, timeout=None ):
# We can't just do flag_is_set = event.wait( timeout ) because that breaks on
# Python 2.6
event.wait( timeout )
flag_is_set = event.is_set()
if flag_is_set:
event.clear()
return flag_is_set

View File

@ -0,0 +1,83 @@
#!/usr/bin/env python
#
# Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import abc
from threading import Thread, Event
from completer import Completer
class ThreadedCompleter( Completer ):
def __init__( self ):
super( ThreadedCompleter, self ).__init__()
self._query_ready = Event()
self._candidates_ready = Event()
self._candidates = None
self._start_completion_thread()
def _start_completion_thread( self ):
self._completion_thread = Thread( target=self.SetCandidates )
self._completion_thread.daemon = True
self._completion_thread.start()
def CandidatesForQueryAsyncInner( self, query, start_column ):
self._candidates = None
self._candidates_ready.clear()
self._query = query
self._start_column = start_column
self._query_ready.set()
def AsyncCandidateRequestReadyInner( self ):
return WaitAndClearIfSet( self._candidates_ready, timeout=0.005 )
def CandidatesFromStoredRequestInner( self ):
return self._candidates or []
@abc.abstractmethod
def ComputeCandidates( self, query, start_column ):
pass
def SetCandidates( self ):
while True:
try:
WaitAndClearIfSet( self._query_ready )
self._candidates = self.ComputeCandidates( self._query,
self._start_column )
except:
self._query_ready.clear()
self._candidates = []
self._candidates_ready.set()
def WaitAndClearIfSet( event, timeout=None ):
"""Given an |event| and a |timeout|, waits for the event a maximum of timeout
seconds. After waiting, clears the event if it's set and returns the state of
the event before it was cleared."""
# We can't just do flag_is_set = event.wait( timeout ) because that breaks on
# Python 2.6
event.wait( timeout )
flag_is_set = event.is_set()
if flag_is_set:
event.clear()
return flag_is_set

View File

@ -35,7 +35,7 @@ except ImportError as e:
from completers.all.omni_completer import OmniCompleter
from completers.general.general_completer import GeneralCompleterStore
from completers.general.general_completer_store import GeneralCompleterStore
FILETYPE_SPECIFIC_COMPLETION_TO_DISABLE = vim.eval(

View File

@ -23,3 +23,4 @@ def IsIdentifierChar( char ):
def SanitizeQuery( query ):
return query.strip()