diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 61058f8e8be..4d754ed33e0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -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; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index fbd2432e9b7..21eae040ba9 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -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 | undefined; - let enUSComparer: Comparer | 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 { - // 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 { - // 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 | undefined; @@ -2772,32 +2703,6 @@ function cartesianProductWorker(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(array: readonly T[], predicate: (element: T) => element is U): U[]; /** @internal */ @@ -2829,42 +2734,6 @@ export function skipWhile(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 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f77a3980104..7a9eec8d8e8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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]; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 294a0e72007..13a3c37bc41 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -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. diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 76eba963863..5a519dc79dc 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -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) { diff --git a/src/compiler/semver.ts b/src/compiler/semver.ts index 1c8af4fccab..23573253459 100644 --- a/src/compiler/semver.ts +++ b/src/compiler/semver.ts @@ -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; } } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index fedc66d6e77..6a5af5699b3 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -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)) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bd5958feaf6..960494f8a9a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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); diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 8417f159442..e7b2a1167a1 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -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); } } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 8f9a5e5738f..1f5f83fb8ff 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -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; diff --git a/src/jsTyping/shared.ts b/src/jsTyping/shared.ts index 3330d12afb1..09deb0fc2c5 100644 --- a/src/jsTyping/shared.ts +++ b/src/jsTyping/shared.ts @@ -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")}`; } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 91e0573993e..977979c69b1 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -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); } diff --git a/src/testRunner/unittests/canWatch.ts b/src/testRunner/unittests/canWatch.ts index 1c0738e7aec..0d25ee03f56 100644 --- a/src/testRunner/unittests/canWatch.ts +++ b/src/testRunner/unittests/canWatch.ts @@ -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);