Clean up helpers which are always present in ES2020 (#55515)

This commit is contained in:
Jake Bailey 2023-08-28 12:26:22 -07:00 committed by GitHub
parent 5ce34cafad
commit b5b6048bb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 37 additions and 199 deletions

View File

@ -110,7 +110,6 @@ import {
toFileNameLowerCase,
toPath,
tracing,
trimString,
TsConfigOnlyOption,
TsConfigSourceFile,
TypeAcquisition,
@ -1699,13 +1698,13 @@ function createDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType
}
/** @internal */
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) {
return convertJsonOptionOfCustomType(opt, trimString(value || ""), errors);
export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string | undefined, errors: Diagnostic[]) {
return convertJsonOptionOfCustomType(opt, (value ?? "").trim(), errors);
}
/** @internal */
export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Diagnostic[]): string | (string | number)[] | undefined {
value = trimString(value);
value = value.trim();
if (startsWith(value, "-")) {
return undefined;
}

View File

@ -5,7 +5,6 @@ import {
Comparison,
Debug,
EqualityComparer,
isWhiteSpaceLike,
MapLike,
Queue,
SortedArray,
@ -2241,11 +2240,7 @@ export function getStringComparer(ignoreCase?: boolean) {
* Creates a string comparer for use with string collation in the UI.
*/
const createUIStringComparer = (() => {
let defaultComparer: Comparer<string> | undefined;
let enUSComparer: Comparer<string> | undefined;
const stringComparerFactory = getStringComparerFactory();
return createStringComparer;
return createIntlCollatorStringComparer;
function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) {
if (a === b) return Comparison.EqualTo;
@ -2261,70 +2256,6 @@ const createUIStringComparer = (() => {
const comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" }).compare;
return (a, b) => compareWithCallback(a, b, comparer);
}
function createLocaleCompareStringComparer(locale: string | undefined): Comparer<string> {
// if the locale is not the default locale (`undefined`), use the fallback comparer.
if (locale !== undefined) return createFallbackStringComparer();
return (a, b) => compareWithCallback(a, b, compareStrings);
function compareStrings(a: string, b: string) {
return a.localeCompare(b);
}
}
function createFallbackStringComparer(): Comparer<string> {
// An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b".
// We first sort case insensitively. So "Aaa" will come before "baa".
// Then we sort case sensitively, so "aaa" will come before "Aaa".
//
// For case insensitive comparisons we always map both strings to their
// upper-case form as some unicode characters do not properly round-trip to
// lowercase (such as `ẞ` (German sharp capital s)).
return (a, b) => compareWithCallback(a, b, compareDictionaryOrder);
function compareDictionaryOrder(a: string, b: string) {
return compareStrings(a.toUpperCase(), b.toUpperCase()) || compareStrings(a, b);
}
function compareStrings(a: string, b: string) {
return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
}
}
function getStringComparerFactory() {
// If the host supports Intl, we use it for comparisons using the default locale.
if (typeof Intl === "object" && typeof Intl.Collator === "function") {
return createIntlCollatorStringComparer;
}
// If the host does not support Intl, we fall back to localeCompare.
// localeCompare in Node v0.10 is just an ordinal comparison, so don't use it.
if (
typeof String.prototype.localeCompare === "function" &&
typeof String.prototype.toLocaleUpperCase === "function" &&
"a".localeCompare("B") < 0
) {
return createLocaleCompareStringComparer;
}
// Otherwise, fall back to ordinal comparison:
return createFallbackStringComparer;
}
function createStringComparer(locale: string | undefined) {
// Hold onto common string comparers. This avoids constantly reallocating comparers during
// tests.
if (locale === undefined) {
return defaultComparer || (defaultComparer = stringComparerFactory(locale));
}
else if (locale === "en-US") {
return enUSComparer || (enUSComparer = stringComparerFactory(locale));
}
else {
return stringComparerFactory(locale);
}
}
})();
let uiComparerCaseSensitive: Comparer<string> | undefined;
@ -2772,32 +2703,6 @@ function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (r
}
}
/**
* Returns string left-padded with spaces or zeros until it reaches the given length.
*
* @param s String to pad.
* @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged.
* @param padString Character to use as padding (default " ").
*
* @internal
*/
export function padLeft(s: string, length: number, padString: " " | "0" = " ") {
return length <= s.length ? s : padString.repeat(length - s.length) + s;
}
/**
* Returns string right-padded with spaces until it reaches the given length.
*
* @param s String to pad.
* @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged.
* @param padString Character to use as padding (default " ").
*
* @internal
*/
export function padRight(s: string, length: number, padString: " " = " ") {
return length <= s.length ? s : s + padString.repeat(length - s.length);
}
/** @internal */
export function takeWhile<T, U extends T>(array: readonly T[], predicate: (element: T) => element is U): U[];
/** @internal */
@ -2829,42 +2734,6 @@ export function skipWhile<T, U extends T>(array: readonly T[] | undefined, predi
}
}
/**
* Removes the leading and trailing white space and line terminator characters from a string.
*
* @internal
*/
export const trimString = !!String.prototype.trim ? ((s: string) => s.trim()) : (s: string) => trimStringEnd(trimStringStart(s));
/**
* Returns a copy with trailing whitespace removed.
*
* @internal
*/
export const trimStringEnd = !!String.prototype.trimEnd ? ((s: string) => s.trimEnd()) : trimEndImpl;
/**
* Returns a copy with leading whitespace removed.
*
* @internal
*/
export const trimStringStart = !!String.prototype.trimStart ? ((s: string) => s.trimStart()) : (s: string) => s.replace(/^\s+/g, "");
/**
* https://jsbench.me/gjkoxld4au/1
* The simple regex for this, /\s+$/g is O(n^2) in v8.
* The native .trimEnd method is by far best, but since that's technically ES2019,
* we provide a (still much faster than the simple regex) fallback.
*/
function trimEndImpl(s: string) {
let end = s.length - 1;
while (end >= 0) {
if (!isWhiteSpaceLike(s.charCodeAt(end))) break;
end--;
}
return s.slice(0, end + 1);
}
/** @internal */
export function isNodeLikeSystem(): boolean {
// This is defined here rather than in sys.ts to prevent a cycle from its

View File

@ -368,8 +368,6 @@ import {
tokenToString,
tracing,
TransformFlags,
trimString,
trimStringEnd,
TryStatement,
TupleTypeNode,
TypeAliasDeclaration,
@ -8895,7 +8893,7 @@ namespace Parser {
nextTokenJSDoc();
}
}
const trimmedComments = trimStringEnd(comments.join(""));
const trimmedComments = comments.join("").trimEnd();
if (parts.length && trimmedComments.length) {
parts.push(finishNode(factory.createJSDocText(trimmedComments), linkEnd ?? start, commentsPos));
}
@ -8912,7 +8910,7 @@ namespace Parser {
function removeTrailingWhitespace(comments: string[]) {
while (comments.length) {
const trimmed = trimStringEnd(comments[comments.length - 1]);
const trimmed = comments[comments.length - 1].trimEnd();
if (trimmed === "") {
comments.pop();
}
@ -9174,7 +9172,7 @@ namespace Parser {
}
removeLeadingNewlines(comments);
const trimmedComments = trimStringEnd(comments.join(""));
const trimmedComments = comments.join("").trimEnd();
if (parts.length) {
if (trimmedComments.length) {
parts.push(finishNode(factory.createJSDocText(trimmedComments), linkEnd ?? commentsPos));
@ -10618,7 +10616,7 @@ function addPragmaForMatch(pragmas: PragmaPseudoMapEntry[], range: CommentRange,
function getNamedPragmaArguments(pragma: PragmaDefinition, text: string | undefined): { [index: string]: string; } | "fail" {
if (!text) return {};
if (!pragma.args) return {};
const args = trimString(text).split(/\s+/);
const args = text.trim().split(/\s+/);
const argMap: { [index: string]: string; } = {};
for (let i = 0; i < pragma.args.length; i++) {
const argument = pragma.args[i];

View File

@ -250,7 +250,6 @@ import {
packageIdToPackageName,
packageIdToString,
PackageJsonInfoCache,
padLeft,
ParameterDeclaration,
ParseConfigFileHost,
ParsedCommandLine,
@ -318,7 +317,6 @@ import {
toPath as ts_toPath,
trace,
tracing,
trimStringEnd,
TsConfigSourceFile,
TypeChecker,
typeDirectiveIsEqualTo,
@ -722,22 +720,22 @@ function formatCodeSpan(file: SourceFile, start: number, length: number, indent:
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
// so we'll skip ahead to the second-to-last line.
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
context += indent + formatColorAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
context += indent + formatColorAndReset(ellipsis.padStart(gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
i = lastLine - 1;
}
const lineStart = getPositionOfLineAndCharacter(file, i, 0);
const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
let lineContent = file.text.slice(lineStart, lineEnd);
lineContent = trimStringEnd(lineContent); // trim from end
lineContent = lineContent.trimEnd(); // trim from end
lineContent = lineContent.replace(/\t/g, " "); // convert tabs to single spaces
// Output the gutter and the actual contents of the line.
context += indent + formatColorAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
context += indent + formatColorAndReset((i + 1 + "").padStart(gutterWidth), gutterStyleSequence) + gutterSeparator;
context += lineContent + host.getNewLine();
// Output the gutter and the error span for the line using tildes.
context += indent + formatColorAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
context += indent + formatColorAndReset("".padStart(gutterWidth), gutterStyleSequence) + gutterSeparator;
context += squiggleColor;
if (i === firstLine) {
// If we're on the last line, then limit it to the last character of the last line.

View File

@ -18,7 +18,6 @@ import {
LanguageVariant,
LineAndCharacter,
MapLike,
padLeft,
parsePseudoBigInt,
positionIsSynthesized,
PunctuationOrKeywordSyntaxKind,
@ -26,7 +25,6 @@ import {
SourceFileLike,
SyntaxKind,
TokenFlags,
trimStringStart,
} from "./_namespaces/ts";
export type ErrorCallback = (message: DiagnosticMessage, length: number, arg0?: any) => void;
@ -1490,7 +1488,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
tokenFlags |= TokenFlags.ContainsInvalidEscape;
if (shouldEmitInvalidEscapeError) {
const code = parseInt(text.substring(start + 1, pos), 8);
error(Diagnostics.Octal_escape_sequences_are_not_allowed_Use_the_syntax_0, start, pos - start, "\\x" + padLeft(code.toString(16), 2, "0"));
error(Diagnostics.Octal_escape_sequences_are_not_allowed_Use_the_syntax_0, start, pos - start, "\\x" + code.toString(16).padStart(2, "0"));
return String.fromCharCode(code);
}
return text.substring(start, pos);
@ -2392,7 +2390,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
commentDirectiveRegEx: RegExp,
lineStart: number,
) {
const type = getDirectiveFromComment(trimStringStart(text), commentDirectiveRegEx);
const type = getDirectiveFromComment(text.trimStart(), commentDirectiveRegEx);
if (type === undefined) {
return commentDirectives;
}
@ -2793,25 +2791,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
}
/** @internal */
const codePointAt: (s: string, i: number) => number = (String.prototype as any).codePointAt ? (s, i) => (s as any).codePointAt(i) : function codePointAt(str, i): number {
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt
const size = str.length;
// Account for out-of-bounds indices:
if (i < 0 || i >= size) {
return undefined!; // String.codePointAt returns `undefined` for OOB indexes
}
// Get the first code unit
const first = str.charCodeAt(i);
// check if it's the start of a surrogate pair
if (first >= 0xD800 && first <= 0xDBFF && size > i + 1) { // high surrogate and there is a next code unit
const second = str.charCodeAt(i + 1);
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return first;
};
function codePointAt(s: string, i: number): number {
// TODO(jakebailey): this is wrong and should have ?? 0; but all users are okay with it
return s.codePointAt(i)!;
}
/** @internal */
function charSize(ch: number) {

View File

@ -8,7 +8,6 @@ import {
isArray,
map,
some,
trimString,
} from "./_namespaces/ts";
// https://semver.org/#spec-item-2
@ -275,17 +274,17 @@ const rangeRegExp = /^(~|\^|<|<=|>|>=|=)?\s*([a-z0-9-+.*]+)$/i;
function parseRange(text: string) {
const alternatives: Comparator[][] = [];
for (let range of trimString(text).split(logicalOrRegExp)) {
for (let range of text.trim().split(logicalOrRegExp)) {
if (!range) continue;
const comparators: Comparator[] = [];
range = trimString(range);
range = range.trim();
const match = hyphenRegExp.exec(range);
if (match) {
if (!parseHyphen(match[1], match[2], comparators)) return undefined;
}
else {
for (const simple of range.split(whitespaceRegExp)) {
const match = rangeRegExp.exec(trimString(simple));
const match = rangeRegExp.exec(simple.trim());
if (!match || !parseComparator(match[1], match[2], comparators)) return undefined;
}
}

View File

@ -24,7 +24,6 @@ import {
sortAndDeduplicate,
SortedReadonlyArray,
SourceMapGenerator,
trimStringEnd,
} from "./_namespaces/ts";
import * as performance from "./_namespaces/ts.performance";
@ -392,7 +391,7 @@ export function tryGetSourceMappingURL(lineInfo: LineInfo) {
const line = lineInfo.getLineText(index);
const comment = sourceMapCommentRegExp.exec(line);
if (comment) {
return trimStringEnd(comment[1]);
return comment[1].trimEnd();
}
// If we see a non-whitespace/map comment-like line, break, to avoid scanning up the entire file
else if (!line.match(whitespaceOrMapCommentRegExp)) {

View File

@ -521,8 +521,6 @@ import {
tracing,
TransformFlags,
TransientSymbol,
trimString,
trimStringStart,
TriviaSyntaxKind,
tryCast,
tryRemovePrefix,
@ -1231,7 +1229,7 @@ export function getTextOfNodeFromSourceText(sourceText: string, node: Node, incl
if (isJSDocTypeExpressionOrChild(node)) {
// strip space + asterisk at line start
text = text.split(/\r\n|\n|\r/).map(line => trimStringStart(line.replace(/^\s*\*/, ""))).join("\n");
text = text.split(/\r\n|\n|\r/).map(line => line.replace(/^\s*\*/, "").trimStart()).join("\n");
}
return text;
@ -6828,7 +6826,7 @@ export function writeCommentRange(text: string, lineMap: readonly number[], writ
function writeTrimmedCurrentLine(text: string, commentEnd: number, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) {
const end = Math.min(commentEnd, nextLineStart - 1);
const currentLineText = trimString(text.substring(pos, end));
const currentLineText = text.substring(pos, end).trim();
if (currentLineText) {
// trimmed forward and ending spaces text
writer.writeComment(currentLineText);

View File

@ -61,8 +61,6 @@ import {
optionDeclarations,
optionsForBuild,
optionsForWatch,
padLeft,
padRight,
parseBuildCommand,
parseCommandLine,
parseConfigFileWithSystem,
@ -329,12 +327,12 @@ function generateOptionOutput(sys: System, option: CommandLineOption, rightAlign
while (remainRight.length > 0) {
let curLeft = "";
if (isFirstLine) {
curLeft = padLeft(left, rightAlignOfLeft);
curLeft = padRight(curLeft, leftAlignOfRight);
curLeft = left.padStart(rightAlignOfLeft);
curLeft = curLeft.padEnd(leftAlignOfRight);
curLeft = colorLeft ? colors.blue(curLeft) : curLeft;
}
else {
curLeft = padLeft("", leftAlignOfRight);
curLeft = "".padStart(leftAlignOfRight);
}
const curRight = remainRight.substr(0, rightCharacterNumber);
@ -524,15 +522,15 @@ function getHeader(sys: System, message: string) {
const terminalWidth = sys.getWidthOfTerminal?.() ?? 0;
const tsIconLength = 5;
const tsIconFirstLine = colors.blueBackground(padLeft("", tsIconLength));
const tsIconSecondLine = colors.blueBackground(colors.brightWhite(padLeft("TS ", tsIconLength)));
const tsIconFirstLine = colors.blueBackground("".padStart(tsIconLength));
const tsIconSecondLine = colors.blueBackground(colors.brightWhite("TS ".padStart(tsIconLength)));
// If we have enough space, print TS icon.
if (terminalWidth >= message.length + tsIconLength) {
// right align of the icon is 120 at most.
const rightAlign = terminalWidth > 120 ? 120 : terminalWidth;
const leftAlign = rightAlign - tsIconLength;
header.push(padRight(message, leftAlign) + tsIconFirstLine + sys.newLine);
header.push(padLeft("", leftAlign) + tsIconSecondLine + sys.newLine);
header.push(message.padEnd(leftAlign) + tsIconFirstLine + sys.newLine);
header.push("".padStart(leftAlign) + tsIconSecondLine + sys.newLine);
}
else {
header.push(message + sys.newLine);
@ -1261,7 +1259,7 @@ function reportAllStatistics(sys: System, statistics: Statistic[]) {
}
for (const s of statistics) {
sys.write(padRight(s.name + ":", nameSize + 2) + padLeft(statisticValue(s).toString(), valueSize) + sys.newLine);
sys.write(`${s.name}:`.padEnd(nameSize + 2) + statisticValue(s).toString().padStart(valueSize) + sys.newLine);
}
}

View File

@ -2120,7 +2120,7 @@ export class TestState {
const output: string[] = [];
for (let lineNumber = contextStart.line; lineNumber <= contextEnd.line; lineNumber++) {
const spanLine = contextString.substring(contextLineMap[lineNumber], contextLineMap[lineNumber + 1]);
output.push(lineNumbers ? `${ts.padLeft(`${lineNumber + 1}: `, lineNumberPrefixLength)}${spanLine}` : spanLine);
output.push(lineNumbers ? `${`${lineNumber + 1}: `.padStart(lineNumberPrefixLength)}${spanLine}` : spanLine);
if (selection) {
if (lineNumber < selectionStart.line || lineNumber > selectionEnd.line) {
continue;

View File

@ -1,5 +1,4 @@
import {
padLeft,
sys,
} from "./_namespaces/ts";
@ -64,5 +63,5 @@ export function findArgument(argumentName: string): string | undefined {
export function nowString() {
// E.g. "12:34:56.789"
const d = new Date();
return `${padLeft(d.getHours().toString(), 2, "0")}:${padLeft(d.getMinutes().toString(), 2, "0")}:${padLeft(d.getSeconds().toString(), 2, "0")}.${padLeft(d.getMilliseconds().toString(), 3, "0")}`;
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}.${d.getMilliseconds().toString().padStart(3, "0")}`;
}

View File

@ -52,8 +52,6 @@ import {
SyntaxKind,
TemplateExpression,
TextSpan,
trimString,
trimStringStart,
TryStatement,
} from "./_namespaces/ts";
@ -164,11 +162,11 @@ const regionDelimiterRegExp = /^#(end)?region(?:\s+(.*))?(?:\r)?$/;
function isRegionDelimiter(lineText: string) {
// We trim the leading whitespace and // without the regex since the
// multiple potential whitespace matches can make for some gnarly backtracking behavior
lineText = trimStringStart(lineText);
lineText = lineText.trimStart();
if (!startsWith(lineText, "//")) {
return null; // eslint-disable-line no-null/no-null
}
lineText = trimString(lineText.slice(2));
lineText = lineText.slice(2).trim();
return regionDelimiterRegExp.exec(lineText);
}

View File

@ -190,7 +190,7 @@ describe("unittests:: canWatch::", () => {
let result = "|";
let divider = addDivider ? "|" : undefined;
columns.forEach((header, index) => {
result += " " + ts.padRight(header, maxLengths[index]) + " |";
result += " " + header.padEnd(maxLengths[index]) + " |";
if (addDivider) divider += " " + "-".repeat(maxLengths[index]) + " |";
});
baseline.push(result);