// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Utilities for strings.
*/
export class StringUtil {
static toUpperCamelCase(str: string): string {
const wordRegex = /(?:^\w|[A-Z]|(?:\b|_)\w)/g;
const underscoreAndWhitespaceRegex = /(\s|_)+/g;
return str.replace(wordRegex, word => word.toUpperCase())
.replace(underscoreAndWhitespaceRegex, '');
}
/**
* Returns the length of the longest common prefix of two strings.
* TODO(b/319783585): This doesn't work well if there's a character
* represented with a surrogate pair.
* @param first The first string.
* @param second The second string.
* @return The length of the longest common prefix, which may be 0
* for an empty common prefix.
*/
static longestCommonPrefixLength(first: string, second: string): number {
const limit = Math.min(first.length, second.length);
let i;
for (i = 0; i < limit; ++i) {
if (first.charAt(i) !== second.charAt(i)) {
break;
}
}
return i;
}
/**
* Returns the length of the longest common suffix of two strings.
* TODO(b/319783585): This doesn't work well if there's a character
* represented with a surrogate pair.
* @param first The first string.
* @param second The second string.
* @return The length of the longest common suffix, which may be 0
* for an empty common suffix.
*/
static longestCommonSuffixLength(first: string, second: string): number {
const limit = Math.min(first.length, second.length);
let i;
for (i = 0; i < limit; ++i) {
if (first.charAt(first.length - i - 1) !==
second.charAt(second.length - i - 1)) {
break;
}
}
return i;
}
/**
* Returns the offset after the code point denoted by |offset|.
* If |offset| points at a character that is not the first code unit of
* a valid code point, then |offset + 1| is returned. If there are no
* characters after the code point denoted by |offset|, then the length of
* |str| is returned.
* @param str String of characters.
* @param offset A valid character index in |str|.
* @return A valid index of |str| or |str.length|.
*/
static nextCodePointOffset(str: string, offset: number): number {
if (offset >= str.length) {
return str.length;
}
// TODO(b/314203187): Not null asserted, check these to make sure this is
// correct.
if (str.codePointAt(offset)! > StringUtil.MAX_BMP_CODEPOINT) {
return offset + 2;
}
return offset + 1;
}
/**
* Returns the offset of the first code unit of the last code point before
* |offset| in a string. If there is no valid code point right before
* |offset| (including if offset is zero), |offset -1| is returned.
* @param str String of characters.
* @param offset A valid character offset into |str|.
* @return A valid character index into |str| (or -1 in the case
* where |offset| is 0).
*/
static previousCodePointOffset(str: string, offset: number): number {
if (offset <= 0) {
return -1;
}
// TODO(b/314203187): Not null asserted, check these to make sure this is
// correct.
if (offset > 1 &&
str.codePointAt(offset - 2)! > StringUtil.MAX_BMP_CODEPOINT) {
return offset - 2;
}
return offset - 1;
}
static toTitleCase(title: string): string {
return title.replace(
/\w\S*/g, word => word.charAt(0).toUpperCase() + word.substr(1));
}
/**
* Converts a camel case string to snake case.
* @param s A camel case string, e.g. 'brailleTable8'.
* @return A snake case string, e.g. 'braille_table_8'.
*/
static camelToSnake(s: string): string {
return s.replace(/([A-Z0-9])/g, '_$1').toLowerCase();
}
/**
* @param ch The character to test.
* @return True if a character breaks a word, used to determine
* if the previous word should be spoken.
*/
static isWordBreakChar(ch: string): boolean {
return Boolean(ch.match(/^\W$/));
}
}
export namespace StringUtil {
/**
* The last code point of the Unicode basic multilingual plane.
* Code points larger than this value are represented in UTF-16 by a surrogate
* pair, that is two code units.
*/
export const MAX_BMP_CODEPOINT = 65535;
}