Now using new ycm_client_support shared lib

This means we can now load just ycm_client_support (which is a much smaller
library) into Vim and ycm_core into ycmd. Since ycm_client_support never depends
on libclang.so, we never have to load that into Vim which makes things much,
much easier.
This commit is contained in:
Strahinja Val Markovic 2013-10-15 11:19:56 -07:00
parent 016434e846
commit 436017bd4d
16 changed files with 221 additions and 66 deletions

View File

@ -58,7 +58,7 @@ function! youcompleteme#Enable()
if !pyeval( 'base.CompatibleWithYcmCore()') if !pyeval( 'base.CompatibleWithYcmCore()')
echohl WarningMsg | echohl WarningMsg |
\ echomsg "YouCompleteMe unavailable: ycm_core too old, PLEASE RECOMPILE ycm_core" | \ echomsg "YouCompleteMe unavailable: YCM support libs too old, PLEASE RECOMPILE" |
\ echohl None \ echohl None
return return
endif endif

View File

@ -26,8 +26,8 @@ cmake_minimum_required( VERSION 2.8 )
project( BoostParts ) project( BoostParts )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5 ) set( Python_ADDITIONAL_VERSIONS 2.7 2.6 )
find_package( PythonLibs 2.5 REQUIRED ) find_package( PythonLibs 2.6 REQUIRED )
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" ) if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR message( FATAL_ERROR

View File

@ -17,10 +17,12 @@
cmake_minimum_required( VERSION 2.8 ) cmake_minimum_required( VERSION 2.8 )
project( ycm_core ) project( ycm_support_libs )
set( CLIENT_LIB "ycm_client_support" )
set( SERVER_LIB "ycm_core" )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5 ) set( Python_ADDITIONAL_VERSIONS 2.7 2.6 )
find_package( PythonLibs 2.5 REQUIRED ) find_package( PythonLibs 2.6 REQUIRED )
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" ) if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR message( FATAL_ERROR
@ -138,15 +140,15 @@ include_directories(
${CLANG_INCLUDES_DIR} ${CLANG_INCLUDES_DIR}
) )
file( GLOB_RECURSE SOURCES *.h *.cpp ) file( GLOB_RECURSE SERVER_SOURCES *.h *.cpp )
# The test sources are a part of a different target, so we remove them # The test sources are a part of a different target, so we remove them
# The CMakeFiles cpp file is picked up when the user creates an in-source build, # The CMakeFiles cpp file is picked up when the user creates an in-source build,
# and we don't want that. # and we don't want that. We also remove client-specific code
file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp ) file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp *client* )
if( to_remove ) if( to_remove )
list( REMOVE_ITEM SOURCES ${to_remove} ) list( REMOVE_ITEM SERVER_SOURCES ${to_remove} )
endif() endif()
if ( USE_CLANG_COMPLETER ) if ( USE_CLANG_COMPLETER )
@ -158,7 +160,7 @@ else()
file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp ) file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp )
if( to_remove_clang ) if( to_remove_clang )
list( REMOVE_ITEM SOURCES ${to_remove_clang} ) list( REMOVE_ITEM SERVER_SOURCES ${to_remove_clang} )
endif() endif()
endif() endif()
@ -217,11 +219,33 @@ endif()
############################################################################# #############################################################################
add_library( ${PROJECT_NAME} SHARED # We don't actually need all of the files this picks up, just the ones needed by
${SOURCES} # PythonSupport.cpp. But this is easier to maintain and dead code elemination
# will remove unused code.
file( GLOB CLIENT_SOURCES *.h *.cpp )
file( GLOB SERVER_SPECIFIC *ycm_core* )
if( SERVER_SPECIFIC )
list( REMOVE_ITEM CLIENT_SOURCES ${SERVER_SPECIFIC} )
endif()
add_library( ${CLIENT_LIB} SHARED
${CLIENT_SOURCES}
) )
target_link_libraries( ${PROJECT_NAME} target_link_libraries( ${CLIENT_LIB}
BoostParts
${PYTHON_LIBRARIES}
${EXTRA_LIBS}
)
#############################################################################
add_library( ${SERVER_LIB} SHARED
${SERVER_SOURCES}
)
target_link_libraries( ${SERVER_LIB}
BoostParts BoostParts
${PYTHON_LIBRARIES} ${PYTHON_LIBRARIES}
${LIBCLANG_TARGET} ${LIBCLANG_TARGET}
@ -231,35 +255,43 @@ target_link_libraries( ${PROJECT_NAME}
if( LIBCLANG_TARGET ) if( LIBCLANG_TARGET )
if( NOT WIN32 ) if( NOT WIN32 )
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME} TARGET ${SERVER_LIB}
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${PROJECT_NAME}>" COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${SERVER_LIB}>"
) )
else() else()
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME} TARGET ${SERVER_LIB}
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${PROJECT_NAME}>") COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${SERVER_LIB}>")
endif() endif()
endif() endif()
#############################################################################
# Convenience target that builds both support libs.
add_custom_target( ${PROJECT_NAME}
DEPENDS ${CLIENT_LIB} ${SERVER_LIB} )
############################################################################# #############################################################################
# Things are a bit different on Macs when using an external libclang.dylib; here # Things are a bit different on Macs when using an external libclang.dylib; here
# we want to make sure we use @loader_path/libclang.dylib instead of # we want to make sure we use @loader_path/libclang.dylib instead of
# @rpath/libclang.dylib in the final ycm_core.so. If we use the @rpath version, # @rpath/libclang.dylib in the final ycm_core.so. If we use the
# then it may load the system libclang which the user explicitely does not want # @rpath version, then it may load the system libclang which the user
# (otherwise the user would specify USE_SYSTEM_LIBCLANG). With @loader_path, we # explicitely does not want (otherwise the user would specify
# make sure that only the libclang.dylib present in the same directory as our # USE_SYSTEM_LIBCLANG). With @loader_path, we make sure that only the
# ycm_core.so is used. # libclang.dylib present in the same directory as our ycm_core.so
# is used.
if ( EXTERNAL_LIBCLANG_PATH AND APPLE ) if ( EXTERNAL_LIBCLANG_PATH AND APPLE )
add_custom_command( TARGET ${PROJECT_NAME} add_custom_command( TARGET ${SERVER_LIB}
POST_BUILD POST_BUILD
COMMAND install_name_tool COMMAND install_name_tool
"-change" "-change"
"@rpath/libclang.dylib" "@rpath/libclang.dylib"
"@loader_path/libclang.dylib" "@loader_path/libclang.dylib"
"$<TARGET_FILE:${PROJECT_NAME}>" "$<TARGET_FILE:${SERVER_LIB}>"
) )
endif() endif()
@ -268,19 +300,24 @@ endif()
# We don't want the "lib" prefix, it can screw up python when it tries to search # We don't want the "lib" prefix, it can screw up python when it tries to search
# for our module # for our module
set_target_properties( ${PROJECT_NAME} PROPERTIES PREFIX "") set_target_properties( ${CLIENT_LIB} PROPERTIES PREFIX "")
set_target_properties( ${SERVER_LIB} PROPERTIES PREFIX "")
if ( WIN32 OR CYGWIN ) if ( WIN32 OR CYGWIN )
# This is the extension for compiled Python modules on Windows # This is the extension for compiled Python modules on Windows
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".pyd") set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".pyd")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".pyd")
else() else()
# Even on macs, we want a .so extension instead of a .dylib which is what # Even on macs, we want a .so extension instead of a .dylib which is what
# cmake would give us by default. Python won't recognize a .dylib as a module, # cmake would give us by default. Python won't recognize a .dylib as a module,
# but it will recognize a .so # but it will recognize a .so
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".so") set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".so")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".so")
endif() endif()
set_target_properties( ${PROJECT_NAME} PROPERTIES set_target_properties( ${CLIENT_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../python )
set_target_properties( ${SERVER_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../python ) LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../python )
############################################################################# #############################################################################

View File

@ -28,7 +28,7 @@ endif()
add_subdirectory( gmock ) add_subdirectory( gmock )
include_directories( include_directories(
${ycm_core_SOURCE_DIR} ${ycm_support_libs_SOURCE_DIR}
) )
include_directories( include_directories(
@ -67,7 +67,8 @@ add_executable( ${PROJECT_NAME}
) )
target_link_libraries( ${PROJECT_NAME} target_link_libraries( ${PROJECT_NAME}
ycm_core ${SERVER_LIB}
${CLIENT_LIB}
gmock ) gmock )

26
cpp/ycm/versioning.cpp Normal file
View File

@ -0,0 +1,26 @@
// 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/>.
namespace YouCompleteMe {
int YcmCoreVersion() {
// We increment this every time when we want to force users to recompile
// ycm_core.
return 7;
}
} // namespace YouCompleteMe

22
cpp/ycm/versioning.h Normal file
View File

@ -0,0 +1,22 @@
// 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/>.
namespace YouCompleteMe {
int YcmCoreVersion();
} // namespace YouCompleteMe

View File

@ -0,0 +1,44 @@
// 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/>.
#include "IdentifierCompleter.h"
#include "PythonSupport.h"
#include "versioning.h"
#include <boost/python.hpp>
#include <boost/utility.hpp>
BOOST_PYTHON_MODULE(ycm_client_support)
{
using namespace boost::python;
using namespace YouCompleteMe;
// Necessary because of usage of the ReleaseGil class
PyEval_InitThreads();
def( "FilterAndSortCandidates", FilterAndSortCandidates );
def( "YcmCoreVersion", YcmCoreVersion );
}
// Boost.Thread forces us to implement this.
// We don't use any thread-specific (local) storage so it's fine to implement
// this as an empty function.
namespace boost {
void tss_cleanup_implemented() {}
};

View File

@ -17,6 +17,7 @@
#include "IdentifierCompleter.h" #include "IdentifierCompleter.h"
#include "PythonSupport.h" #include "PythonSupport.h"
#include "versioning.h"
#ifdef USE_CLANG_COMPLETER #ifdef USE_CLANG_COMPLETER
# include "ClangCompleter.h" # include "ClangCompleter.h"
@ -32,8 +33,7 @@
#include <boost/utility.hpp> #include <boost/utility.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp> #include <boost/python/suite/indexing/vector_indexing_suite.hpp>
bool HasClangSupport() bool HasClangSupport() {
{
#ifdef USE_CLANG_COMPLETER #ifdef USE_CLANG_COMPLETER
return true; return true;
#else #else
@ -41,13 +41,6 @@ bool HasClangSupport()
#endif // USE_CLANG_COMPLETER #endif // USE_CLANG_COMPLETER
} }
int YcmCoreVersion()
{
// We increment this every time when we want to force users to recompile
// ycm_core.
return 6;
}
BOOST_PYTHON_MODULE(ycm_core) BOOST_PYTHON_MODULE(ycm_core)
{ {

View File

@ -37,11 +37,14 @@ let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
function! s:HasYcmCore() function! s:HasYcmCore()
let path_prefix = s:script_folder_path . '/../python/' let path_prefix = s:script_folder_path . '/../python/'
if filereadable(path_prefix . 'ycm_core.so') if filereadable(path_prefix . 'ycm_client_support.so') &&
\ filereadable(path_prefix . 'ycm_core.so')
return 1 return 1
elseif filereadable(path_prefix . 'ycm_core.pyd') elseif filereadable(path_prefix . 'ycm_client_support.pyd') &&
\ filereadable(path_prefix . 'ycm_core.pyd')
return 1 return 1
elseif filereadable(path_prefix . 'ycm_core.dll') elseif filereadable(path_prefix . 'ycm_client_support.dll')
\ filereadable(path_prefix . 'ycm_core.dll')
return 1 return 1
endif endif
return 0 return 0
@ -52,7 +55,8 @@ let g:ycm_check_if_ycm_core_present =
if g:ycm_check_if_ycm_core_present && !s:HasYcmCore() if g:ycm_check_if_ycm_core_present && !s:HasYcmCore()
echohl WarningMsg | echohl WarningMsg |
\ echomsg "ycm_core.[so|pyd|dll] not detected; you need to compile " . \ echomsg "ycm_client_support.[so|pyd|dll] and " .
\ "ycm_core.[so|pyd|dll] not detected; you need to compile " .
\ "YCM before using it. Read the docs!" | \ "YCM before using it. Read the docs!" |
\ echohl None \ echohl None
finish finish

View File

@ -17,25 +17,15 @@
# 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 os
import re import re
import vim import vim
from ycm import vimsupport from ycm import vimsupport
from ycm import utils from ycm import utils
from ycm import user_options_store from ycm import user_options_store
import ycm_client_support
YCM_VAR_PREFIX = 'ycm_' YCM_VAR_PREFIX = 'ycm_'
try:
import ycm_core
except ImportError as e:
vimsupport.PostVimMessage(
'Error importing ycm_core. Are you sure you have placed a version 3.2+ '
'libclang.[so|dll|dylib] in folder "{0}"? See the Installation Guide in '
'the docs. Full error: {1}'.format(
os.path.dirname( os.path.dirname( os.path.abspath( __file__ ) ) ),
str( e ) ) )
def BuildServerConf(): def BuildServerConf():
"""Builds a dictionary mapping YCM Vim user options to values. Option names """Builds a dictionary mapping YCM Vim user options to values. Option names
@ -152,11 +142,11 @@ def AdjustCandidateInsertionText( candidates ):
return new_candidates return new_candidates
COMPATIBLE_WITH_CORE_VERSION = 6 COMPATIBLE_WITH_CORE_VERSION = 7
def CompatibleWithYcmCore(): def CompatibleWithYcmCore():
try: try:
current_core_version = ycm_core.YcmCoreVersion() current_core_version = ycm_client_support.YcmCoreVersion()
except AttributeError: except AttributeError:
return False return False

View File

@ -18,7 +18,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import abc import abc
import ycm_core import ycm_client_support
from ycm.utils import ToUtf8IfNeeded, ForceSemanticCompletion from ycm.utils import ToUtf8IfNeeded, ForceSemanticCompletion
from ycm.completers.completer_utils import TriggersForFiletype from ycm.completers.completer_utils import TriggersForFiletype
@ -207,7 +207,7 @@ class Completer( object ):
elif 'insertion_text' in candidates[ 0 ]: elif 'insertion_text' in candidates[ 0 ]:
sort_property = 'insertion_text' sort_property = 'insertion_text'
matches = ycm_core.FilterAndSortCandidates( matches = ycm_client_support.FilterAndSortCandidates(
candidates, candidates,
sort_property, sort_property,
ToUtf8IfNeeded( query ) ) ToUtf8IfNeeded( query ) )

View File

@ -17,6 +17,18 @@
# 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/>.
from os import path
try:
import ycm_core
except ImportError as e:
raise RuntimeError(
'Error importing ycm_core. Are you sure you have placed a '
'version 3.2+ libclang.[so|dll|dylib] in folder "{0}"? '
'See the Installation Guide in the docs. Full error: {1}'.format(
path.realpath( path.join( path.abspath( __file__ ), '../../..' ) ),
str( e ) ) )
import atexit import atexit
import logging import logging
import json import json
@ -28,6 +40,7 @@ from ycm import user_options_store
from ycm.server.responses import BuildExceptionResponse from ycm.server.responses import BuildExceptionResponse
from ycm import extra_conf_store from ycm import extra_conf_store
# num bytes for the request body buffer; request.json only works if the request # num bytes for the request body buffer; request.json only works if the request
# size is less than this # size is less than this
bottle.Request.MEMFILE_MAX = 300 * 1024 bottle.Request.MEMFILE_MAX = 300 * 1024
@ -135,8 +148,6 @@ def LoadExtraConfFile():
@app.post( '/debug_info' ) @app.post( '/debug_info' )
def DebugInfo(): def DebugInfo():
# This can't be at the top level because of possible extra conf preload
import ycm_core
LOGGER.info( 'Received debug info request' ) LOGGER.info( 'Received debug info request' )
output = [] output = []

View File

@ -59,13 +59,14 @@ def Main():
if args.options_file: if args.options_file:
options = json.load( open( args.options_file, 'r' ) ) options = json.load( open( args.options_file, 'r' ) )
user_options_store.SetAll( options ) user_options_store.SetAll( options )
# This ensures that ycm_core is not loaded before extra conf preload # This ensures that ycm_core is not loaded before extra conf
# was run. # preload was run.
YcmCoreSanityCheck() YcmCoreSanityCheck()
extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists() extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists()
# This can't be a top-level import because it transitively imports ycm_core # This can't be a top-level import because it transitively imports
# which we want to be imported ONLY after extra conf preload has executed. # ycm_core which we want to be imported ONLY after extra conf
# preload has executed.
import handlers import handlers
handlers.UpdateUserOptions( options ) handlers.UpdateUserOptions( options )
waitress.serve( handlers.app, waitress.serve( handlers.app,

View File

@ -146,7 +146,14 @@ def NumLinesInBuffer( buffer_object ):
# time of writing, YCM only uses the GUI thread inside Vim (this used to not be # time of writing, YCM only uses the GUI thread inside Vim (this used to not be
# the case). # the case).
def PostVimMessage( message ): def PostVimMessage( message ):
vim.command( "echohl WarningMsg | echomsg '{0}' | echohl None" vim.command( "echohl WarningMsg | echom '{0}' | echohl None"
.format( EscapeForVim( str( message ) ) ) )
# Unlike PostVimMesasge, this supports messages with newlines in them because it
# uses 'echo' instead of 'echomsg'. This also means that the message will NOT
# appear in Vim's message log.
def PostMultiLineNotice( message ):
vim.command( "echohl WarningMsg | echo '{0}' | echohl None"
.format( EscapeForVim( str( message ) ) ) ) .format( EscapeForVim( str( message ) ) ) )

View File

@ -40,6 +40,11 @@ try:
except ImportError: except ImportError:
USE_ULTISNIPS_DATA = False USE_ULTISNIPS_DATA = False
SERVER_CRASH_MESSAGE_STDERR_FILE = 'The ycmd server crashed with output:\n'
SERVER_CRASH_MESSAGE_SAME_STDERR = (
'The ycmd server crashed, check console output for logs!' )
class YouCompleteMe( object ): class YouCompleteMe( object ):
def __init__( self, user_options ): def __init__( self, user_options ):
self._user_options = user_options self._user_options = user_options
@ -83,6 +88,18 @@ class YouCompleteMe( object ):
self._server_popen = subprocess.Popen( args, self._server_popen = subprocess.Popen( args,
stdout = fstdout, stdout = fstdout,
stderr = fstderr ) stderr = fstderr )
self._CheckIfServerCrashed()
def _CheckIfServerCrashed( self ):
server_crashed = self._server_popen.poll()
if server_crashed:
if self._server_stderr:
with open( self._server_stderr, 'r' ) as server_stderr_file:
vimsupport.PostMultiLineNotice( SERVER_CRASH_MESSAGE_STDERR_FILE +
server_stderr_file.read() )
else:
vimsupport.PostVimMessage( SERVER_CRASH_MESSAGE_SAME_STDERR )
def CreateCompletionRequest( self, force_semantic = False ): def CreateCompletionRequest( self, force_semantic = False ):
@ -126,6 +143,7 @@ class YouCompleteMe( object ):
def OnFileReadyToParse( self ): def OnFileReadyToParse( self ):
self._CheckIfServerCrashed()
self._omnicomp.OnFileReadyToParse( None ) self._omnicomp.OnFileReadyToParse( None )
extra_data = {} extra_data = {}

View File

@ -19,6 +19,7 @@ astyle \
--recursive \ --recursive \
--exclude=gmock \ --exclude=gmock \
--exclude=testdata \ --exclude=testdata \
--exclude=ycm_client_support.cpp \
--exclude=ycm_core.cpp \ --exclude=ycm_core.cpp \
--exclude=CustomAssert.h \ --exclude=CustomAssert.h \
--exclude=CustomAssert.cpp \ --exclude=CustomAssert.cpp \