/******************************************************************************** * Copyright (C) 2018 Red Hat, Inc. and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ // based on https://github.com/Microsoft/vscode/blob/bf7ac9201e7a7d01741d4e6e64b5dc9f3197d97b/src/vs/base/common/strings.ts /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { CharCode } from './char-code'; /** * Determines if haystack ends with needle. */ export function endsWith(haystack: string, needle: string): boolean { const diff = haystack.length - needle.length; if (diff > 0) { return haystack.indexOf(needle, diff) === diff; } else if (diff === 0) { return haystack === needle; } else { return false; } } export function isLowerAsciiLetter(code: number): boolean { return code >= CharCode.a && code <= CharCode.z; } export function isUpperAsciiLetter(code: number): boolean { return code >= CharCode.A && code <= CharCode.Z; } function isAsciiLetter(code: number): boolean { return isLowerAsciiLetter(code) || isUpperAsciiLetter(code); } export function equalsIgnoreCase(a: string, b: string): boolean { const len1 = a ? a.length : 0; const len2 = b ? b.length : 0; if (len1 !== len2) { return false; } return doEqualsIgnoreCase(a, b); } function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean { if (typeof a !== 'string' || typeof b !== 'string') { return false; } for (let i = 0; i < stopAt; i++) { const codeA = a.charCodeAt(i); const codeB = b.charCodeAt(i); if (codeA === codeB) { continue; } // a-z A-Z if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) { const diff = Math.abs(codeA - codeB); if (diff !== 0 && diff !== 32) { return false; } } // Any other charcode // tslint:disable-next-line:one-line else { if (String.fromCharCode(codeA).toLowerCase() !== String.fromCharCode(codeB).toLowerCase()) { return false; } } } return true; } /** * Escapes regular expression characters in a given string */ export function escapeRegExpCharacters(value: string): string { return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\[\]\(\)\#]/g, '\\$&'); } export function startsWithIgnoreCase(str: string, candidate: string): boolean { const candidateLength = candidate.length; if (candidate.length > str.length) { return false; } return doEqualsIgnoreCase(str, candidate, candidateLength); } export function* split(s: string, splitter: string): IterableIterator { let start = 0; while (start < s.length) { let end = s.indexOf(splitter, start); if (end === -1) { end = s.length; } yield s.substring(start, end); start = end + splitter.length; } } export function escapeInvisibleChars(value: string): string { return value.replace(/\n/g, '\\n').replace(/\r/g, '\\r'); } export function unescapeInvisibleChars(value: string): string { return value.replace(/\\n/g, '\n').replace(/\\r/g, '\r'); }