提交 5070ce43 编写于 作者: B Benjamin Pasero 提交者: GitHub

Merge pull request #18279 from hun1ahpu/fix12040

Fix for issue 12040
......@@ -8,6 +8,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform';
import { fill } from 'vs/base/common/arrays';
import { rtrim } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
import { endsWith } from 'vs/base/common/strings';
/**
* The forward slash path separator.
......@@ -394,3 +395,57 @@ export const isAbsoluteRegex = /^((\/|[a-zA-Z]:\\)[^\(\)<>\\'\"\[\]]+)/;
export function isAbsolute(path: string): boolean {
return isAbsoluteRegex.test(path);
}
/**
* Shortens the paths but keeps them easy to distinguish.
* Replaces not important parts with ellipsis.
* Every shorten path matches only one original path and vice versa.
*/
export function shorten(paths: string[]): string[] {
const ellipsis = '\u2026';
let shortenedPaths: string[] = new Array(paths.length);
let match = false;
// for every path
for (let path = 0; path < paths.length; path++) {
let segments: string[] = paths[path].split(nativeSep);
match = true;
// pick the first shortest subpath found
for (let subpathLength = 1; match && subpathLength <= segments.length; subpathLength++) {
for (let start = segments.length - subpathLength; match && start >= 0; start--) {
match = false;
let subpath = segments.slice(start, start + subpathLength).join(nativeSep);
// that is unique to any other path
for (let otherPath = 0; !match && otherPath < paths.length; otherPath++) {
if (otherPath !== path && paths[otherPath].indexOf(subpath) > -1) {
// suffix subpath treated specially as we consider no match 'x' and 'x/...'
let isSubpathEnding: boolean = (start + subpathLength === segments.length);
let isOtherPathEnding: boolean = endsWith(paths[otherPath], subpath);
match = !isSubpathEnding || isOtherPathEnding;
}
}
if (!match) {
// found unique subpath
let result = subpath;
if (start + subpathLength < segments.length) {
result = result + nativeSep + ellipsis;
}
if (start > 0) {
result = ellipsis + nativeSep + result;
}
shortenedPaths[path] = result;
}
}
}
if (match) {
// use full path if no unique subpaths found
shortenedPaths[path] = paths[path];
}
}
return shortenedPaths;
}
\ No newline at end of file
......@@ -246,4 +246,44 @@ suite('Paths', () => {
assert.equal(paths.isAbsolute('F\\a\\b\\c'), false);
assert.equal(paths.isAbsolute('F:\\a'), true);
});
test('shorten', () => {
// nothing to shorten
assert.deepEqual(paths.shorten(['a']), ['a']);
assert.deepEqual(paths.shorten(['a', 'b']), ['a', 'b']);
assert.deepEqual(paths.shorten(['a', 'b', 'c']), ['a', 'b', 'c']);
// completely different paths
assert.deepEqual(paths.shorten(['a\\b', 'c\\d', 'e\\f']), ['\\b', '\\d', '\\f']);
// same beginning
assert.deepEqual(paths.shorten(['a', 'a\\b']), ['a', '\\b']);
assert.deepEqual(paths.shorten(['a\\b', 'a\\b\\c']), ['\\b', '\\c']);
assert.deepEqual(paths.shorten(['a', 'a\\b', 'a\\b\\c']), ['a', '\\b', '\\c']);
assert.deepEqual(paths.shorten(['x:\\a\\b', 'x:\\a\\c']), ['\\b', '\\c'], 'TODO: drive letter (or schema) should be preserved');
assert.deepEqual(paths.shorten(['\\\\a\\b', '\\\\a\\c']), ['\\b', '\\c'], 'TODO: root uri should be preserved');
// same ending
assert.deepEqual(paths.shorten(['a', 'b\\a']), ['a', 'b\\']);
assert.deepEqual(paths.shorten(['a\\b\\c', 'd\\b\\c']), ['a\\', 'd\\']);
assert.deepEqual(paths.shorten(['a\\b\\c\\d', 'f\\b\\c\\d']), ['a\\', 'f\\']);
assert.deepEqual(paths.shorten(['d\\e\\a\\b\\c', 'd\\b\\c']), ['\\a\\', 'd\\b\\']);
assert.deepEqual(paths.shorten(['a\\b\\c\\d', 'a\\f\\b\\c\\d']), ['a\\b\\', '\\f\\']);
assert.deepEqual(paths.shorten(['a\\b\\a', 'b\\b\\a']), ['a\\b\\', 'b\\b\\']);
assert.deepEqual(paths.shorten(['d\\f\\a\\b\\c', 'h\\d\\b\\c']), ['\\a\\', 'h\\']);
assert.deepEqual(paths.shorten(['a\\b\\c', 'x:\\0\\a\\b\\c']), ['a\\b\\c', '\\0\\'], 'TODO: drive letter (or schema) should be always preserved');
assert.deepEqual(paths.shorten(['x:\\a\\b', 'y:\\a\\b']), ['x:\\', 'y:\\']);
assert.deepEqual(paths.shorten(['\\\\x\\b', '\\\\y\\b']), ['\\x\\', '\\y\\'], 'TODO: \\\\x instead of …\\x');
// same in the middle
assert.deepEqual(paths.shorten(['a\\b\\c', 'd\\b\\e']), ['\\c', '\\e']);
// case-sensetive
assert.deepEqual(paths.shorten(['a\\b\\c', 'd\\b\\C']), ['\\c', '\\C']);
assert.deepEqual(paths.shorten(['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']), ['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']);
assert.deepEqual(paths.shorten(['a', 'a\\b', 'b']), ['a', 'a\\b', 'b']);
assert.deepEqual(paths.shorten(['', 'a', 'b', 'b\\c', 'a\\c']), ['', 'a', 'b', 'b\\c', 'a\\c']);
assert.deepEqual(paths.shorten(['src\\vs\\workbench\\parts\\execution\\electron-browser', 'src\\vs\\workbench\\parts\\execution\\electron-browser\\something', 'src\\vs\\workbench\\parts\\terminal\\electron-browser']), ['\\execution\\electron-browser', '\\something', '\\terminal\\']);
});
});
\ No newline at end of file
......@@ -256,15 +256,10 @@ export class TabsTitleControl extends TitleControl {
const labels: IEditorInputLabel[] = [];
const mapLabelToDuplicates = new LinkedMap<string, IEditorInputLabel[]>();
const mapLabelAndDescriptionToDuplicates = new LinkedMap<string, IEditorInputLabel[]>();
// Build labels and descriptions for each editor
editors.forEach(editor => {
let description = editor.getDescription();
if (description && description.indexOf(paths.nativeSep) >= 0) {
description = paths.basename(description); // optimize for editors that show paths and build a shorter description to keep tab width small
}
const item: IEditorInputLabel = {
editor,
name: editor.getName(),
......@@ -274,31 +269,20 @@ export class TabsTitleControl extends TitleControl {
labels.push(item);
mapLabelToDuplicates.getOrSet(item.name, []).push(item);
if (item.description) {
mapLabelAndDescriptionToDuplicates.getOrSet(item.name + item.description, []).push(item);
}
});
// Mark label duplicates
// Mark duplicates and shorten their descriptions
const labelDuplicates = mapLabelToDuplicates.values();
labelDuplicates.forEach(duplicates => {
if (duplicates.length > 1) {
duplicates.forEach(duplicate => {
let shortenedDescriptions = paths.shorten(duplicates.map(duplicate => duplicate.editor.getDescription()));
duplicates.forEach((duplicate, i) => {
duplicate.description = shortenedDescriptions[i];
duplicate.hasAmbiguousName = true;
});
}
});
// React to duplicates for combination of label and description
const descriptionDuplicates = mapLabelAndDescriptionToDuplicates.values();
descriptionDuplicates.forEach(duplicates => {
if (duplicates.length > 1) {
duplicates.forEach(duplicate => {
duplicate.description = duplicate.editor.getDescription(); // fallback to full description if the short description still has duplicates
});
}
});
return labels;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册