syntax_parse.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2013 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. import re
  20. import vim
  21. from ycm import vimsupport
  22. SYNTAX_GROUP_REGEX = re.compile(
  23. r"""^
  24. (?P<group_name>\w+)
  25. \s+
  26. xxx
  27. \s+
  28. (?P<content>.+?)
  29. $""",
  30. re.VERBOSE )
  31. KEYWORD_REGEX = re.compile( r'^[\w,]+$' )
  32. SYNTAX_ARGUMENT_REGEX = re.compile(
  33. r"^\w+=.*$" )
  34. SYNTAX_ARGUMENTS = set([
  35. 'cchar',
  36. 'conceal',
  37. 'contained',
  38. 'containedin',
  39. 'nextgroup',
  40. 'skipempty',
  41. 'skipnl',
  42. 'skipwhite',
  43. 'transparent',
  44. 'concealends',
  45. 'contains',
  46. 'display',
  47. 'extend',
  48. 'fold',
  49. 'oneline',
  50. 'keepend',
  51. 'excludenl',
  52. ])
  53. # We want to parse lines starting with these args
  54. ALLOWED_SYNTAX_ARGUMENTS = set([
  55. 'contained',
  56. ])
  57. # These are the parent groups from which we want to extract keywords
  58. ROOT_GROUPS = set([
  59. 'Statement',
  60. 'Boolean',
  61. 'Include',
  62. 'Type',
  63. 'Identifier',
  64. ])
  65. class SyntaxGroup( object ):
  66. def __init__( self, name, lines = None ):
  67. self.name = name
  68. self.lines = lines if lines else []
  69. self.children = []
  70. def SyntaxKeywordsForCurrentBuffer():
  71. vim.command( 'redir => b:ycm_syntax' )
  72. vim.command( 'silent! syntax list' )
  73. vim.command( 'redir END' )
  74. syntax_output = vimsupport.GetVariableValue( 'b:ycm_syntax' )
  75. return _KeywordsFromSyntaxListOutput( syntax_output )
  76. def _KeywordsFromSyntaxListOutput( syntax_output ):
  77. group_name_to_group = _SyntaxGroupsFromOutput( syntax_output )
  78. _ConnectGroupChildren( group_name_to_group )
  79. groups_with_keywords = []
  80. for root_group in ROOT_GROUPS:
  81. groups_with_keywords.extend(
  82. _GetAllDescendentats( group_name_to_group[ root_group ] ) )
  83. keywords = []
  84. for group in groups_with_keywords:
  85. keywords.extend( _ExtractKeywordsFromGroup( group ) )
  86. return set( keywords )
  87. def _SyntaxGroupsFromOutput( syntax_output ):
  88. group_name_to_group = _CreateInitialGroupMap()
  89. lines = syntax_output.split( '\n' )
  90. looking_for_group = True
  91. current_group = None
  92. for line in lines:
  93. if not line:
  94. continue
  95. match = SYNTAX_GROUP_REGEX.search( line )
  96. if match:
  97. if looking_for_group:
  98. looking_for_group = False
  99. else:
  100. group_name_to_group[ current_group.name ] = current_group
  101. current_group = SyntaxGroup( match.group( 'group_name' ),
  102. [ match.group( 'content').strip() ] )
  103. else:
  104. if looking_for_group:
  105. continue
  106. if line[ 0 ] == ' ' or line[ 0 ] == '\t':
  107. current_group.lines.append( line.strip() )
  108. if current_group:
  109. group_name_to_group[ current_group.name ] = current_group
  110. return group_name_to_group
  111. def _CreateInitialGroupMap():
  112. def AddToGroupMap( name, parent ):
  113. new_group = SyntaxGroup( name )
  114. group_name_to_group[ name ] = new_group
  115. parent.children.append( new_group )
  116. statement_group = SyntaxGroup( 'Statement' )
  117. type_group = SyntaxGroup( 'Type' )
  118. identifier_group = SyntaxGroup( 'Identifier' )
  119. # See `:h group-name` for details on how the initial group hierarchy is built
  120. group_name_to_group = {
  121. 'Statement': statement_group,
  122. 'Type': type_group,
  123. 'Boolean': SyntaxGroup( 'Boolean' ),
  124. 'Include': SyntaxGroup( 'Include' ),
  125. 'Identifier': identifier_group,
  126. }
  127. AddToGroupMap( 'Conditional', statement_group )
  128. AddToGroupMap( 'Repeat' , statement_group )
  129. AddToGroupMap( 'Label' , statement_group )
  130. AddToGroupMap( 'Operator' , statement_group )
  131. AddToGroupMap( 'Keyword' , statement_group )
  132. AddToGroupMap( 'Exception' , statement_group )
  133. AddToGroupMap( 'StorageClass', type_group )
  134. AddToGroupMap( 'Structure' , type_group )
  135. AddToGroupMap( 'Typedef' , type_group )
  136. AddToGroupMap( 'Function', identifier_group )
  137. return group_name_to_group
  138. def _ConnectGroupChildren( group_name_to_group ):
  139. def GetParentNames( group ):
  140. links_to = 'links to '
  141. parent_names = []
  142. for line in group.lines:
  143. if line.startswith( links_to ):
  144. parent_names.append( line[ len( links_to ): ] )
  145. return parent_names
  146. for group in group_name_to_group.itervalues():
  147. parent_names = GetParentNames( group )
  148. for parent_name in parent_names:
  149. try:
  150. parent_group = group_name_to_group[ parent_name ]
  151. except KeyError:
  152. continue
  153. parent_group.children.append( group )
  154. def _GetAllDescendentats( root_group ):
  155. descendants = []
  156. for child in root_group.children:
  157. descendants.append( child )
  158. descendants.extend( _GetAllDescendentats( child ) )
  159. return descendants
  160. def _ExtractKeywordsFromGroup( group ):
  161. keywords = []
  162. for line in group.lines:
  163. if line.startswith( 'links to ' ):
  164. continue
  165. words = line.split()
  166. if not words or ( words[ 0 ] in SYNTAX_ARGUMENTS and
  167. words[ 0 ] not in ALLOWED_SYNTAX_ARGUMENTS ):
  168. continue
  169. for word in words:
  170. if ( word not in SYNTAX_ARGUMENTS and
  171. not SYNTAX_ARGUMENT_REGEX.match( word ) and
  172. KEYWORD_REGEX.match( word ) ):
  173. if word.endswith( ',' ):
  174. word = word[ :-1 ]
  175. keywords.append( word )
  176. return keywords