blob: db8096c4a89df06c318f51eb15b2fed0670ad786 [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# pylint: disable=import-error,print-statement,relative-import
import copy
import re
SPECIAL_TOKENS = [
# This list should be sorted by length.
'WebCodecs',
'WebSocket',
'String16',
'Float32',
'Float64',
'Base64',
'IFrame',
'Latin1',
'MathML',
'PlugIn',
'SQLite',
'Uint16',
'Uint32',
'WebGL2',
'webgl2',
'WebGPU',
'ASCII',
'CSSOM',
'CType',
'DList',
'Int16',
'Int32',
'MPath',
'OList',
'TSpan',
'UList',
'UTF16',
'Uint8',
'WebGL',
'XPath',
'ETC1',
'etc1',
'HTML',
'Int8',
'S3TC',
's3tc',
'SPv2',
'UTF8',
'sRGB',
'URLs',
'API',
'CSS',
'DNS',
'DOM',
'EXT',
'RTC',
'SVG',
'XSS',
'2D',
'AX',
'FE',
'JS',
'V0',
'V8',
'v8',
'XR',
]
_SPECIAL_TOKENS_WITH_NUMBERS = [
token for token in SPECIAL_TOKENS if re.search(r'[0-9]', token)
]
# Applying _TOKEN_PATTERNS repeatedly should capture any sequence of a-z, A-Z,
# 0-9.
_TOKEN_PATTERNS = [
# 'Foo' 'foo'
'[A-Z]?[a-z]+',
# The following pattern captures only 'FOO' in 'FOOElement'.
'[A-Z]+(?![a-z])',
# '2D' '3D', but not '2Dimension'
'[0-9][Dd](?![a-z])',
'[0-9]+',
]
_TOKEN_RE = re.compile(r'(' + '|'.join(SPECIAL_TOKENS + _TOKEN_PATTERNS) +
r')')
def tokenize_name(name):
"""Tokenize the specified name.
A token consists of A-Z, a-z, and 0-9 characters. Other characters work as
token delimiters, and the resultant list won't contain such characters.
Capital letters also work as delimiters. E.g. 'FooBar-baz' is tokenized to
['Foo', 'Bar', 'baz']. See _TOKEN_PATTERNS for more details.
This function detects special cases that are not easily discernible without
additional knowledge, such as recognizing that in SVGSVGElement, the first
two SVGs are separate tokens, but WebGL is one token.
Returns:
A list of token strings.
"""
# In case |name| is written in lowerCamelCase, we try to match special
# tokens that contains numbers ignoring cases only at the first step.
tokens = []
match = re.search(r'^(' + '|'.join(_SPECIAL_TOKENS_WITH_NUMBERS) + r')',
name, re.IGNORECASE)
if match:
tokens.append(match.group(0))
name = name[match.end(0):]
return tokens + _TOKEN_RE.findall(name)
class NameStyleConverter(object):
"""Converts names from camelCase to various other styles.
"""
def __init__(self, name):
self.tokens = tokenize_name(name)
self._original = name
@property
def original(self):
return self._original
def __str__(self):
return self._original
# Make this class workable with sort().
def __lt__(self, other):
return self.original < other.original
# Make this class workable with groupby().
def __eq__(self, other):
return self.original == other.original
# If __eq__() is defined then a custom __hash__() needs to be defined.
def __hash__(self):
return hash(self.original)
def to_snake_case(self):
"""Snake case is the file and variable name style per Google C++ Style
Guide:
https://google.github.io/styleguide/cppguide.html#Variable_Names
Also known as the hacker case.
https://en.wikipedia.org/wiki/Snake_case
"""
return '_'.join([token.lower() for token in self.tokens])
def to_upper_camel_case(self):
"""Upper-camel case is the class and function name style per
Google C++ Style Guide:
https://google.github.io/styleguide/cppguide.html#Function_Names
Also known as the PascalCase.
https://en.wikipedia.org/wiki/Camel_case.
"""
tokens = self.tokens
# If the first token is one of SPECIAL_TOKENS, we should replace the
# token with the matched special token.
# e.g. ['css', 'External', 'Scanner', 'Preload'] => 'CSSExternalScannerPreload'
if tokens and tokens[0].lower() == tokens[0]:
for special in SPECIAL_TOKENS:
if special.lower() == tokens[0]:
tokens = copy.deepcopy(tokens)
tokens[0] = special
break
return ''.join([token[0].upper() + token[1:] for token in tokens])
def to_lower_camel_case(self):
"""Lower camel case is the name style for attribute names and operation
names in web platform APIs.
e.g. 'addEventListener', 'documentURI', 'fftSize'
https://en.wikipedia.org/wiki/Camel_case.
"""
if not self.tokens:
return ''
return self.tokens[0].lower() + ''.join(
[token[0].upper() + token[1:] for token in self.tokens[1:]])
def to_macro_case(self):
"""Macro case is the macro name style per Google C++ Style Guide:
https://google.github.io/styleguide/cppguide.html#Macro_Names
"""
return '_'.join([token.upper() for token in self.tokens])
def to_all_cases(self):
return {
'snake_case': self.to_snake_case(),
'upper_camel_case': self.to_upper_camel_case(),
'macro_case': self.to_macro_case(),
}
# Use the following high level naming functions which describe the semantics
# of the name, rather than a particular style.
def to_class_name(self, prefix=None, suffix=None):
"""Represents this name as a class name in Chromium C++ style.
i.e. UpperCamelCase.
"""
camel_prefix = prefix[0].upper() + prefix[1:].lower() if prefix else ''
camel_suffix = suffix[0].upper() + suffix[1:].lower() if suffix else ''
return camel_prefix + self.to_upper_camel_case() + camel_suffix
def to_class_data_member(self, prefix=None, suffix=None):
"""Represents this name as a data member name in Chromium C++ style.
i.e. snake_case_with_trailing_underscore_.
"""
lower_prefix = prefix.lower() + '_' if prefix else ''
lower_suffix = suffix.lower() + '_' if suffix else ''
return lower_prefix + self.to_snake_case() + '_' + lower_suffix
def to_function_name(self, prefix=None, suffix=None):
"""Represents this name as a function name in Blink C++ style.
i.e. UpperCamelCase
Note that this function should not be used for IDL operation names and
C++ functions implementing IDL operations and attributes.
"""
camel_prefix = prefix[0].upper() + prefix[1:].lower() if prefix else ''
camel_suffix = ''
if type(suffix) is list:
for item in suffix:
camel_suffix += item[0].upper() + item[1:].lower()
elif suffix:
camel_suffix = suffix[0].upper() + suffix[1:].lower()
return camel_prefix + self.to_upper_camel_case() + camel_suffix
def to_enum_value(self):
"""Represents this name as an enum value in Blink C++ style.
i.e. kUpperCamelCase
"""
return 'k' + self.to_upper_camel_case()
def to_header_guard(self):
"""Represents this name as a header guard style in Chromium C++ style.
i.e. THIRD_PARTY_BLINK_RENDERER_MODULES_MODULES_EXPORT_H_
"""
return re.sub(r'[-/.]', '_', self.to_macro_case()) + '_'