# Copyright (C) 2013 Google Inc.
#
# 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 .
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()
from builtins import * # noqa
from future.utils import itervalues
import re
import vim
from ycm import vimsupport
SYNTAX_GROUP_REGEX = re.compile(
r"""^
(?P\w+)
\s+
xxx
\s+
(?P.+?)
$""",
re.VERBOSE )
KEYWORD_REGEX = re.compile( r'^[\w,]+$' )
SYNTAX_ARGUMENT_REGEX = re.compile(
r"^\w+=.*$" )
SYNTAX_ARGUMENTS = set([
'cchar',
'conceal',
'contained',
'containedin',
'nextgroup',
'skipempty',
'skipnl',
'skipwhite',
'transparent',
'concealends',
'contains',
'display',
'extend',
'fold',
'oneline',
'keepend',
'excludenl',
])
# We want to parse lines starting with these args
ALLOWED_SYNTAX_ARGUMENTS = set([
'contained',
])
# These are the parent groups from which we want to extract keywords
ROOT_GROUPS = set([
'Statement',
'Boolean',
'Include',
'Type',
'Identifier',
])
class SyntaxGroup( object ):
def __init__( self, name, lines = None ):
self.name = name
self.lines = lines if lines else []
self.children = []
def SyntaxKeywordsForCurrentBuffer():
vim.command( 'redir => b:ycm_syntax' )
vim.command( 'silent! syntax list' )
vim.command( 'redir END' )
syntax_output = vimsupport.VimExpressionToPythonType( 'b:ycm_syntax' )
return _KeywordsFromSyntaxListOutput( syntax_output )
def _KeywordsFromSyntaxListOutput( syntax_output ):
group_name_to_group = _SyntaxGroupsFromOutput( syntax_output )
_ConnectGroupChildren( group_name_to_group )
groups_with_keywords = []
for root_group in ROOT_GROUPS:
groups_with_keywords.extend(
_GetAllDescendentats( group_name_to_group[ root_group ] ) )
keywords = []
for group in groups_with_keywords:
keywords.extend( _ExtractKeywordsFromGroup( group ) )
return set( keywords )
def _SyntaxGroupsFromOutput( syntax_output ):
group_name_to_group = _CreateInitialGroupMap()
lines = syntax_output.split( '\n' )
looking_for_group = True
current_group = None
for line in lines:
if not line:
continue
match = SYNTAX_GROUP_REGEX.search( line )
if match:
if looking_for_group:
looking_for_group = False
else:
group_name_to_group[ current_group.name ] = current_group
current_group = SyntaxGroup( match.group( 'group_name' ),
[ match.group( 'content').strip() ] )
else:
if looking_for_group:
continue
if line[ 0 ] == ' ' or line[ 0 ] == '\t':
current_group.lines.append( line.strip() )
if current_group:
group_name_to_group[ current_group.name ] = current_group
return group_name_to_group
def _CreateInitialGroupMap():
def AddToGroupMap( name, parent ):
new_group = SyntaxGroup( name )
group_name_to_group[ name ] = new_group
parent.children.append( new_group )
statement_group = SyntaxGroup( 'Statement' )
type_group = SyntaxGroup( 'Type' )
identifier_group = SyntaxGroup( 'Identifier' )
# See `:h group-name` for details on how the initial group hierarchy is built
group_name_to_group = {
'Statement': statement_group,
'Type': type_group,
'Boolean': SyntaxGroup( 'Boolean' ),
'Include': SyntaxGroup( 'Include' ),
'Identifier': identifier_group,
}
AddToGroupMap( 'Conditional', statement_group )
AddToGroupMap( 'Repeat' , statement_group )
AddToGroupMap( 'Label' , statement_group )
AddToGroupMap( 'Operator' , statement_group )
AddToGroupMap( 'Keyword' , statement_group )
AddToGroupMap( 'Exception' , statement_group )
AddToGroupMap( 'StorageClass', type_group )
AddToGroupMap( 'Structure' , type_group )
AddToGroupMap( 'Typedef' , type_group )
AddToGroupMap( 'Function', identifier_group )
return group_name_to_group
def _ConnectGroupChildren( group_name_to_group ):
def GetParentNames( group ):
links_to = 'links to '
parent_names = []
for line in group.lines:
if line.startswith( links_to ):
parent_names.append( line[ len( links_to ): ] )
return parent_names
for group in itervalues( group_name_to_group ):
parent_names = GetParentNames( group )
for parent_name in parent_names:
try:
parent_group = group_name_to_group[ parent_name ]
except KeyError:
continue
parent_group.children.append( group )
def _GetAllDescendentats( root_group ):
descendants = []
for child in root_group.children:
descendants.append( child )
descendants.extend( _GetAllDescendentats( child ) )
return descendants
def _ExtractKeywordsFromGroup( group ):
keywords = []
for line in group.lines:
if line.startswith( 'links to ' ):
continue
words = line.split()
if not words or ( words[ 0 ] in SYNTAX_ARGUMENTS and
words[ 0 ] not in ALLOWED_SYNTAX_ARGUMENTS ):
continue
for word in words:
if ( word not in SYNTAX_ARGUMENTS and
not SYNTAX_ARGUMENT_REGEX.match( word ) and
KEYWORD_REGEX.match( word ) ):
if word.endswith( ',' ):
word = word[ :-1 ]
keywords.append( word )
return keywords