Files
vscode-gitlens/src/system/string.ts

124 lines
4.0 KiB
TypeScript

'use strict';
const _escapeRegExp = require('lodash.escaperegexp');
const stringWidth = require('string-width');
export namespace Strings {
export function escapeRegExp(s: string): string {
return _escapeRegExp(s);
}
export function getWidth(s: string): number {
return stringWidth(s);
}
const TokenRegex = /\$\{([^|]*?)(?:\|(\d+)(\-|\?)?)?\}/g;
const TokenSanitizeRegex = /\$\{(\w*?)(?:\W|\d)*?\}/g;
export interface ITokenOptions {
padDirection: 'left' | 'right';
truncateTo: number | undefined;
collapseWhitespace: boolean;
}
export function getTokensFromTemplate(template: string) {
const tokens: { key: string, options: ITokenOptions }[] = [];
let match = TokenRegex.exec(template);
while (match != null) {
const truncateTo = match[2];
const option = match[3];
tokens.push({
key: match[1],
options: {
truncateTo: truncateTo == null ? undefined : parseInt(truncateTo, 10),
padDirection: option === '-' ? 'left' : 'right',
collapseWhitespace: option === '?'
}
});
match = TokenRegex.exec(template);
}
return tokens;
}
export function interpolate(template: string, context: object): string {
if (!template) return template;
template = template.replace(TokenSanitizeRegex, '$${this.$1}');
return new Function(`return \`${template}\`;`).call(context);
}
export function* lines(s: string): IterableIterator<string> {
let i = 0;
while (i < s.length) {
let j = s.indexOf('\n', i);
if (j === -1) {
j = s.length;
}
yield s.substring(i, j);
i = j + 1;
}
}
export function pad(s: string, before: number = 0, after: number = 0, padding: string = `\u00a0`) {
if (before === 0 && after === 0) return s;
return `${before === 0 ? '' : padding.repeat(before)}${s}${after === 0 ? '' : padding.repeat(after)}`;
}
export function padLeft(s: string, padTo: number, padding: string = '\u00a0') {
const diff = padTo - getWidth(s);
return (diff <= 0) ? s : '\u00a0'.repeat(diff) + s;
}
export function padLeftOrTruncate(s: string, max: number, padding?: string) {
const len = getWidth(s);
if (len < max) return padLeft(s, max, padding);
if (len > max) return truncate(s, max);
return s;
}
export function padRight(s: string, padTo: number, padding: string = '\u00a0') {
const diff = padTo - getWidth(s);
return (diff <= 0) ? s : s + '\u00a0'.repeat(diff);
}
export function padOrTruncate(s: string, max: number, padding?: string) {
const left = max < 0;
max = Math.abs(max);
const len = getWidth(s);
if (len < max) return left ? padLeft(s, max, padding) : padRight(s, max, padding);
if (len > max) return truncate(s, max);
return s;
}
export function padRightOrTruncate(s: string, max: number, padding?: string) {
const len = getWidth(s);
if (len < max) return padRight(s, max, padding);
if (len > max) return truncate(s, max);
return s;
}
export function truncate(s: string, truncateTo?: number) {
if (!s || truncateTo === undefined) return s;
const len = getWidth(s);
if (len <= truncateTo) return s;
if (len === s.length) return `${s.substring(0, truncateTo - 1)}\u2026`;
// Skip ahead to start as far as we can by assuming all the double-width characters won't be truncated
let chars = Math.floor(truncateTo / (len / s.length));
let count = getWidth(s.substring(0, chars));
while (count < truncateTo) {
count += getWidth(s[chars++]);
}
if (count > truncateTo) {
chars--;
}
return `${s.substring(0, chars)}\u2026`;
}
}