From 9d9c3181f71e6673bd8765e5cb2de54ec3d970a6 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 27 Jul 2017 00:56:33 -0400 Subject: [PATCH] Fixes #107 - Double-byte characters break blame layout --- package-lock.json | 39 ++++++++++++++++++++++++++++----- package.json | 3 ++- src/git/formatters/formatter.ts | 5 +++-- src/system/string.ts | 26 ++++++++++++++-------- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index ace4edd..718fb45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitlens", - "version": "4.3.1", + "version": "4.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -16,7 +16,7 @@ "integrity": "sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk=", "dev": true, "requires": { - "@types/node": "8.0.14" + "@types/node": "8.0.17" } }, "@types/mocha": { @@ -26,9 +26,9 @@ "dev": true }, "@types/node": { - "version": "8.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.14.tgz", - "integrity": "sha512-lrtgE/5FeTdcuxgsDbLUIFJ33dTp4TkbKkTDZt/ueUMeqmGYqJRQd908q5Yj9EzzWSMonEhMr1q/CQlgVGEt4w==", + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.17.tgz", + "integrity": "sha512-iq0LxqG6P9GV/2bVA2AQAQ58NvneLdVDVs9dJ+88Jk6gDK9opNC0SushdYqlAyvxo0dk0NJjTKCenq/l3AKtuA==", "dev": true }, "@types/tmp": { @@ -1319,6 +1319,11 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", @@ -2338,6 +2343,30 @@ "safe-buffer": "5.1.1" } }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", diff --git a/package.json b/package.json index 000a350..06c9718 100644 --- a/package.json +++ b/package.json @@ -1546,13 +1546,14 @@ "lodash.once": "4.1.1", "moment": "2.18.1", "spawn-rx": "2.0.11", + "string-width": "2.1.1", "tmp": "0.0.31" }, "devDependencies": { "@types/copy-paste": "1.1.30", "@types/iconv-lite": "0.0.1", "@types/mocha": "2.2.41", - "@types/node": "8.0.14", + "@types/node": "8.0.17", "@types/tmp": "0.0.33", "mocha": "3.4.2", "tslint": "5.5.0", diff --git a/src/git/formatters/formatter.ts b/src/git/formatters/formatter.ts index a92907f..873891d 100644 --- a/src/git/formatters/formatter.ts +++ b/src/git/formatters/formatter.ts @@ -51,11 +51,12 @@ export abstract class Formatter 0) { if (options.collapseWhitespace) { this.collapsableWhitespace = diff; diff --git a/src/system/string.ts b/src/system/string.ts index 694860f..18b8b42 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -1,11 +1,16 @@ '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; @@ -63,18 +68,19 @@ export namespace Strings { } export function padLeft(s: string, padTo: number, padding: string = '\u00a0') { - const diff = padTo - s.length; + const diff = padTo - getWidth(s); return (diff <= 0) ? s : '\u00a0'.repeat(diff) + s; } export function padLeftOrTruncate(s: string, max: number, padding?: string) { - if (s.length < max) return padLeft(s, max, padding); - if (s.length > max) return truncate(s, max); + 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 - s.length; + const diff = padTo - getWidth(s); return (diff <= 0) ? s : s + '\u00a0'.repeat(diff); } @@ -82,19 +88,21 @@ export namespace Strings { const left = max < 0; max = Math.abs(max); - if (s.length < max) return left ? padLeft(s, max, padding) : padRight(s, max, padding); - if (s.length > max) return truncate(s, 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) { - if (s.length < max) return padRight(s, max, padding); - if (s.length > max) return truncate(s, max); + 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 || s.length <= truncateTo) return s; + if (!s || truncateTo === undefined || getWidth(s) <= truncateTo) return s; return `${s.substring(0, truncateTo - 1)}\u2026`; } } \ No newline at end of file