base.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2011, 2012 Google Inc.
  4. #
  5. # This file is part of YouCompleteMe.
  6. #
  7. # YouCompleteMe is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # YouCompleteMe is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  19. from ycm import vimsupport
  20. from ycmd import user_options_store
  21. from ycmd import request_wrap
  22. from ycmd import identifier_utils
  23. import ycm_client_support
  24. YCM_VAR_PREFIX = 'ycm_'
  25. def BuildServerConf():
  26. """Builds a dictionary mapping YCM Vim user options to values. Option names
  27. don't have the 'ycm_' prefix."""
  28. vim_globals = vimsupport.GetReadOnlyVimGlobals( force_python_objects = True )
  29. server_conf = {}
  30. for key, value in vim_globals.items():
  31. if not key.startswith( YCM_VAR_PREFIX ):
  32. continue
  33. try:
  34. new_value = int( value )
  35. except:
  36. new_value = value
  37. new_key = key[ len( YCM_VAR_PREFIX ): ]
  38. server_conf[ new_key ] = new_value
  39. return server_conf
  40. def LoadJsonDefaultsIntoVim():
  41. defaults = user_options_store.DefaultOptions()
  42. vim_defaults = {}
  43. for key, value in defaults.iteritems():
  44. vim_defaults[ 'ycm_' + key ] = value
  45. vimsupport.LoadDictIntoVimGlobals( vim_defaults, overwrite = False )
  46. def CompletionStartColumn():
  47. return ( request_wrap.CompletionStartColumn(
  48. vimsupport.CurrentLineContents(),
  49. vimsupport.CurrentColumn() + 1,
  50. vimsupport.CurrentFiletypes()[ 0 ] ) - 1 )
  51. def CurrentIdentifierFinished():
  52. current_column = vimsupport.CurrentColumn()
  53. previous_char_index = current_column - 1
  54. if previous_char_index < 0:
  55. return True
  56. line = vimsupport.CurrentLineContents()
  57. filetype = vimsupport.CurrentFiletypes()[ 0 ]
  58. regex = identifier_utils.IdentifierRegexForFiletype( filetype )
  59. for match in regex.finditer( line ):
  60. if match.end() == previous_char_index:
  61. return True
  62. # If the whole line is whitespace, that means the user probably finished an
  63. # identifier on the previous line.
  64. return line[ : current_column ].isspace()
  65. def LastEnteredCharIsIdentifierChar():
  66. current_column = vimsupport.CurrentColumn()
  67. if current_column - 1 < 0:
  68. return False
  69. line = vimsupport.CurrentLineContents()
  70. filetype = vimsupport.CurrentFiletypes()[ 0 ]
  71. return (
  72. identifier_utils.StartOfLongestIdentifierEndingAtIndex(
  73. line, current_column, filetype ) != current_column )
  74. def AdjustCandidateInsertionText( candidates ):
  75. """This function adjusts the candidate insertion text to take into account the
  76. text that's currently in front of the cursor.
  77. For instance ('|' represents the cursor):
  78. 1. Buffer state: 'foo.|bar'
  79. 2. A completion candidate of 'zoobar' is shown and the user selects it.
  80. 3. Buffer state: 'foo.zoobar|bar' instead of 'foo.zoo|bar' which is what the
  81. user wanted.
  82. This function changes candidates to resolve that issue.
  83. It could be argued that the user actually wants the final buffer state to be
  84. 'foo.zoobar|' (the cursor at the end), but that would be much more difficult
  85. to implement and is probably not worth doing.
  86. """
  87. def NewCandidateInsertionText( to_insert, text_after_cursor ):
  88. overlap_len = OverlapLength( to_insert, text_after_cursor )
  89. if overlap_len:
  90. return to_insert[ :-overlap_len ]
  91. return to_insert
  92. text_after_cursor = vimsupport.TextAfterCursor()
  93. if not text_after_cursor:
  94. return candidates
  95. new_candidates = []
  96. for candidate in candidates:
  97. if type( candidate ) is dict:
  98. new_candidate = candidate.copy()
  99. if not 'abbr' in new_candidate:
  100. new_candidate[ 'abbr' ] = new_candidate[ 'word' ]
  101. new_candidate[ 'word' ] = NewCandidateInsertionText(
  102. new_candidate[ 'word' ],
  103. text_after_cursor )
  104. new_candidates.append( new_candidate )
  105. elif type( candidate ) is str:
  106. new_candidates.append(
  107. { 'abbr': candidate,
  108. 'word': NewCandidateInsertionText( candidate, text_after_cursor ) } )
  109. return new_candidates
  110. def OverlapLength( left_string, right_string ):
  111. """Returns the length of the overlap between two strings.
  112. Example: "foo baro" and "baro zoo" -> 4
  113. """
  114. left_string_length = len( left_string )
  115. right_string_length = len( right_string )
  116. if not left_string_length or not right_string_length:
  117. return 0
  118. # Truncate the longer string.
  119. if left_string_length > right_string_length:
  120. left_string = left_string[ -right_string_length: ]
  121. elif left_string_length < right_string_length:
  122. right_string = right_string[ :left_string_length ]
  123. if left_string == right_string:
  124. return min( left_string_length, right_string_length )
  125. # Start by looking for a single character match
  126. # and increase length until no match is found.
  127. best = 0
  128. length = 1
  129. while True:
  130. pattern = left_string[ -length: ]
  131. found = right_string.find( pattern )
  132. if found < 0:
  133. return best
  134. length += found
  135. if left_string[ -length: ] == right_string[ :length ]:
  136. best = length
  137. length += 1
  138. COMPATIBLE_WITH_CORE_VERSION = 12
  139. def CompatibleWithYcmCore():
  140. try:
  141. current_core_version = ycm_client_support.YcmCoreVersion()
  142. except AttributeError:
  143. return False
  144. return current_core_version == COMPATIBLE_WITH_CORE_VERSION