A (barely) working version of ycmd + client
Still a lot of work to do.
This commit is contained in:
parent
02b88dccf1
commit
1730660555
@ -539,11 +539,6 @@ function! youcompleteme#Complete( findstart, base )
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
py request = ycm_state.CreateCompletionRequest()
|
py request = ycm_state.CreateCompletionRequest()
|
||||||
if !pyeval( 'request.ShouldComplete()' )
|
|
||||||
" for vim, -2 means not found but don't trigger an error message
|
|
||||||
" see :h complete-functions
|
|
||||||
return -2
|
|
||||||
endif
|
|
||||||
return pyeval( 'request.CompletionStartColumn()' )
|
return pyeval( 'request.CompletionStartColumn()' )
|
||||||
else
|
else
|
||||||
return s:CompletionsForQuery( a:base )
|
return s:CompletionsForQuery( a:base )
|
||||||
@ -628,14 +623,15 @@ function! youcompleteme#OpenGoToList()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete
|
" TODO: Make this work again
|
||||||
\ YcmCompleter call s:CompleterCommand(<f-args>)
|
" command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete
|
||||||
|
" \ YcmCompleter call s:CompleterCommand(<f-args>)
|
||||||
|
"
|
||||||
function! youcompleteme#SubCommandsComplete( arglead, cmdline, cursorpos )
|
"
|
||||||
return join( pyeval( 'ycm_state.GetFiletypeCompleter().DefinedSubcommands()' ),
|
" function! youcompleteme#SubCommandsComplete( arglead, cmdline, cursorpos )
|
||||||
\ "\n")
|
" return join( pyeval( 'ycm_state.GetFiletypeCompleter().DefinedSubcommands()' ),
|
||||||
endfunction
|
" \ "\n")
|
||||||
|
" endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:ForceCompile()
|
function! s:ForceCompile()
|
||||||
|
@ -217,6 +217,18 @@ TEST( IdentifierCompleterTest, ShorterAndLowercaseWins ) {
|
|||||||
"STDIN_FILENO" ) );
|
"STDIN_FILENO" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST( IdentifierCompleterTest, AddIdentifiersToDatabaseFromBufferWorks ) {
|
||||||
|
IdentifierCompleter completer;
|
||||||
|
completer.AddIdentifiersToDatabaseFromBuffer( "foo foogoo ba",
|
||||||
|
"foo",
|
||||||
|
"/foo/bar",
|
||||||
|
false );
|
||||||
|
|
||||||
|
EXPECT_THAT( completer.CandidatesForQueryAndType( "oo", "foo" ),
|
||||||
|
ElementsAre( "foo",
|
||||||
|
"foogoo" ) );
|
||||||
|
}
|
||||||
|
|
||||||
TEST( IdentifierCompleterTest, TagsEndToEndWorks ) {
|
TEST( IdentifierCompleterTest, TagsEndToEndWorks ) {
|
||||||
IdentifierCompleter completer;
|
IdentifierCompleter completer;
|
||||||
std::vector< std::string > tag_files;
|
std::vector< std::string > tag_files;
|
||||||
|
0
python/ycm/client/__init__.py
Normal file
0
python/ycm/client/__init__.py
Normal file
77
python/ycm/client/base_request.py
Normal file
77
python/ycm/client/base_request.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 vim
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from ycm import vimsupport
|
||||||
|
|
||||||
|
HEADERS = {'content-type': 'application/json'}
|
||||||
|
|
||||||
|
class BaseRequest( object ):
|
||||||
|
def __init__( self ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def Start( self ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def Done( self ):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def Response( self ):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def PostDataToHandler( self, data, handler ):
|
||||||
|
response = requests.post( _BuildUri( handler ),
|
||||||
|
data = json.dumps( data ),
|
||||||
|
headers = HEADERS )
|
||||||
|
response.raise_for_status()
|
||||||
|
if response.text:
|
||||||
|
return response.json()
|
||||||
|
return None
|
||||||
|
|
||||||
|
server_location = 'http://localhost:6666'
|
||||||
|
|
||||||
|
|
||||||
|
def BuildRequestData( start_column = None, query = None ):
|
||||||
|
line, column = vimsupport.CurrentLineAndColumn()
|
||||||
|
request_data = {
|
||||||
|
'filetypes': vimsupport.CurrentFiletypes(),
|
||||||
|
'line_num': line,
|
||||||
|
'column_num': column,
|
||||||
|
'start_column': start_column,
|
||||||
|
'line_value': vim.current.line,
|
||||||
|
'filepath': vim.current.buffer.name,
|
||||||
|
'file_data': vimsupport.GetUnsavedAndCurrentBufferData()
|
||||||
|
}
|
||||||
|
|
||||||
|
if query:
|
||||||
|
request_data[ 'query' ] = query
|
||||||
|
|
||||||
|
return request_data
|
||||||
|
|
||||||
|
|
||||||
|
def _BuildUri( handler ):
|
||||||
|
return ''.join( [ BaseRequest.server_location, '/', handler ] )
|
||||||
|
|
||||||
|
|
67
python/ycm/client/command_request.py
Normal file
67
python/ycm/client/command_request.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 ycm.client.base_request import BaseRequest, BuildRequestData
|
||||||
|
|
||||||
|
|
||||||
|
class CommandRequest( BaseRequest ):
|
||||||
|
class ServerResponse( object ):
|
||||||
|
def __init__( self ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def Valid( self ):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __init__( self, arguments, completer_target = None ):
|
||||||
|
super( CommandRequest, self ).__init__()
|
||||||
|
self._arguments = arguments
|
||||||
|
self._completer_target = ( completer_target if completer_target
|
||||||
|
else 'filetype_default' )
|
||||||
|
# TODO: Handle this case.
|
||||||
|
# if completer_target == 'omni':
|
||||||
|
# completer = SERVER_STATE.GetOmniCompleter()
|
||||||
|
|
||||||
|
def Start( self ):
|
||||||
|
request_data = BuildRequestData()
|
||||||
|
request_data.update( {
|
||||||
|
'completer_target': self._completer_target,
|
||||||
|
'command_arguments': self._arguments
|
||||||
|
} )
|
||||||
|
self._response = self.PostDataToHandler( request_data,
|
||||||
|
'run_completer_command' )
|
||||||
|
|
||||||
|
|
||||||
|
def Response( self ):
|
||||||
|
# TODO: Call vimsupport.JumpToLocation if the user called a GoTo command...
|
||||||
|
# we may want to have specific subclasses of CommandRequest so that a
|
||||||
|
# GoToRequest knows it needs to jump after the data comes back.
|
||||||
|
#
|
||||||
|
# Also need to run the following on GoTo data:
|
||||||
|
#
|
||||||
|
# CAREFUL about line/column number 0-based/1-based confusion!
|
||||||
|
#
|
||||||
|
# defs = []
|
||||||
|
# defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
|
||||||
|
# 'lnum': definition.line,
|
||||||
|
# 'col': definition.column + 1,
|
||||||
|
# 'text': definition.description.encode( 'utf-8' ) } )
|
||||||
|
# vim.eval( 'setqflist( %s )' % repr( defs ) )
|
||||||
|
# vim.eval( 'youcompleteme#OpenGoToList()' )
|
||||||
|
return self.ServerResponse()
|
||||||
|
|
74
python/ycm/client/completion_request.py
Normal file
74
python/ycm/client/completion_request.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 ycm import base
|
||||||
|
from ycm import vimsupport
|
||||||
|
from ycm.client.base_request import BaseRequest, BuildRequestData
|
||||||
|
|
||||||
|
|
||||||
|
class CompletionRequest( BaseRequest ):
|
||||||
|
def __init__( self ):
|
||||||
|
super( CompletionRequest, self ).__init__()
|
||||||
|
|
||||||
|
self._completion_start_column = base.CompletionStartColumn()
|
||||||
|
self._request_data = BuildRequestData( self._completion_start_column )
|
||||||
|
|
||||||
|
|
||||||
|
# def ShouldComplete( self ):
|
||||||
|
# return ( self._do_filetype_completion or
|
||||||
|
# self._ycm_state.ShouldUseGeneralCompleter( self._request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
def CompletionStartColumn( self ):
|
||||||
|
return self._completion_start_column
|
||||||
|
|
||||||
|
|
||||||
|
def Start( self, query ):
|
||||||
|
self._request_data[ 'query' ] = query
|
||||||
|
self._response = self.PostDataToHandler( self._request_data,
|
||||||
|
'get_completions' )
|
||||||
|
|
||||||
|
|
||||||
|
def Results( self ):
|
||||||
|
if not self._response:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
return [ _ConvertCompletionDataToVimData( x ) for x in self._response ]
|
||||||
|
except Exception as e:
|
||||||
|
vimsupport.PostVimMessage( str( e ) )
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _ConvertCompletionDataToVimData( completion_data ):
|
||||||
|
# see :h complete-items for a description of the dictionary fields
|
||||||
|
vim_data = {
|
||||||
|
'word' : completion_data[ 'insertion_text' ],
|
||||||
|
'dup' : 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'menu_text' in completion_data:
|
||||||
|
vim_data[ 'abbr' ] = completion_data[ 'menu_text' ]
|
||||||
|
if 'extra_menu_info' in completion_data:
|
||||||
|
vim_data[ 'menu' ] = completion_data[ 'extra_menu_info' ]
|
||||||
|
if 'kind' in completion_data:
|
||||||
|
vim_data[ 'kind' ] = completion_data[ 'kind' ]
|
||||||
|
if 'detailed_info' in completion_data:
|
||||||
|
vim_data[ 'info' ] = completion_data[ 'detailed_info' ]
|
||||||
|
|
||||||
|
return vim_data
|
42
python/ycm/client/event_notification.py
Normal file
42
python/ycm/client/event_notification.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 ycm.client.base_request import BaseRequest, BuildRequestData
|
||||||
|
|
||||||
|
|
||||||
|
class EventNotification( BaseRequest ):
|
||||||
|
def __init__( self, event_name, extra_data = None ):
|
||||||
|
super( EventNotification, self ).__init__()
|
||||||
|
self._event_name = event_name
|
||||||
|
self._extra_data = extra_data
|
||||||
|
|
||||||
|
|
||||||
|
def Start( self ):
|
||||||
|
request_data = BuildRequestData()
|
||||||
|
if self._extra_data:
|
||||||
|
request_data.update( self._extra_data )
|
||||||
|
request_data[ 'event_name' ] = self._event_name
|
||||||
|
|
||||||
|
self.PostDataToHandler( request_data, 'event_notification' )
|
||||||
|
|
||||||
|
|
||||||
|
def SendEventNotificationAsync( event_name, extra_data = None ):
|
||||||
|
event = EventNotification( event_name, extra_data )
|
||||||
|
event.Start()
|
||||||
|
|
@ -18,12 +18,14 @@
|
|||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
import ycm_core
|
import ycm_core
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from ycm.completers.general_completer import GeneralCompleter
|
from ycm.completers.general_completer import GeneralCompleter
|
||||||
# from ycm.completers.general import syntax_parse
|
# from ycm.completers.general import syntax_parse
|
||||||
from ycm import utils
|
from ycm import utils
|
||||||
from ycm import server_responses
|
from ycm.utils import ToUtf8IfNeeded
|
||||||
|
from ycm.server import responses
|
||||||
|
|
||||||
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
|
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
|
||||||
SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX'
|
SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX'
|
||||||
@ -36,6 +38,7 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
self.completer.EnableThreading()
|
self.completer.EnableThreading()
|
||||||
self.tags_file_last_mtime = defaultdict( int )
|
self.tags_file_last_mtime = defaultdict( int )
|
||||||
self.filetypes_with_keywords_loaded = set()
|
self.filetypes_with_keywords_loaded = set()
|
||||||
|
self._logger = logging.getLogger( __name__ )
|
||||||
|
|
||||||
|
|
||||||
def ShouldUseNow( self, request_data ):
|
def ShouldUseNow( self, request_data ):
|
||||||
@ -44,8 +47,8 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
|
|
||||||
def CandidatesForQueryAsync( self, request_data ):
|
def CandidatesForQueryAsync( self, request_data ):
|
||||||
self.completions_future = self.completer.CandidatesForQueryAndTypeAsync(
|
self.completions_future = self.completer.CandidatesForQueryAndTypeAsync(
|
||||||
utils.SanitizeQuery( request_data[ 'query' ] ),
|
ToUtf8IfNeeded( utils.SanitizeQuery( request_data[ 'query' ] ) ),
|
||||||
request_data[ 'filetypes' ][ 0 ] )
|
ToUtf8IfNeeded( request_data[ 'filetypes' ][ 0 ] ) )
|
||||||
|
|
||||||
|
|
||||||
def AddIdentifier( self, identifier, request_data ):
|
def AddIdentifier( self, identifier, request_data ):
|
||||||
@ -56,10 +59,11 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
return
|
return
|
||||||
|
|
||||||
vector = ycm_core.StringVec()
|
vector = ycm_core.StringVec()
|
||||||
vector.append( identifier )
|
vector.append( ToUtf8IfNeeded( identifier ) )
|
||||||
|
self._logger.info( 'Adding ONE buffer identifier for file: %s', filepath )
|
||||||
self.completer.AddIdentifiersToDatabase( vector,
|
self.completer.AddIdentifiersToDatabase( vector,
|
||||||
filetype,
|
ToUtf8IfNeeded( filetype ),
|
||||||
filepath )
|
ToUtf8IfNeeded( filepath ) )
|
||||||
|
|
||||||
|
|
||||||
def AddPreviousIdentifier( self, request_data ):
|
def AddPreviousIdentifier( self, request_data ):
|
||||||
@ -88,10 +92,11 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
return
|
return
|
||||||
|
|
||||||
text = request_data[ 'file_data' ][ filepath ][ 'contents' ]
|
text = request_data[ 'file_data' ][ filepath ][ 'contents' ]
|
||||||
|
self._logger.info( 'Adding buffer identifiers for file: %s', filepath )
|
||||||
self.completer.AddIdentifiersToDatabaseFromBufferAsync(
|
self.completer.AddIdentifiersToDatabaseFromBufferAsync(
|
||||||
text,
|
ToUtf8IfNeeded( text ),
|
||||||
filetype,
|
ToUtf8IfNeeded( filetype ),
|
||||||
filepath,
|
ToUtf8IfNeeded( filepath ),
|
||||||
collect_from_comments_and_strings )
|
collect_from_comments_and_strings )
|
||||||
|
|
||||||
|
|
||||||
@ -110,7 +115,7 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
self.tags_file_last_mtime[ tag_file ] = current_mtime
|
self.tags_file_last_mtime[ tag_file ] = current_mtime
|
||||||
absolute_paths_to_tag_files.append( tag_file )
|
absolute_paths_to_tag_files.append( ToUtf8IfNeeded( tag_file ) )
|
||||||
|
|
||||||
if not absolute_paths_to_tag_files:
|
if not absolute_paths_to_tag_files:
|
||||||
return
|
return
|
||||||
@ -161,7 +166,7 @@ class IdentifierCompleter( GeneralCompleter ):
|
|||||||
completions = _RemoveSmallCandidates(
|
completions = _RemoveSmallCandidates(
|
||||||
completions, self.user_options[ 'min_num_identifier_candidate_chars' ] )
|
completions, self.user_options[ 'min_num_identifier_candidate_chars' ] )
|
||||||
|
|
||||||
return [ server_responses.BuildCompletionData( x ) for x in completions ]
|
return [ responses.BuildCompletionData( x ) for x in completions ]
|
||||||
|
|
||||||
|
|
||||||
def _PreviousIdentifier( min_num_completion_start_chars, request_data ):
|
def _PreviousIdentifier( min_num_completion_start_chars, request_data ):
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import ycm_core
|
import ycm_core
|
||||||
import logging
|
import logging
|
||||||
from ycm import server_responses
|
from ycm.server import responses
|
||||||
from ycm import extra_conf_store
|
from ycm import extra_conf_store
|
||||||
from ycm.utils import ToUtf8IfNeeded
|
from ycm.utils import ToUtf8IfNeeded
|
||||||
from ycm.completers.completer import Completer
|
from ycm.completers.completer import Completer
|
||||||
@ -72,9 +72,10 @@ class ClangCompleter( Completer ):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
unsaved_file = ycm_core.UnsavedFile()
|
unsaved_file = ycm_core.UnsavedFile()
|
||||||
unsaved_file.contents_ = contents
|
utf8_contents = ToUtf8IfNeeded( contents )
|
||||||
unsaved_file.length_ = len( contents )
|
unsaved_file.contents_ = utf8_contents
|
||||||
unsaved_file.filename_ = filename
|
unsaved_file.length_ = len( utf8_contents )
|
||||||
|
unsaved_file.filename_ = ToUtf8IfNeeded( filename )
|
||||||
|
|
||||||
files.append( unsaved_file )
|
files.append( unsaved_file )
|
||||||
return files
|
return files
|
||||||
@ -86,17 +87,17 @@ class ClangCompleter( Completer ):
|
|||||||
if not filename:
|
if not filename:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.completer.UpdatingTranslationUnit( filename ):
|
if self.completer.UpdatingTranslationUnit( ToUtf8IfNeeded( filename ) ):
|
||||||
self.completions_future = None
|
self.completions_future = None
|
||||||
self._logger.info( PARSING_FILE_MESSAGE )
|
self._logger.info( PARSING_FILE_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
PARSING_FILE_MESSAGE )
|
PARSING_FILE_MESSAGE )
|
||||||
|
|
||||||
flags = self.flags.FlagsForFile( filename )
|
flags = self.flags.FlagsForFile( filename )
|
||||||
if not flags:
|
if not flags:
|
||||||
self.completions_future = None
|
self.completions_future = None
|
||||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
NO_COMPILE_FLAGS_MESSAGE )
|
NO_COMPILE_FLAGS_MESSAGE )
|
||||||
|
|
||||||
# TODO: sanitize query, probably in C++ code
|
# TODO: sanitize query, probably in C++ code
|
||||||
@ -142,11 +143,11 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
command = arguments[ 0 ]
|
command = arguments[ 0 ]
|
||||||
if command == 'GoToDefinition':
|
if command == 'GoToDefinition':
|
||||||
self._GoToDefinition( request_data )
|
return self._GoToDefinition( request_data )
|
||||||
elif command == 'GoToDeclaration':
|
elif command == 'GoToDeclaration':
|
||||||
self._GoToDeclaration( request_data )
|
return self._GoToDeclaration( request_data )
|
||||||
elif command == 'GoToDefinitionElseDeclaration':
|
elif command == 'GoToDefinitionElseDeclaration':
|
||||||
self._GoToDefinitionElseDeclaration( request_data )
|
return self._GoToDefinitionElseDeclaration( request_data )
|
||||||
elif command == 'ClearCompilationFlagCache':
|
elif command == 'ClearCompilationFlagCache':
|
||||||
self._ClearCompilationFlagCache( request_data )
|
self._ClearCompilationFlagCache( request_data )
|
||||||
|
|
||||||
@ -155,20 +156,20 @@ class ClangCompleter( Completer ):
|
|||||||
filename = request_data[ 'filepath' ]
|
filename = request_data[ 'filepath' ]
|
||||||
if not filename:
|
if not filename:
|
||||||
self._logger.warning( INVALID_FILE_MESSAGE )
|
self._logger.warning( INVALID_FILE_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
INVALID_FILE_MESSAGE )
|
INVALID_FILE_MESSAGE )
|
||||||
|
|
||||||
flags = self.flags.FlagsForFile( filename )
|
flags = self.flags.FlagsForFile( filename )
|
||||||
if not flags:
|
if not flags:
|
||||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
NO_COMPILE_FLAGS_MESSAGE )
|
NO_COMPILE_FLAGS_MESSAGE )
|
||||||
|
|
||||||
files = self.GetUnsavedFilesVector()
|
files = self.GetUnsavedFilesVector( request_data )
|
||||||
line = request_data[ 'line_num' ] + 1
|
line = request_data[ 'line_num' ] + 1
|
||||||
column = request_data[ 'start_column' ] + 1
|
column = request_data[ 'start_column' ] + 1
|
||||||
return getattr( self.completer, goto_function )(
|
return getattr( self.completer, goto_function )(
|
||||||
filename,
|
ToUtf8IfNeeded( filename ),
|
||||||
line,
|
line,
|
||||||
column,
|
column,
|
||||||
files,
|
files,
|
||||||
@ -180,9 +181,9 @@ class ClangCompleter( Completer ):
|
|||||||
if not location or not location.IsValid():
|
if not location or not location.IsValid():
|
||||||
raise RuntimeError( 'Can\'t jump to definition.' )
|
raise RuntimeError( 'Can\'t jump to definition.' )
|
||||||
|
|
||||||
return server_responses.BuildGoToResponse( location.filename_,
|
return responses.BuildGoToResponse( location.filename_,
|
||||||
location.line_number_,
|
location.line_number_,
|
||||||
location.column_number_ )
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
def _GoToDeclaration( self, request_data ):
|
def _GoToDeclaration( self, request_data ):
|
||||||
@ -190,9 +191,9 @@ class ClangCompleter( Completer ):
|
|||||||
if not location or not location.IsValid():
|
if not location or not location.IsValid():
|
||||||
raise RuntimeError( 'Can\'t jump to declaration.' )
|
raise RuntimeError( 'Can\'t jump to declaration.' )
|
||||||
|
|
||||||
return server_responses.BuildGoToResponse( location.filename_,
|
return responses.BuildGoToResponse( location.filename_,
|
||||||
location.line_number_,
|
location.line_number_,
|
||||||
location.column_number_ )
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
def _GoToDefinitionElseDeclaration( self, request_data ):
|
def _GoToDefinitionElseDeclaration( self, request_data ):
|
||||||
@ -202,9 +203,9 @@ class ClangCompleter( Completer ):
|
|||||||
if not location or not location.IsValid():
|
if not location or not location.IsValid():
|
||||||
raise RuntimeError( 'Can\'t jump to definition or declaration.' )
|
raise RuntimeError( 'Can\'t jump to definition or declaration.' )
|
||||||
|
|
||||||
return server_responses.BuildGoToResponse( location.filename_,
|
return responses.BuildGoToResponse( location.filename_,
|
||||||
location.line_number_,
|
location.line_number_,
|
||||||
location.column_number_ )
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -223,10 +224,10 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
if not filename:
|
if not filename:
|
||||||
self._logger.warning( INVALID_FILE_MESSAGE )
|
self._logger.warning( INVALID_FILE_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
INVALID_FILE_MESSAGE )
|
INVALID_FILE_MESSAGE )
|
||||||
|
|
||||||
if self.completer.UpdatingTranslationUnit( filename ):
|
if self.completer.UpdatingTranslationUnit( ToUtf8IfNeeded( filename ) ):
|
||||||
self.extra_parse_desired = True
|
self.extra_parse_desired = True
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -234,11 +235,11 @@ class ClangCompleter( Completer ):
|
|||||||
if not flags:
|
if not flags:
|
||||||
self.parse_future = None
|
self.parse_future = None
|
||||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
NO_COMPILE_FLAGS_MESSAGE )
|
NO_COMPILE_FLAGS_MESSAGE )
|
||||||
|
|
||||||
self.parse_future = self.completer.UpdateTranslationUnitAsync(
|
self.parse_future = self.completer.UpdateTranslationUnitAsync(
|
||||||
filename,
|
ToUtf8IfNeeded( filename ),
|
||||||
self.GetUnsavedFilesVector( request_data ),
|
self.GetUnsavedFilesVector( request_data ),
|
||||||
flags )
|
flags )
|
||||||
|
|
||||||
@ -246,7 +247,8 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
|
|
||||||
def OnBufferUnload( self, request_data ):
|
def OnBufferUnload( self, request_data ):
|
||||||
self.completer.DeleteCachesForFileAsync( request_data[ 'unloaded_buffer' ] )
|
self.completer.DeleteCachesForFileAsync(
|
||||||
|
ToUtf8IfNeeded( request_data[ 'unloaded_buffer' ] ) )
|
||||||
|
|
||||||
|
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
def DiagnosticsForCurrentFileReady( self ):
|
||||||
@ -257,16 +259,18 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
|
|
||||||
def GettingCompletions( self, request_data ):
|
def GettingCompletions( self, request_data ):
|
||||||
return self.completer.UpdatingTranslationUnit( request_data[ 'filepath' ] )
|
return self.completer.UpdatingTranslationUnit(
|
||||||
|
ToUtf8IfNeeded( request_data[ 'filepath' ] ) )
|
||||||
|
|
||||||
|
|
||||||
def GetDiagnosticsForCurrentFile( self, request_data ):
|
def GetDiagnosticsForCurrentFile( self, request_data ):
|
||||||
filename = request_data[ 'filepath' ]
|
filename = request_data[ 'filepath' ]
|
||||||
if self.DiagnosticsForCurrentFileReady():
|
if self.DiagnosticsForCurrentFileReady():
|
||||||
diagnostics = self.completer.DiagnosticsForFile( filename )
|
diagnostics = self.completer.DiagnosticsForFile(
|
||||||
|
ToUtf8IfNeeded( filename ) )
|
||||||
self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
|
self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
|
||||||
self.last_prepared_diagnostics = [
|
self.last_prepared_diagnostics = [
|
||||||
server_responses.BuildDiagnosticData( x ) for x in
|
responses.BuildDiagnosticData( x ) for x in
|
||||||
diagnostics[ : self.max_diagnostics_to_display ] ]
|
diagnostics[ : self.max_diagnostics_to_display ] ]
|
||||||
self.parse_future = None
|
self.parse_future = None
|
||||||
|
|
||||||
@ -282,12 +286,12 @@ class ClangCompleter( Completer ):
|
|||||||
current_file = request_data[ 'filepath' ]
|
current_file = request_data[ 'filepath' ]
|
||||||
|
|
||||||
if not self.diagnostic_store:
|
if not self.diagnostic_store:
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
NO_DIAGNOSTIC_MESSAGE )
|
NO_DIAGNOSTIC_MESSAGE )
|
||||||
|
|
||||||
diagnostics = self.diagnostic_store[ current_file ][ current_line ]
|
diagnostics = self.diagnostic_store[ current_file ][ current_line ]
|
||||||
if not diagnostics:
|
if not diagnostics:
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
NO_DIAGNOSTIC_MESSAGE )
|
NO_DIAGNOSTIC_MESSAGE )
|
||||||
|
|
||||||
closest_diagnostic = None
|
closest_diagnostic = None
|
||||||
@ -299,7 +303,7 @@ class ClangCompleter( Completer ):
|
|||||||
distance_to_closest_diagnostic = distance
|
distance_to_closest_diagnostic = distance
|
||||||
closest_diagnostic = diagnostic
|
closest_diagnostic = diagnostic
|
||||||
|
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
closest_diagnostic.long_formatted_text_ )
|
closest_diagnostic.long_formatted_text_ )
|
||||||
|
|
||||||
|
|
||||||
@ -314,7 +318,7 @@ class ClangCompleter( Completer ):
|
|||||||
return ''
|
return ''
|
||||||
flags = self.flags.FlagsForFile( filename ) or []
|
flags = self.flags.FlagsForFile( filename ) or []
|
||||||
source = extra_conf_store.ModuleFileForSourceFile( filename )
|
source = extra_conf_store.ModuleFileForSourceFile( filename )
|
||||||
return server_responses.BuildDisplayMessageResponse(
|
return responses.BuildDisplayMessageResponse(
|
||||||
'Flags for {0} loaded from {1}:\n{2}'.format( filename,
|
'Flags for {0} loaded from {1}:\n{2}'.format( filename,
|
||||||
source,
|
source,
|
||||||
list( flags ) ) )
|
list( flags ) ) )
|
||||||
@ -348,7 +352,7 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
|
|
||||||
def ConvertCompletionData( completion_data ):
|
def ConvertCompletionData( completion_data ):
|
||||||
return server_responses.BuildCompletionData(
|
return responses.BuildCompletionData(
|
||||||
insertion_text = completion_data.TextToInsertInBuffer(),
|
insertion_text = completion_data.TextToInsertInBuffer(),
|
||||||
menu_text = completion_data.MainCompletionText(),
|
menu_text = completion_data.MainCompletionText(),
|
||||||
extra_menu_info = completion_data.ExtraMenuInfo(),
|
extra_menu_info = completion_data.ExtraMenuInfo(),
|
||||||
|
@ -22,13 +22,13 @@ import os
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
import glob
|
import glob
|
||||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||||
from ycm import server_responses
|
from ycm.server import responses
|
||||||
|
from ycm import utils
|
||||||
import urllib2
|
import urllib2
|
||||||
import urllib
|
import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class CsharpCompleter( ThreadedCompleter ):
|
|||||||
def __init__( self, user_options ):
|
def __init__( self, user_options ):
|
||||||
super( CsharpCompleter, self ).__init__( user_options )
|
super( CsharpCompleter, self ).__init__( user_options )
|
||||||
self._omnisharp_port = None
|
self._omnisharp_port = None
|
||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger( __name__ )
|
||||||
|
|
||||||
# if self.user_options[ 'auto_start_csharp_server' ]:
|
# if self.user_options[ 'auto_start_csharp_server' ]:
|
||||||
# self._StartServer()
|
# self._StartServer()
|
||||||
@ -62,7 +62,7 @@ class CsharpCompleter( ThreadedCompleter ):
|
|||||||
|
|
||||||
|
|
||||||
def ComputeCandidates( self, request_data ):
|
def ComputeCandidates( self, request_data ):
|
||||||
return [ server_responses.BuildCompletionData(
|
return [ responses.BuildCompletionData(
|
||||||
completion[ 'CompletionText' ],
|
completion[ 'CompletionText' ],
|
||||||
completion[ 'DisplayText' ],
|
completion[ 'DisplayText' ],
|
||||||
completion[ 'Description' ] )
|
completion[ 'Description' ] )
|
||||||
@ -135,8 +135,8 @@ class CsharpCompleter( ThreadedCompleter ):
|
|||||||
command = [ omnisharp + ' -p ' + str( self._omnisharp_port ) + ' -s ' +
|
command = [ omnisharp + ' -p ' + str( self._omnisharp_port ) + ' -s ' +
|
||||||
path_to_solutionfile ]
|
path_to_solutionfile ]
|
||||||
|
|
||||||
filename_format = ( tempfile.gettempdir() +
|
filename_format = os.path.join( utils.PathToTempDir(),
|
||||||
'/omnisharp_{port}_{sln}_{std}.log' )
|
'omnisharp_{port}_{sln}_{std}.log' )
|
||||||
|
|
||||||
self._filename_stdout = filename_format.format(
|
self._filename_stdout = filename_format.format(
|
||||||
port=self._omnisharp_port, sln=solutionfile, std='stdout' )
|
port=self._omnisharp_port, sln=solutionfile, std='stdout' )
|
||||||
@ -169,9 +169,9 @@ class CsharpCompleter( ThreadedCompleter ):
|
|||||||
definition = self._GetResponse( '/gotodefinition',
|
definition = self._GetResponse( '/gotodefinition',
|
||||||
self._DefaultParameters( request_data ) )
|
self._DefaultParameters( request_data ) )
|
||||||
if definition[ 'FileName' ] != None:
|
if definition[ 'FileName' ] != None:
|
||||||
return server_responses.BuildGoToResponse( definition[ 'FileName' ],
|
return responses.BuildGoToResponse( definition[ 'FileName' ],
|
||||||
definition[ 'Line' ],
|
definition[ 'Line' ],
|
||||||
definition[ 'Column' ] )
|
definition[ 'Column' ] )
|
||||||
else:
|
else:
|
||||||
raise RuntimeError( 'Can\'t jump to definition' )
|
raise RuntimeError( 'Can\'t jump to definition' )
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import re
|
|||||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||||
from ycm.completers.cpp.clang_completer import InCFamilyFile
|
from ycm.completers.cpp.clang_completer import InCFamilyFile
|
||||||
from ycm.completers.cpp.flags import Flags
|
from ycm.completers.cpp.flags import Flags
|
||||||
from ycm import server_responses
|
from ycm.server import responses
|
||||||
|
|
||||||
class FilenameCompleter( ThreadedCompleter ):
|
class FilenameCompleter( ThreadedCompleter ):
|
||||||
"""
|
"""
|
||||||
@ -146,7 +146,7 @@ def _GenerateCandidatesForPaths( absolute_paths ):
|
|||||||
|
|
||||||
is_dir = os.path.isdir( absolute_path )
|
is_dir = os.path.isdir( absolute_path )
|
||||||
completion_dicts.append(
|
completion_dicts.append(
|
||||||
server_responses.BuildCompletionData( basename,
|
responses.BuildCompletionData( basename,
|
||||||
'[Dir]' if is_dir else '[File]' ) )
|
'[Dir]' if is_dir else '[File]' ) )
|
||||||
|
|
||||||
return completion_dicts
|
return completion_dicts
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
from ycm.completers.general_completer import GeneralCompleter
|
from ycm.completers.general_completer import GeneralCompleter
|
||||||
from UltiSnips import UltiSnips_Manager
|
from UltiSnips import UltiSnips_Manager
|
||||||
from ycm import server_responses
|
from ycm.server import responses
|
||||||
|
|
||||||
|
|
||||||
class UltiSnipsCompleter( GeneralCompleter ):
|
class UltiSnipsCompleter( GeneralCompleter ):
|
||||||
@ -64,7 +64,7 @@ def _GetCandidates():
|
|||||||
# UltiSnips_Manager._snips() returns a class instance where:
|
# UltiSnips_Manager._snips() returns a class instance where:
|
||||||
# class.trigger - name of snippet trigger word ( e.g. defn or testcase )
|
# class.trigger - name of snippet trigger word ( e.g. defn or testcase )
|
||||||
# class.description - description of the snippet
|
# class.description - description of the snippet
|
||||||
return [ server_responses.BuildCompletionData(
|
return [ responses.BuildCompletionData(
|
||||||
str( snip.trigger ),
|
str( snip.trigger ),
|
||||||
str( '<snip> ' + snip.description.encode( 'utf-8' ) ) )
|
str( '<snip> ' + snip.description.encode( 'utf-8' ) ) )
|
||||||
for snip in rawsnips ]
|
for snip in rawsnips ]
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||||
from ycm import server_responses
|
from ycm.server import responses
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from os.path import join, abspath, dirname
|
from os.path import join, abspath, dirname
|
||||||
@ -65,7 +65,7 @@ class JediCompleter( ThreadedCompleter ):
|
|||||||
|
|
||||||
def ComputeCandidates( self, request_data ):
|
def ComputeCandidates( self, request_data ):
|
||||||
script = self._GetJediScript( request_data )
|
script = self._GetJediScript( request_data )
|
||||||
return [ server_responses.BuildCompletionData(
|
return [ responses.BuildCompletionData(
|
||||||
str( completion.name ),
|
str( completion.name ),
|
||||||
str( completion.description ),
|
str( completion.description ),
|
||||||
str( completion.doc ) )
|
str( completion.doc ) )
|
||||||
@ -140,21 +140,21 @@ class JediCompleter( ThreadedCompleter ):
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError( 'Builtin modules cannot be displayed.' )
|
raise RuntimeError( 'Builtin modules cannot be displayed.' )
|
||||||
else:
|
else:
|
||||||
return server_responses.BuildGoToResponse( definition.module_path,
|
return responses.BuildGoToResponse( definition.module_path,
|
||||||
definition.line -1,
|
definition.line -1,
|
||||||
definition.column )
|
definition.column )
|
||||||
else:
|
else:
|
||||||
# multiple definitions
|
# multiple definitions
|
||||||
defs = []
|
defs = []
|
||||||
for definition in definition_list:
|
for definition in definition_list:
|
||||||
if definition.in_builtin_module():
|
if definition.in_builtin_module():
|
||||||
defs.append( server_responses.BuildDescriptionOnlyGoToResponse(
|
defs.append( responses.BuildDescriptionOnlyGoToResponse(
|
||||||
'Builting ' + definition.description ) )
|
'Builting ' + definition.description ) )
|
||||||
else:
|
else:
|
||||||
defs.append(
|
defs.append(
|
||||||
server_responses.BuildGoToResponse( definition.module_path,
|
responses.BuildGoToResponse( definition.module_path,
|
||||||
definition.line -1,
|
definition.line -1,
|
||||||
definition.column,
|
definition.column,
|
||||||
definition.description ) )
|
definition.description ) )
|
||||||
return defs
|
return defs
|
||||||
|
|
||||||
|
0
python/ycm/server/__init__.py
Normal file
0
python/ycm/server/__init__.py
Normal file
1
python/ycm/server/default_settings.json
Normal file
1
python/ycm/server/default_settings.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{
"filepath_completion_use_working_dir": 0,
"min_num_of_chars_for_completion": 2,
"semantic_triggers": {},
"collect_identifiers_from_comments_and_strings": 0,
"filetype_specific_completion_to_disable": {},
"collect_identifiers_from_tags_files": 0,
"extra_conf_globlist": [
"~\/repos\/*",
"\/home\/strahinja\/googrepos\/*",
"~\/local_googrepos\/*",
"~\/.ycm_extra_conf.py"
],
"global_ycm_extra_conf": "\/usr\/lib\/youcompleteme\/ycm_extra_conf.py",
"confirm_extra_conf": 1,
"complete_in_comments": 0,
"complete_in_strings": 1,
"min_num_identifier_candidate_chars": 0,
"max_diagnostics_to_display": 30,
"auto_stop_csharp_server": 1,
"seed_identifiers_with_syntax": 0,
"csharp_server_port": 2000,
"filetype_whitelist": {
"*": "1"
},
"auto_start_csharp_server": 1,
"filetype_blacklist": {
"tagbar": "1",
"notes": "1",
"markdown": "1",
"unite": "1",
"text": "1"
}
}
|
@ -18,6 +18,8 @@
|
|||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Move this file under server/ and rename it responses.py
|
||||||
|
|
||||||
def BuildGoToResponse( filepath, line_num, column_num, description = None ):
|
def BuildGoToResponse( filepath, line_num, column_num, description = None ):
|
||||||
response = {
|
response = {
|
||||||
'filepath': filepath,
|
'filepath': filepath,
|
168
python/ycm/server/server.py
Executable file
168
python/ycm/server/server.py
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# We want to have the YouCompleteMe/python directory on the Python PATH because
|
||||||
|
# all the code already assumes that it's there. This is a relic from before the
|
||||||
|
# client/server architecture.
|
||||||
|
# TODO: Fix things so that this is not needed anymore when we split ycmd into a
|
||||||
|
# separate repository.
|
||||||
|
sys.path.insert( 0, os.path.join(
|
||||||
|
os.path.dirname( os.path.abspath( __file__ ) ),
|
||||||
|
'../..' ) )
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
import httplib
|
||||||
|
import json
|
||||||
|
import bottle
|
||||||
|
from bottle import run, request, response
|
||||||
|
import server_state
|
||||||
|
from ycm import extra_conf_store
|
||||||
|
from ycm import user_options_store
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# num bytes for the request body buffer; request.json only works if the request
|
||||||
|
# size is less than this
|
||||||
|
bottle.Request.MEMFILE_MAX = 300 * 1024
|
||||||
|
|
||||||
|
user_options_store.LoadDefaults()
|
||||||
|
SERVER_STATE = server_state.ServerState( user_options_store.GetAll() )
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger( __name__ )
|
||||||
|
app = bottle.Bottle()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/event_notification' )
|
||||||
|
def EventNotification():
|
||||||
|
LOGGER.info( 'Received event notification')
|
||||||
|
request_data = request.json
|
||||||
|
event_name = request_data[ 'event_name' ]
|
||||||
|
LOGGER.debug( 'Event name: %s', event_name )
|
||||||
|
|
||||||
|
event_handler = 'On' + event_name
|
||||||
|
getattr( SERVER_STATE.GetGeneralCompleter(), event_handler )( request_data )
|
||||||
|
|
||||||
|
filetypes = request_data[ 'filetypes' ]
|
||||||
|
if SERVER_STATE.FiletypeCompletionUsable( filetypes ):
|
||||||
|
getattr( SERVER_STATE.GetFiletypeCompleter( filetypes ),
|
||||||
|
event_handler )( request_data )
|
||||||
|
|
||||||
|
if hasattr( extra_conf_store, event_handler ):
|
||||||
|
getattr( extra_conf_store, event_handler )( request_data )
|
||||||
|
|
||||||
|
# TODO: shut down the server on VimClose
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/run_completer_command' )
|
||||||
|
def RunCompleterCommand():
|
||||||
|
LOGGER.info( 'Received command request')
|
||||||
|
request_data = request.json
|
||||||
|
completer_target = request_data[ 'completer_target' ]
|
||||||
|
|
||||||
|
if completer_target == 'identifier':
|
||||||
|
completer = SERVER_STATE.GetGeneralCompleter()
|
||||||
|
else:
|
||||||
|
completer = SERVER_STATE.GetFiletypeCompleter()
|
||||||
|
|
||||||
|
return _JsonResponse(
|
||||||
|
completer.OnUserCommand( request_data[ 'command_arguments' ],
|
||||||
|
request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/get_completions' )
|
||||||
|
def GetCompletions():
|
||||||
|
LOGGER.info( 'Received completion request')
|
||||||
|
request_data = request.json
|
||||||
|
do_filetype_completion = SERVER_STATE.ShouldUseFiletypeCompleter(
|
||||||
|
request_data )
|
||||||
|
LOGGER.debug( 'Using filetype completion: %s', do_filetype_completion )
|
||||||
|
filetypes = request_data[ 'filetypes' ]
|
||||||
|
completer = ( SERVER_STATE.GetFiletypeCompleter( filetypes ) if
|
||||||
|
do_filetype_completion else
|
||||||
|
SERVER_STATE.GetGeneralCompleter() )
|
||||||
|
|
||||||
|
# This is necessary so that general_completer_store fills up
|
||||||
|
# _current_query_completers.
|
||||||
|
# TODO: Fix this.
|
||||||
|
completer.ShouldUseNow( request_data )
|
||||||
|
|
||||||
|
# TODO: This should not be async anymore, server is multi-threaded
|
||||||
|
completer.CandidatesForQueryAsync( request_data )
|
||||||
|
while not completer.AsyncCandidateRequestReady():
|
||||||
|
time.sleep( 0.03 )
|
||||||
|
return _JsonResponse( completer.CandidatesFromStoredRequest() )
|
||||||
|
|
||||||
|
|
||||||
|
@app.route( '/user_options' )
|
||||||
|
def UserOptions():
|
||||||
|
global SERVER_STATE
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
LOGGER.info( 'Received user options GET request')
|
||||||
|
return SERVER_STATE.user_options
|
||||||
|
elif request.method == 'POST':
|
||||||
|
LOGGER.info( 'Received user options POST request')
|
||||||
|
data = request.json
|
||||||
|
SERVER_STATE = server_state.ServerState( data )
|
||||||
|
user_options_store.SetAll( data )
|
||||||
|
else:
|
||||||
|
response.status = httplib.BAD_REQUEST
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/filetype_completion_available')
|
||||||
|
def FiletypeCompletionAvailable():
|
||||||
|
LOGGER.info( 'Received filetype completion available request')
|
||||||
|
return _JsonResponse( SERVER_STATE.FiletypeCompletionAvailable(
|
||||||
|
request.json[ 'filetypes' ] ) )
|
||||||
|
|
||||||
|
|
||||||
|
def _JsonResponse( data ):
|
||||||
|
response.set_header( 'Content-Type', 'application/json' )
|
||||||
|
return json.dumps( data )
|
||||||
|
|
||||||
|
|
||||||
|
def Main():
|
||||||
|
global LOGGER
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument( '--host', type = str, default = 'localhost',
|
||||||
|
help='server hostname')
|
||||||
|
parser.add_argument( '--port', type = int, default = 6666,
|
||||||
|
help='server port')
|
||||||
|
parser.add_argument( '--log', type = str, default = 'info',
|
||||||
|
help='log level, one of '
|
||||||
|
'[debug|info|warning|error|critical]')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
numeric_level = getattr( logging, args.log.upper(), None )
|
||||||
|
if not isinstance( numeric_level, int ):
|
||||||
|
raise ValueError( 'Invalid log level: %s' % args.log )
|
||||||
|
|
||||||
|
logging.basicConfig( format = '%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
level = numeric_level )
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger( __name__ )
|
||||||
|
run( app = app, host = args.host, port = args.port, server='cherrypy' )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Main()
|
||||||
|
|
108
python/ycm/server/server_state.py
Normal file
108
python/ycm/server/server_state.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 imp
|
||||||
|
import os
|
||||||
|
from ycm.completers.general.general_completer_store import GeneralCompleterStore
|
||||||
|
|
||||||
|
|
||||||
|
class ServerState( object ):
|
||||||
|
def __init__( self, user_options ):
|
||||||
|
self._user_options = user_options
|
||||||
|
self._filetype_completers = {}
|
||||||
|
self._gencomp = GeneralCompleterStore( self._user_options )
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_options( self ):
|
||||||
|
return self._user_options
|
||||||
|
|
||||||
|
|
||||||
|
def _GetFiletypeCompleterForFiletype( self, filetype ):
|
||||||
|
try:
|
||||||
|
return self._filetype_completers[ filetype ]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
module_path = _PathToFiletypeCompleterPluginLoader( filetype )
|
||||||
|
|
||||||
|
completer = None
|
||||||
|
supported_filetypes = [ filetype ]
|
||||||
|
if os.path.exists( module_path ):
|
||||||
|
module = imp.load_source( filetype, module_path )
|
||||||
|
completer = module.GetCompleter( self._user_options )
|
||||||
|
if completer:
|
||||||
|
supported_filetypes.extend( completer.SupportedFiletypes() )
|
||||||
|
|
||||||
|
for supported_filetype in supported_filetypes:
|
||||||
|
self._filetype_completers[ supported_filetype ] = completer
|
||||||
|
return completer
|
||||||
|
|
||||||
|
|
||||||
|
def GetFiletypeCompleter( self, current_filetypes ):
|
||||||
|
completers = [ self._GetFiletypeCompleterForFiletype( filetype )
|
||||||
|
for filetype in current_filetypes ]
|
||||||
|
|
||||||
|
for completer in completers:
|
||||||
|
if completer:
|
||||||
|
return completer
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def FiletypeCompletionAvailable( self, filetypes ):
|
||||||
|
return bool( self.GetFiletypeCompleter( filetypes ) )
|
||||||
|
|
||||||
|
|
||||||
|
def FiletypeCompletionUsable( self, filetypes ):
|
||||||
|
return ( self.CurrentFiletypeCompletionEnabled( filetypes ) and
|
||||||
|
self.FiletypeCompletionAvailable( filetypes ) )
|
||||||
|
|
||||||
|
|
||||||
|
def ShouldUseGeneralCompleter( self, request_data ):
|
||||||
|
return self._gencomp.ShouldUseNow( request_data )
|
||||||
|
|
||||||
|
|
||||||
|
def ShouldUseFiletypeCompleter( self, request_data ):
|
||||||
|
filetypes = request_data[ 'filetypes' ]
|
||||||
|
if self.FiletypeCompletionUsable( filetypes ):
|
||||||
|
return self.GetFiletypeCompleter( filetypes ).ShouldUseNow( request_data )
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def GetGeneralCompleter( self ):
|
||||||
|
return self._gencomp
|
||||||
|
|
||||||
|
|
||||||
|
def CurrentFiletypeCompletionEnabled( self, current_filetypes ):
|
||||||
|
filetype_to_disable = self._user_options[
|
||||||
|
'filetype_specific_completion_to_disable' ]
|
||||||
|
return not all([ x in filetype_to_disable for x in current_filetypes ])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _PathToCompletersFolder():
|
||||||
|
dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) )
|
||||||
|
return os.path.join( dir_of_current_script, '..', 'completers' )
|
||||||
|
|
||||||
|
|
||||||
|
def _PathToFiletypeCompleterPluginLoader( filetype ):
|
||||||
|
return os.path.join( _PathToCompletersFolder(), filetype, 'hook.py' )
|
||||||
|
|
||||||
|
|
0
python/ycm/server/tests/__init__.py
Normal file
0
python/ycm/server/tests/__init__.py
Normal file
74
python/ycm/server/tests/basic_test.py
Normal file
74
python/ycm/server/tests/basic_test.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 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 webtest import TestApp
|
||||||
|
from .. import server
|
||||||
|
from ..responses import BuildCompletionData
|
||||||
|
from nose.tools import ok_, eq_
|
||||||
|
import bottle
|
||||||
|
|
||||||
|
bottle.debug( True )
|
||||||
|
|
||||||
|
|
||||||
|
def GetCompletions_IdentifierCompleterWorks_test():
|
||||||
|
app = TestApp( server.app )
|
||||||
|
event_data = {
|
||||||
|
'event_name': 'FileReadyToParse',
|
||||||
|
'filetypes': ['foo'],
|
||||||
|
'filepath': '/foo/bar',
|
||||||
|
'file_data': {
|
||||||
|
'/foo/bar': {
|
||||||
|
'contents': 'foo foogoo ba',
|
||||||
|
'filetypes': ['foo']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post_json( '/event_notification', event_data )
|
||||||
|
|
||||||
|
line_value = 'oo foo foogoo ba';
|
||||||
|
completion_data = {
|
||||||
|
'query': 'oo',
|
||||||
|
'filetypes': ['foo'],
|
||||||
|
'filepath': '/foo/bar',
|
||||||
|
'line_num': 0,
|
||||||
|
'column_num': 2,
|
||||||
|
'start_column': 0,
|
||||||
|
'line_value': line_value,
|
||||||
|
'file_data': {
|
||||||
|
'/foo/bar': {
|
||||||
|
'contents': line_value,
|
||||||
|
'filetypes': ['foo']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eq_( [ BuildCompletionData( 'foo' ),
|
||||||
|
BuildCompletionData( 'foogoo' ) ],
|
||||||
|
app.post_json( '/get_completions', completion_data ).json )
|
||||||
|
|
||||||
|
|
||||||
|
def FiletypeCompletionAvailable_Works_test():
|
||||||
|
app = TestApp( server.app )
|
||||||
|
request_data = {
|
||||||
|
'filetypes': ['cpp']
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_( app.post_json( '/filetype_completion_available',
|
||||||
|
request_data ).json )
|
@ -17,6 +17,8 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
from ycm.frozendict import frozendict
|
from ycm.frozendict import frozendict
|
||||||
|
|
||||||
_USER_OPTIONS = {}
|
_USER_OPTIONS = {}
|
||||||
@ -32,3 +34,16 @@ def GetAll():
|
|||||||
|
|
||||||
def Value( key ):
|
def Value( key ):
|
||||||
return _USER_OPTIONS[ key ]
|
return _USER_OPTIONS[ key ]
|
||||||
|
|
||||||
|
|
||||||
|
def LoadDefaults():
|
||||||
|
SetAll( _DefaultOptions() )
|
||||||
|
|
||||||
|
|
||||||
|
def _DefaultOptions():
|
||||||
|
settings_path = os.path.join(
|
||||||
|
os.path.dirname( os.path.abspath( __file__ ) ),
|
||||||
|
'server/default_settings.json' )
|
||||||
|
with open( settings_path ) as f:
|
||||||
|
return json.loads( f.read() )
|
||||||
|
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
|
||||||
def IsIdentifierChar( char ):
|
def IsIdentifierChar( char ):
|
||||||
return char.isalnum() or char == '_'
|
return char.isalnum() or char == '_'
|
||||||
|
|
||||||
@ -29,3 +32,7 @@ def ToUtf8IfNeeded( string_or_unicode ):
|
|||||||
if isinstance( string_or_unicode, unicode ):
|
if isinstance( string_or_unicode, unicode ):
|
||||||
return string_or_unicode.encode( 'utf8' )
|
return string_or_unicode.encode( 'utf8' )
|
||||||
return string_or_unicode
|
return string_or_unicode
|
||||||
|
|
||||||
|
|
||||||
|
def PathToTempDir():
|
||||||
|
return os.path.join( tempfile.gettempdir(), 'ycm_temp' )
|
||||||
|
@ -17,176 +17,53 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import imp
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import vim
|
import vim
|
||||||
import ycm_core
|
import ycm_core
|
||||||
import logging
|
import subprocess
|
||||||
import tempfile
|
|
||||||
from ycm import vimsupport
|
from ycm import vimsupport
|
||||||
from ycm import base
|
from ycm import utils
|
||||||
from ycm import extra_conf_store
|
|
||||||
from ycm.completers.all.omni_completer import OmniCompleter
|
from ycm.completers.all.omni_completer import OmniCompleter
|
||||||
from ycm.completers.general.general_completer_store import GeneralCompleterStore
|
from ycm.client.base_request import BaseRequest
|
||||||
|
from ycm.client.command_request import CommandRequest
|
||||||
|
from ycm.client.completion_request import CompletionRequest
|
||||||
# TODO: Put the Request classes in separate files
|
from ycm.client.event_notification import SendEventNotificationAsync
|
||||||
class BaseRequest( object ):
|
|
||||||
def __init__( self ):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def Start( self ):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def Done( self ):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionRequest( BaseRequest ):
|
|
||||||
def __init__( self, ycm_state ):
|
|
||||||
super( CompletionRequest, self ).__init__()
|
|
||||||
|
|
||||||
self._completion_start_column = base.CompletionStartColumn()
|
|
||||||
self._ycm_state = ycm_state
|
|
||||||
self._request_data = _BuildRequestData( self._completion_start_column )
|
|
||||||
self._do_filetype_completion = self._ycm_state.ShouldUseFiletypeCompleter(
|
|
||||||
self._request_data )
|
|
||||||
self._completer = ( self._ycm_state.GetFiletypeCompleter() if
|
|
||||||
self._do_filetype_completion else
|
|
||||||
self._ycm_state.GetGeneralCompleter() )
|
|
||||||
|
|
||||||
|
|
||||||
def ShouldComplete( self ):
|
|
||||||
return ( self._do_filetype_completion or
|
|
||||||
self._ycm_state.ShouldUseGeneralCompleter( self._request_data ) )
|
|
||||||
|
|
||||||
|
|
||||||
def CompletionStartColumn( self ):
|
|
||||||
return self._completion_start_column
|
|
||||||
|
|
||||||
|
|
||||||
def Start( self, query ):
|
|
||||||
self._request_data[ 'query' ] = query
|
|
||||||
self._completer.CandidatesForQueryAsync( self._request_data )
|
|
||||||
|
|
||||||
def Done( self ):
|
|
||||||
return self._completer.AsyncCandidateRequestReady()
|
|
||||||
|
|
||||||
|
|
||||||
def Results( self ):
|
|
||||||
try:
|
|
||||||
return [ _ConvertCompletionDataToVimData( x )
|
|
||||||
for x in self._completer.CandidatesFromStoredRequest() ]
|
|
||||||
except Exception as e:
|
|
||||||
vimsupport.PostVimMessage( str( e ) )
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CommandRequest( BaseRequest ):
|
|
||||||
class ServerResponse( object ):
|
|
||||||
def __init__( self ):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def Valid( self ):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __init__( self, ycm_state, arguments, completer_target = None ):
|
|
||||||
super( CommandRequest, self ).__init__()
|
|
||||||
|
|
||||||
if not completer_target:
|
|
||||||
completer_target = 'filetpe_default'
|
|
||||||
|
|
||||||
if completer_target == 'omni':
|
|
||||||
self._completer = ycm_state.GetOmniCompleter()
|
|
||||||
elif completer_target == 'identifier':
|
|
||||||
self._completer = ycm_state.GetGeneralCompleter()
|
|
||||||
else:
|
|
||||||
self._completer = ycm_state.GetFiletypeCompleter()
|
|
||||||
self._arguments = arguments
|
|
||||||
|
|
||||||
|
|
||||||
def Start( self ):
|
|
||||||
self._completer.OnUserCommand( self._arguments,
|
|
||||||
_BuildRequestData() )
|
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
|
||||||
# TODO: Call vimsupport.JumpToLocation if the user called a GoTo command...
|
|
||||||
# we may want to have specific subclasses of CommandRequest so that a
|
|
||||||
# GoToRequest knows it needs to jump after the data comes back.
|
|
||||||
#
|
|
||||||
# Also need to run the following on GoTo data:
|
|
||||||
# CAREFUL about line/column number 0-based/1-based confusion!
|
|
||||||
#
|
|
||||||
# defs = []
|
|
||||||
# defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
|
|
||||||
# 'lnum': definition.line,
|
|
||||||
# 'col': definition.column + 1,
|
|
||||||
# 'text': definition.description.encode( 'utf-8' ) } )
|
|
||||||
# vim.eval( 'setqflist( %s )' % repr( defs ) )
|
|
||||||
# vim.eval( 'youcompleteme#OpenGoToList()' )
|
|
||||||
return self.ServerResponse()
|
|
||||||
|
|
||||||
|
|
||||||
class EventNotification( BaseRequest ):
|
|
||||||
def __init__( self, event_name, ycm_state, extra_data = None ):
|
|
||||||
super( EventNotification, self ).__init__()
|
|
||||||
|
|
||||||
self._ycm_state = ycm_state
|
|
||||||
self._event_name = event_name
|
|
||||||
self._request_data = _BuildRequestData()
|
|
||||||
if extra_data:
|
|
||||||
self._request_data.update( extra_data )
|
|
||||||
|
|
||||||
|
|
||||||
def Start( self ):
|
|
||||||
event_handler = 'On' + self._event_name
|
|
||||||
getattr( self._ycm_state.GetGeneralCompleter(),
|
|
||||||
event_handler )( self._request_data )
|
|
||||||
|
|
||||||
if self._ycm_state.FiletypeCompletionUsable():
|
|
||||||
getattr( self._ycm_state.GetFiletypeCompleter(),
|
|
||||||
event_handler )( self._request_data )
|
|
||||||
|
|
||||||
if hasattr( extra_conf_store, event_handler ):
|
|
||||||
getattr( extra_conf_store, event_handler )( self._request_data )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SendEventNotificationAsync( event_name, ycm_state, extra_data = None ):
|
|
||||||
event = EventNotification( event_name, ycm_state, extra_data )
|
|
||||||
event.Start()
|
|
||||||
|
|
||||||
|
SERVER_PORT_RANGE_START = 10000
|
||||||
|
|
||||||
class YouCompleteMe( object ):
|
class YouCompleteMe( object ):
|
||||||
def __init__( self, user_options ):
|
def __init__( self, user_options ):
|
||||||
# TODO: This should go into the server
|
|
||||||
# TODO: Use more logging like we do in cs_completer
|
|
||||||
self._logfile = tempfile.NamedTemporaryFile()
|
|
||||||
logging.basicConfig( format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
filename=self._logfile.name,
|
|
||||||
level=logging.DEBUG )
|
|
||||||
|
|
||||||
self._user_options = user_options
|
self._user_options = user_options
|
||||||
self._gencomp = GeneralCompleterStore( user_options )
|
|
||||||
self._omnicomp = OmniCompleter( user_options )
|
self._omnicomp = OmniCompleter( user_options )
|
||||||
self._filetype_completers = {}
|
|
||||||
self._current_completion_request = None
|
self._current_completion_request = None
|
||||||
|
|
||||||
|
server_port = SERVER_PORT_RANGE_START + os.getpid()
|
||||||
|
command = ''.join( [ 'python ',
|
||||||
|
_PathToServerScript(),
|
||||||
|
' --port=',
|
||||||
|
str( server_port ) ] )
|
||||||
|
|
||||||
|
BaseRequest.server_location = 'http://localhost:' + str( server_port )
|
||||||
|
|
||||||
|
filename_format = os.path.join( utils.PathToTempDir(),
|
||||||
|
'server_{port}_{std}.log' )
|
||||||
|
|
||||||
|
self._server_stdout = filename_format.format( port=server_port,
|
||||||
|
std='stdout' )
|
||||||
|
self._server_stderr = filename_format.format( port=server_port,
|
||||||
|
std='stderr' )
|
||||||
|
|
||||||
|
with open( self._server_stderr, 'w' ) as fstderr:
|
||||||
|
with open( self._server_stdout, 'w' ) as fstdout:
|
||||||
|
subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True )
|
||||||
|
|
||||||
|
|
||||||
def CreateCompletionRequest( self ):
|
def CreateCompletionRequest( self ):
|
||||||
# We have to store a reference to the newly created CompletionRequest
|
# We have to store a reference to the newly created CompletionRequest
|
||||||
# because VimScript can't store a reference to a Python object across
|
# because VimScript can't store a reference to a Python object across
|
||||||
# function calls... Thus we need to keep this request somewhere.
|
# function calls... Thus we need to keep this request somewhere.
|
||||||
self._current_completion_request = CompletionRequest( self )
|
self._current_completion_request = CompletionRequest()
|
||||||
return self._current_completion_request
|
return self._current_completion_request
|
||||||
|
|
||||||
|
|
||||||
@ -204,72 +81,19 @@ class YouCompleteMe( object ):
|
|||||||
return self._current_completion_request
|
return self._current_completion_request
|
||||||
|
|
||||||
|
|
||||||
def GetGeneralCompleter( self ):
|
|
||||||
return self._gencomp
|
|
||||||
|
|
||||||
|
|
||||||
def GetOmniCompleter( self ):
|
def GetOmniCompleter( self ):
|
||||||
return self._omnicomp
|
return self._omnicomp
|
||||||
|
|
||||||
|
|
||||||
def GetFiletypeCompleter( self ):
|
def NativeFiletypeCompletionAvailable( self ):
|
||||||
filetypes = vimsupport.CurrentFiletypes()
|
# TODO: Talk to server about this.
|
||||||
|
|
||||||
completers = [ self.GetFiletypeCompleterForFiletype( filetype )
|
|
||||||
for filetype in filetypes ]
|
|
||||||
|
|
||||||
if not completers:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Try to find a native completer first
|
|
||||||
for completer in completers:
|
|
||||||
if completer and completer is not self._omnicomp:
|
|
||||||
return completer
|
|
||||||
|
|
||||||
# Return the omni completer for the first filetype
|
|
||||||
return completers[ 0 ]
|
|
||||||
|
|
||||||
|
|
||||||
def GetFiletypeCompleterForFiletype( self, filetype ):
|
|
||||||
try:
|
|
||||||
return self._filetype_completers[ filetype ]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
module_path = _PathToFiletypeCompleterPluginLoader( filetype )
|
|
||||||
|
|
||||||
completer = None
|
|
||||||
supported_filetypes = [ filetype ]
|
|
||||||
if os.path.exists( module_path ):
|
|
||||||
module = imp.load_source( filetype, module_path )
|
|
||||||
completer = module.GetCompleter( self._user_options )
|
|
||||||
if completer:
|
|
||||||
supported_filetypes.extend( completer.SupportedFiletypes() )
|
|
||||||
else:
|
|
||||||
completer = self._omnicomp
|
|
||||||
|
|
||||||
for supported_filetype in supported_filetypes:
|
|
||||||
self._filetype_completers[ supported_filetype ] = completer
|
|
||||||
return completer
|
|
||||||
|
|
||||||
|
|
||||||
def ShouldUseGeneralCompleter( self, request_data ):
|
|
||||||
return self._gencomp.ShouldUseNow( request_data )
|
|
||||||
|
|
||||||
|
|
||||||
def ShouldUseFiletypeCompleter( self, request_data ):
|
|
||||||
if self.FiletypeCompletionUsable():
|
|
||||||
return self.GetFiletypeCompleter().ShouldUseNow( request_data )
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def NativeFiletypeCompletionAvailable( self ):
|
# TODO: This may not be needed at all when the server is ready. Evaluate this
|
||||||
completer = self.GetFiletypeCompleter()
|
# later.
|
||||||
return bool( completer ) and completer is not self._omnicomp
|
# def FiletypeCompletionAvailable( self ):
|
||||||
|
# return bool( self.GetFiletypeCompleter() )
|
||||||
|
|
||||||
def FiletypeCompletionAvailable( self ):
|
|
||||||
return bool( self.GetFiletypeCompleter() )
|
|
||||||
|
|
||||||
|
|
||||||
def NativeFiletypeCompletionUsable( self ):
|
def NativeFiletypeCompletionUsable( self ):
|
||||||
@ -277,9 +101,11 @@ class YouCompleteMe( object ):
|
|||||||
self.NativeFiletypeCompletionAvailable() )
|
self.NativeFiletypeCompletionAvailable() )
|
||||||
|
|
||||||
|
|
||||||
def FiletypeCompletionUsable( self ):
|
# TODO: This may not be needed at all when the server is ready. Evaluate this
|
||||||
return ( self.CurrentFiletypeCompletionEnabled() and
|
# later.
|
||||||
self.FiletypeCompletionAvailable() )
|
# def FiletypeCompletionUsable( self ):
|
||||||
|
# return ( self.CurrentFiletypeCompletionEnabled() and
|
||||||
|
# self.FiletypeCompletionAvailable() )
|
||||||
|
|
||||||
|
|
||||||
def OnFileReadyToParse( self ):
|
def OnFileReadyToParse( self ):
|
||||||
@ -290,51 +116,55 @@ class YouCompleteMe( object ):
|
|||||||
# TODO: make this work again
|
# TODO: make this work again
|
||||||
# if self._user_options[ 'seed_identifiers_with_syntax' ]:
|
# if self._user_options[ 'seed_identifiers_with_syntax' ]:
|
||||||
|
|
||||||
SendEventNotificationAsync( 'FileReadyToParse', self, extra_data )
|
SendEventNotificationAsync( 'FileReadyToParse', extra_data )
|
||||||
|
|
||||||
|
|
||||||
def OnBufferUnload( self, deleted_buffer_file ):
|
def OnBufferUnload( self, deleted_buffer_file ):
|
||||||
SendEventNotificationAsync( 'BufferUnload',
|
SendEventNotificationAsync( 'BufferUnload',
|
||||||
self,
|
|
||||||
{ 'unloaded_buffer': deleted_buffer_file } )
|
{ 'unloaded_buffer': deleted_buffer_file } )
|
||||||
|
|
||||||
|
|
||||||
def OnBufferVisit( self ):
|
def OnBufferVisit( self ):
|
||||||
SendEventNotificationAsync( 'BufferVisit', self )
|
SendEventNotificationAsync( 'BufferVisit' )
|
||||||
|
|
||||||
|
|
||||||
def OnInsertLeave( self ):
|
def OnInsertLeave( self ):
|
||||||
SendEventNotificationAsync( 'InsertLeave', self )
|
SendEventNotificationAsync( 'InsertLeave' )
|
||||||
|
|
||||||
|
|
||||||
def OnVimLeave( self ):
|
def OnVimLeave( self ):
|
||||||
SendEventNotificationAsync( 'VimLeave', self )
|
SendEventNotificationAsync( 'VimLeave' )
|
||||||
|
|
||||||
|
|
||||||
def OnCurrentIdentifierFinished( self ):
|
def OnCurrentIdentifierFinished( self ):
|
||||||
SendEventNotificationAsync( 'CurrentIdentifierFinished', self )
|
SendEventNotificationAsync( 'CurrentIdentifierFinished' )
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this work again.
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
def DiagnosticsForCurrentFileReady( self ):
|
||||||
if self.FiletypeCompletionUsable():
|
# if self.FiletypeCompletionUsable():
|
||||||
return self.GetFiletypeCompleter().DiagnosticsForCurrentFileReady()
|
# return self.GetFiletypeCompleter().DiagnosticsForCurrentFileReady()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this work again.
|
||||||
def GetDiagnosticsForCurrentFile( self ):
|
def GetDiagnosticsForCurrentFile( self ):
|
||||||
if self.FiletypeCompletionUsable():
|
# if self.FiletypeCompletionUsable():
|
||||||
return self.GetFiletypeCompleter().GetDiagnosticsForCurrentFile()
|
# return self.GetFiletypeCompleter().GetDiagnosticsForCurrentFile()
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this work again.
|
||||||
def GetDetailedDiagnostic( self ):
|
def GetDetailedDiagnostic( self ):
|
||||||
if self.FiletypeCompletionUsable():
|
# if self.FiletypeCompletionUsable():
|
||||||
return self.GetFiletypeCompleter().GetDetailedDiagnostic()
|
# return self.GetFiletypeCompleter().GetDetailedDiagnostic()
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this work again.
|
||||||
def GettingCompletions( self ):
|
def GettingCompletions( self ):
|
||||||
if self.FiletypeCompletionUsable():
|
# if self.FiletypeCompletionUsable():
|
||||||
return self.GetFiletypeCompleter().GettingCompletions()
|
# return self.GetFiletypeCompleter().GettingCompletions()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -366,51 +196,13 @@ class YouCompleteMe( object ):
|
|||||||
return not all([ x in filetype_to_disable for x in filetypes ])
|
return not all([ x in filetype_to_disable for x in filetypes ])
|
||||||
|
|
||||||
|
|
||||||
def _PathToCompletersFolder():
|
|
||||||
dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) )
|
|
||||||
return os.path.join( dir_of_current_script, 'completers' )
|
|
||||||
|
|
||||||
|
|
||||||
def _PathToFiletypeCompleterPluginLoader( filetype ):
|
|
||||||
return os.path.join( _PathToCompletersFolder(), filetype, 'hook.py' )
|
|
||||||
|
|
||||||
|
|
||||||
def _BuildRequestData( start_column = None, query = None ):
|
|
||||||
line, column = vimsupport.CurrentLineAndColumn()
|
|
||||||
request_data = {
|
|
||||||
'filetypes': vimsupport.CurrentFiletypes(),
|
|
||||||
'line_num': line,
|
|
||||||
'column_num': column,
|
|
||||||
'start_column': start_column,
|
|
||||||
'line_value': vim.current.line,
|
|
||||||
'filepath': vim.current.buffer.name,
|
|
||||||
'file_data': vimsupport.GetUnsavedAndCurrentBufferData()
|
|
||||||
}
|
|
||||||
|
|
||||||
if query:
|
|
||||||
request_data[ 'query' ] = query
|
|
||||||
|
|
||||||
return request_data
|
|
||||||
|
|
||||||
def _ConvertCompletionDataToVimData( completion_data ):
|
|
||||||
# see :h complete-items for a description of the dictionary fields
|
|
||||||
vim_data = {
|
|
||||||
'word' : completion_data[ 'insertion_text' ],
|
|
||||||
'dup' : 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'menu_text' in completion_data:
|
|
||||||
vim_data[ 'abbr' ] = completion_data[ 'menu_text' ]
|
|
||||||
if 'extra_menu_info' in completion_data:
|
|
||||||
vim_data[ 'menu' ] = completion_data[ 'extra_menu_info' ]
|
|
||||||
if 'kind' in completion_data:
|
|
||||||
vim_data[ 'kind' ] = completion_data[ 'kind' ]
|
|
||||||
if 'detailed_info' in completion_data:
|
|
||||||
vim_data[ 'info' ] = completion_data[ 'detailed_info' ]
|
|
||||||
|
|
||||||
return vim_data
|
|
||||||
|
|
||||||
def _GetTagFiles():
|
def _GetTagFiles():
|
||||||
tag_files = vim.eval( 'tagfiles()' )
|
tag_files = vim.eval( 'tagfiles()' )
|
||||||
current_working_directory = os.getcwd()
|
current_working_directory = os.getcwd()
|
||||||
return [ os.path.join( current_working_directory, x ) for x in tag_files ]
|
return [ os.path.join( current_working_directory, x ) for x in tag_files ]
|
||||||
|
|
||||||
|
|
||||||
|
def _PathToServerScript():
|
||||||
|
dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) )
|
||||||
|
return os.path.join( dir_of_current_script, 'server/server.py' )
|
||||||
|
Loading…
Reference in New Issue
Block a user