mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-13 17:23:11 -05:00
Closes #146 - Attempts to deal with emoji in gutter
Adds ability to automagically set the width of the gutter annotations
This commit is contained in:
@@ -6,17 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [5.1.1-beta]
|
||||
## [5.2.0-beta]
|
||||
### Added
|
||||
- Adds new `Changed Files` node to the `Repository Status` node of the `GitLens` custom view's `Repository View` (enabled via `"gitlens.insiders": true`) -- closes [#139](https://github.com/eamodio/vscode-gitlens/issues/139)
|
||||
- Provides a file-based view of all the changed files in the working tree and/or files in commits that haven't yet been pushed upstream
|
||||
- Adds new `Changed Files` node to the `Repository Status` node of the `GitLens` custom view's `Repository View` -- closes [#139](https://github.com/eamodio/vscode-gitlens/issues/139)
|
||||
- Provides a file-based view of all the changed files in the working tree (enabled via `"gitlens.insiders": true`) and/or files in commits that haven't yet been pushed upstream
|
||||
- Adds `gitlens.gitExplorer.enabled` setting to specify whether or not to show the `GitLens` custom view - closes [#144](https://github.com/eamodio/vscode-gitlens/issues/144)
|
||||
|
||||
### Changed
|
||||
- Chnages the default of the `gitlens.gitExplorer.commitFormat` setting to add parentheses around the commit id
|
||||
- Changes the default of the `gitlens.gitExplorer.commitFormat` setting to add parentheses around the commit id
|
||||
- Removes many menu items from `editor/title` & `editor/title/context` by default -- can be re-enabled via the `gitlens.advanced.menus` setting
|
||||
|
||||
### Fixed
|
||||
- Fixes [#146](https://github.com/eamodio/vscode-gitlens/issues/146) - Blame gutter annotation issue when commit contains emoji
|
||||
- Fixes an issue when running `Open File in Remote` with a multi-line selection wasn't properly opening the selection in GitLab -- thanks to [PR #145](https://github.com/eamodio/vscode-gitlens/pull/145) by Amanda Cameron ([@AmandaCameron](https://github.com/AmandaCameron))!
|
||||
- Fixes an issue where the `gitlens.advanced.menus` setting wasn't controlling all the menu items properly
|
||||
|
||||
|
||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -47,11 +47,6 @@
|
||||
"json-stable-stringify": "1.0.1"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
@@ -1336,11 +1331,6 @@
|
||||
"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",
|
||||
@@ -2334,15 +2324,6 @@
|
||||
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=",
|
||||
"dev": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
@@ -2358,14 +2339,6 @@
|
||||
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
|
||||
"dev": true
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
|
||||
@@ -1921,7 +1921,6 @@
|
||||
"lodash.once": "4.1.1",
|
||||
"moment": "2.18.1",
|
||||
"spawn-rx": "2.0.11",
|
||||
"string-width": "2.1.1",
|
||||
"tmp": "0.0.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Dates, Strings } from '../system';
|
||||
import { Dates, Objects, Strings } from '../system';
|
||||
import { DecorationInstanceRenderOptions, DecorationOptions, MarkdownString, ThemableDecorationRenderOptions } from 'vscode';
|
||||
import { DiffWithCommand, OpenCommitInRemoteCommand, ShowQuickCommitDetailsCommand } from '../commands';
|
||||
import { IThemeConfig, themeDefaults } from '../configuration';
|
||||
@@ -133,9 +133,23 @@ export class Annotations {
|
||||
} as DecorationOptions;
|
||||
}
|
||||
|
||||
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig): IRenderOptions {
|
||||
static gutterRenderOptions(cfgTheme: IThemeConfig, heatmap: IHeatmapConfig, options: ICommitFormatOptions): IRenderOptions {
|
||||
const cfgFileTheme = cfgTheme.annotations.file.gutter;
|
||||
|
||||
// Try to get the width of the string, if there is a cap
|
||||
let width = 4; // Start with a padding
|
||||
for (const token of Objects.values<Strings.ITokenOptions | undefined>(options.tokenOptions)) {
|
||||
if (token === undefined) continue;
|
||||
|
||||
// If any token is uncapped, kick out and set no max
|
||||
if (token.truncateTo == null) {
|
||||
width = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
width += token.truncateTo;
|
||||
}
|
||||
|
||||
let borderStyle = undefined;
|
||||
let borderWidth = undefined;
|
||||
if (heatmap.enabled) {
|
||||
@@ -152,7 +166,8 @@ export class Annotations {
|
||||
borderStyle: borderStyle,
|
||||
borderWidth: borderWidth,
|
||||
height: '100%',
|
||||
margin: '0 26px -1px 0'
|
||||
margin: '0 26px -1px 0',
|
||||
width: (width > 4) ? `${width}ch` : undefined
|
||||
},
|
||||
dark: {
|
||||
backgroundColor: cfgFileTheme.dark.backgroundColor || undefined,
|
||||
|
||||
@@ -34,7 +34,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
||||
|
||||
const now = Date.now();
|
||||
const offset = this.uri.offset;
|
||||
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap);
|
||||
const renderOptions = Annotations.gutterRenderOptions(this._config.theme, cfg.heatmap, options);
|
||||
const separateLines = this._config.theme.annotations.file.gutter.separateLines;
|
||||
|
||||
const decorations: DecorationOptions[] = [];
|
||||
@@ -58,7 +58,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
|
||||
...gutter.renderOptions,
|
||||
before: {
|
||||
...gutter.renderOptions!.before,
|
||||
contentText: GlyphChars.Space.repeat(Strings.getWidth(gutter.renderOptions!.before!.contentText!))
|
||||
contentText: GlyphChars.Space.repeat(Strings.width(gutter.renderOptions!.before!.contentText!))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export abstract class Formatter<TItem = any, TOptions extends IFormatOptions = I
|
||||
|
||||
let max = options.truncateTo;
|
||||
|
||||
const width = Strings.getWidth(s);
|
||||
const width = Strings.width(s);
|
||||
if (max === undefined) {
|
||||
if (this.collapsableWhitespace === 0) return s;
|
||||
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
'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;
|
||||
|
||||
@@ -68,19 +63,19 @@ export namespace Strings {
|
||||
}
|
||||
|
||||
export function padLeft(s: string, padTo: number, padding: string = '\u00a0') {
|
||||
const diff = padTo - getWidth(s);
|
||||
const diff = padTo - width(s);
|
||||
return (diff <= 0) ? s : '\u00a0'.repeat(diff) + s;
|
||||
}
|
||||
|
||||
export function padLeftOrTruncate(s: string, max: number, padding?: string) {
|
||||
const len = getWidth(s);
|
||||
const len = width(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);
|
||||
const diff = padTo - width(s);
|
||||
return (diff <= 0) ? s : s + '\u00a0'.repeat(diff);
|
||||
}
|
||||
|
||||
@@ -88,14 +83,14 @@ export namespace Strings {
|
||||
const left = max < 0;
|
||||
max = Math.abs(max);
|
||||
|
||||
const len = getWidth(s);
|
||||
const len = width(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);
|
||||
const len = width(s);
|
||||
if (len < max) return padRight(s, max, padding);
|
||||
if (len > max) return truncate(s, max);
|
||||
return s;
|
||||
@@ -112,15 +107,15 @@ export namespace Strings {
|
||||
export function truncate(s: string, truncateTo: number, ellipsis: string = '\u2026') {
|
||||
if (!s) return s;
|
||||
|
||||
const len = getWidth(s);
|
||||
const len = width(s);
|
||||
if (len <= truncateTo) return s;
|
||||
if (len === s.length) return `${s.substring(0, truncateTo - 1)}${ellipsis}`;
|
||||
|
||||
// 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));
|
||||
let count = width(s.substring(0, chars));
|
||||
while (count < truncateTo) {
|
||||
count += getWidth(s[chars++]);
|
||||
count += width(s[chars++]);
|
||||
}
|
||||
|
||||
if (count >= truncateTo) {
|
||||
@@ -129,4 +124,107 @@ export namespace Strings {
|
||||
|
||||
return `${s.substring(0, chars)}${ellipsis}`;
|
||||
}
|
||||
|
||||
const ansiRegex = /[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))/g;
|
||||
|
||||
export function width(s: string): number {
|
||||
if (!s || s.length === 0) return 0;
|
||||
|
||||
s = s.replace(ansiRegex, '');
|
||||
|
||||
let count = 0;
|
||||
let emoji = 0;
|
||||
let joiners = 0;
|
||||
|
||||
const graphemes = [...s];
|
||||
for (let i = 0; i < graphemes.length; i++) {
|
||||
const code = graphemes[i].codePointAt(0)!;
|
||||
|
||||
// Ignore control characters
|
||||
if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) continue;
|
||||
|
||||
// Ignore combining characters
|
||||
if (code >= 0x300 && code <= 0x36F) continue;
|
||||
|
||||
// https://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji
|
||||
if (
|
||||
(code >= 0x1F600 && code <= 0x1F64F) || // Emoticons
|
||||
(code >= 0x1F300 && code <= 0x1F5FF) || // Misc Symbols and Pictographs
|
||||
(code >= 0x1F680 && code <= 0x1F6FF) || // Transport and Map
|
||||
(code >= 0x2600 && code <= 0x26FF) || // Misc symbols
|
||||
(code >= 0x2700 && code <= 0x27BF) || // Dingbats
|
||||
(code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors
|
||||
(code >= 0x1F900 && code <= 0x1F9FF) || // Supplemental Symbols and Pictographs
|
||||
(code >= 65024 && code <= 65039) || // Variation selector
|
||||
(code >= 8400 && code <= 8447) // Combining Diacritical Marks for Symbols
|
||||
) {
|
||||
if (code >= 0x1F3FB && code <= 0x1F3FF) continue; // emoji modifier fitzpatrick type
|
||||
|
||||
emoji++;
|
||||
count += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore zero-width joiners '\u200d'
|
||||
if (code === 8205) {
|
||||
joiners++;
|
||||
count -= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Surrogates
|
||||
if (code > 0xFFFF) {
|
||||
i++;
|
||||
}
|
||||
|
||||
count += isFullwidthCodePoint(code) ? 2 : 1;
|
||||
}
|
||||
|
||||
const offset = emoji - joiners;
|
||||
if (offset > 1) {
|
||||
count += offset - 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function isFullwidthCodePoint(cp: number) {
|
||||
// code points are derived from:
|
||||
// http://www.unix.org/Public/UNIDATA/EastAsianWidth.txt
|
||||
if (
|
||||
cp >= 0x1100 && (
|
||||
cp <= 0x115f || // Hangul Jamo
|
||||
cp === 0x2329 || // LEFT-POINTING ANGLE BRACKET
|
||||
cp === 0x232a || // RIGHT-POINTING ANGLE BRACKET
|
||||
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
|
||||
(0x2e80 <= cp && cp <= 0x3247 && cp !== 0x303f) ||
|
||||
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
|
||||
(0x3250 <= cp && cp <= 0x4dbf) ||
|
||||
// CJK Unified Ideographs .. Yi Radicals
|
||||
(0x4e00 <= cp && cp <= 0xa4c6) ||
|
||||
// Hangul Jamo Extended-A
|
||||
(0xa960 <= cp && cp <= 0xa97c) ||
|
||||
// Hangul Syllables
|
||||
(0xac00 <= cp && cp <= 0xd7a3) ||
|
||||
// CJK Compatibility Ideographs
|
||||
(0xf900 <= cp && cp <= 0xfaff) ||
|
||||
// Vertical Forms
|
||||
(0xfe10 <= cp && cp <= 0xfe19) ||
|
||||
// CJK Compatibility Forms .. Small Form Variants
|
||||
(0xfe30 <= cp && cp <= 0xfe6b) ||
|
||||
// Halfwidth and Fullwidth Forms
|
||||
(0xff01 <= cp && cp <= 0xff60) ||
|
||||
(0xffe0 <= cp && cp <= 0xffe6) ||
|
||||
// Kana Supplement
|
||||
(0x1b000 <= cp && cp <= 0x1b001) ||
|
||||
// Enclosed Ideographic Supplement
|
||||
(0x1f200 <= cp && cp <= 0x1f251) ||
|
||||
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
|
||||
(0x20000 <= cp && cp <= 0x3fffd)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user