mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-12 19:39:20 -05:00
More improvements to default path label (#148293)
* more fun with labels * more label funs * add tests * fix tests
This commit is contained in:
@@ -3,12 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { hasDriveLetter, isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
import { firstOrDefault } from 'vs/base/common/arrays';
|
||||
import { hasDriveLetter, isRootOrDriveLetter, toSlashes } from 'vs/base/common/extpath';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { posix, sep, win32 } from 'vs/base/common/path';
|
||||
import { isMacintosh, isWindows, OperatingSystem, OS } from 'vs/base/common/platform';
|
||||
import { basename, extUri, extUriIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { rtrim, startsWithIgnoreCase } from 'vs/base/common/strings';
|
||||
import { ltrim, rtrim, startsWithIgnoreCase } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export interface IPathLabelFormatting {
|
||||
@@ -32,10 +33,10 @@ export interface IPathLabelFormatting {
|
||||
* Whether to convert to a relative path if the path
|
||||
* is within any of the opened workspace folders.
|
||||
*/
|
||||
readonly relative?: IWorkspaceFolderProvider;
|
||||
readonly relative?: IRelativePathProvider;
|
||||
}
|
||||
|
||||
export interface IWorkspaceFolderProvider {
|
||||
export interface IRelativePathProvider {
|
||||
getWorkspaceFolder(resource: URI): { uri: URI; name?: string } | null;
|
||||
getWorkspace(): {
|
||||
folders: { uri: URI; name?: string }[];
|
||||
@@ -47,58 +48,99 @@ export interface IUserHomeProvider {
|
||||
}
|
||||
|
||||
export function getPathLabel(resource: URI, formatting: IPathLabelFormatting): string {
|
||||
const { os, tildify: userHomeProvider, relative: rootProvider } = formatting;
|
||||
const pathLib = os === OperatingSystem.Windows ? win32 : posix;
|
||||
const extUriLib = os === OperatingSystem.Linux ? extUri : extUriIgnorePathCase;
|
||||
const { os, tildify: tildifier, relative: relatifier } = formatting;
|
||||
|
||||
let pathLabel: string | undefined = undefined;
|
||||
|
||||
// figure out relative path if we can by using root provider
|
||||
if (rootProvider) {
|
||||
const folder = rootProvider.getWorkspaceFolder(resource);
|
||||
if (folder) {
|
||||
if (extUriLib.isEqual(folder.uri, resource)) {
|
||||
pathLabel = ''; // no label if paths are identical
|
||||
} else {
|
||||
pathLabel = extUriLib.relativePath(folder.uri, resource) ?? '';
|
||||
}
|
||||
|
||||
// normalize
|
||||
if (pathLabel) {
|
||||
pathLabel = pathLib.normalize(pathLabel);
|
||||
}
|
||||
|
||||
if (rootProvider.getWorkspace().folders.length > 1) {
|
||||
const rootName = folder.name ? folder.name : extUriLib.basename(folder.uri);
|
||||
pathLabel = pathLabel ? `${rootName} • ${pathLabel}` : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
// return early with a relative path if we can resolve one
|
||||
if (relatifier) {
|
||||
const relativePath = getRelativePathLabel(resource, relatifier, os);
|
||||
if (typeof relativePath === 'string') {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
// return early if we can resolve a relative path label from the root
|
||||
if (typeof pathLabel === 'string') {
|
||||
return pathLabel;
|
||||
}
|
||||
|
||||
// otherwise we start with the absolute path and apply some normalization
|
||||
else {
|
||||
pathLabel = resource.fsPath;
|
||||
// otherwise try to resolve a absolute path label and
|
||||
// apply target OS standard path separators if target
|
||||
// OS differs from actual OS we are running in
|
||||
let absolutePath = resource.fsPath;
|
||||
if (os === OperatingSystem.Windows && !isWindows) {
|
||||
absolutePath = absolutePath.replace(/\//g, '\\');
|
||||
} else if (os !== OperatingSystem.Windows && isWindows) {
|
||||
absolutePath = absolutePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
// macOS/Linux: tildify with provided user home directory
|
||||
if (os !== OperatingSystem.Windows && userHomeProvider?.userHome) {
|
||||
pathLabel = tildify(pathLabel, userHomeProvider.userHome.fsPath, os);
|
||||
}
|
||||
if (os !== OperatingSystem.Windows && tildifier?.userHome) {
|
||||
let userHome = tildifier.userHome.fsPath;
|
||||
|
||||
// apply target OS standard path separators
|
||||
if (os === OperatingSystem.Windows) {
|
||||
pathLabel = pathLabel.replace(/\//g, '\\');
|
||||
} else {
|
||||
pathLabel = pathLabel.replace(/\\/g, '/');
|
||||
// This is a bit of a hack, but in order to figure out if the
|
||||
// resource is in the user home, we need to make sure to convert it
|
||||
// to a user home resource. We cannot assume that the resource is
|
||||
// already a user home resource.
|
||||
let userHomeCandidate: string;
|
||||
if (resource.scheme !== tildifier.userHome.scheme) {
|
||||
userHomeCandidate = tildifier.userHome.with({ path: `/${ltrim(resource.path, '/')}` }).fsPath;
|
||||
} else {
|
||||
userHomeCandidate = resource.fsPath;
|
||||
}
|
||||
|
||||
// In addition, if we are on windows platform, we need to make
|
||||
// sure to convert to POSIX path, because `tildify` only works
|
||||
// with POSIX paths.
|
||||
if (isWindows) {
|
||||
userHomeCandidate = toSlashes(userHomeCandidate);
|
||||
userHome = toSlashes(userHome);
|
||||
}
|
||||
|
||||
absolutePath = tildify(userHomeCandidate, userHome, os);
|
||||
}
|
||||
|
||||
// normalize
|
||||
return pathLib.normalize(normalizeDriveLetter(pathLabel, os === OperatingSystem.Windows));
|
||||
const pathLib = os === OperatingSystem.Windows ? win32 : posix;
|
||||
return pathLib.normalize(normalizeDriveLetter(absolutePath, os === OperatingSystem.Windows));
|
||||
}
|
||||
|
||||
function getRelativePathLabel(resource: URI, relativePathProvider: IRelativePathProvider, os: OperatingSystem): string | undefined {
|
||||
const pathLib = os === OperatingSystem.Windows ? win32 : posix;
|
||||
const extUriLib = os === OperatingSystem.Linux ? extUri : extUriIgnorePathCase;
|
||||
|
||||
const workspace = relativePathProvider.getWorkspace();
|
||||
const firstFolder = firstOrDefault(workspace.folders);
|
||||
if (!firstFolder) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// This is a bit of a hack, but in order to figure out the folder
|
||||
// the resource belongs to, we need to make sure to convert it
|
||||
// to a workspace resource. We cannot assume that the resource is
|
||||
// already matching the workspace.
|
||||
if (resource.scheme !== firstFolder.uri.scheme) {
|
||||
resource = firstFolder.uri.with({ path: `/${ltrim(resource.path, '/')}` });
|
||||
}
|
||||
|
||||
const folder = relativePathProvider.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let relativePathLabel: string | undefined = undefined;
|
||||
if (extUriLib.isEqual(folder.uri, resource)) {
|
||||
relativePathLabel = ''; // no label if paths are identical
|
||||
} else {
|
||||
relativePathLabel = extUriLib.relativePath(folder.uri, resource) ?? '';
|
||||
}
|
||||
|
||||
// normalize
|
||||
if (relativePathLabel) {
|
||||
relativePathLabel = pathLib.normalize(relativePathLabel);
|
||||
}
|
||||
|
||||
// always show root basename if there are multiple
|
||||
if (workspace.folders.length > 1) {
|
||||
const rootName = folder.name ? folder.name : extUriLib.basename(folder.uri);
|
||||
relativePathLabel = relativePathLabel ? `${rootName} • ${relativePathLabel}` : rootName;
|
||||
}
|
||||
|
||||
return relativePathLabel;
|
||||
}
|
||||
|
||||
export function getBaseLabel(resource: URI | string): string;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as labels from 'vs/base/common/labels';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWindows, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
suite('Labels', () => {
|
||||
(!isWindows ? test.skip : test)('shorten - windows', () => {
|
||||
@@ -162,4 +163,78 @@ suite('Labels', () => {
|
||||
assert.strictEqual(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do _not Save & Continue');
|
||||
}
|
||||
});
|
||||
|
||||
test('getPathLabel', () => {
|
||||
const winFileUri = URI.file('c:/some/folder/file.txt');
|
||||
const nixFileUri = URI.file('/some/folder/file.txt');
|
||||
const uncFileUri = URI.file('c:/some/folder/file.txt').with({ authority: 'auth' });
|
||||
const remoteFileUri = URI.file('/some/folder/file.txt').with({ scheme: 'vscode-test', authority: 'auth' });
|
||||
|
||||
// Basics
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Windows }), 'C:\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Macintosh }), 'c:/some/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Linux }), 'c:/some/folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Windows }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Macintosh }), '/some/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Linux }), '/some/folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(uncFileUri, { os: OperatingSystem.Windows }), '\\\\auth\\c:\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(uncFileUri, { os: OperatingSystem.Macintosh }), '/auth/c:/some/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(uncFileUri, { os: OperatingSystem.Linux }), '/auth/c:/some/folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(remoteFileUri, { os: OperatingSystem.Windows }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(remoteFileUri, { os: OperatingSystem.Macintosh }), '/some/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(remoteFileUri, { os: OperatingSystem.Linux }), '/some/folder/file.txt');
|
||||
|
||||
// Tildify
|
||||
|
||||
const nixUserHome = URI.file('/some');
|
||||
const remoteUserHome = URI.file('/some').with({ scheme: 'vscode-test', authority: 'auth' });
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Windows, tildify: { userHome: nixUserHome } }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Macintosh, tildify: { userHome: nixUserHome } }), '~/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Linux, tildify: { userHome: nixUserHome } }), '~/folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Windows, tildify: { userHome: remoteUserHome } }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Macintosh, tildify: { userHome: remoteUserHome } }), '~/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Linux, tildify: { userHome: remoteUserHome } }), '~/folder/file.txt');
|
||||
|
||||
const nixUntitledUri = URI.file('/some/folder/file.txt').with({ scheme: 'untitled' });
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Windows, tildify: { userHome: nixUserHome } }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Macintosh, tildify: { userHome: nixUserHome } }), '~/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Linux, tildify: { userHome: nixUserHome } }), '~/folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Windows, tildify: { userHome: remoteUserHome } }), '\\some\\folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Macintosh, tildify: { userHome: remoteUserHome } }), '~/folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Linux, tildify: { userHome: remoteUserHome } }), '~/folder/file.txt');
|
||||
|
||||
// Relative
|
||||
|
||||
const winFolder = URI.file('c:/some');
|
||||
const winRelativePathProvider: labels.IRelativePathProvider = {
|
||||
getWorkspace() { return { folders: [{ uri: winFolder }] }; },
|
||||
getWorkspaceFolder(resource) { return { uri: winFolder }; }
|
||||
};
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Windows, relative: winRelativePathProvider }), 'folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Macintosh, relative: winRelativePathProvider }), 'folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(winFileUri, { os: OperatingSystem.Linux, relative: winRelativePathProvider }), 'folder/file.txt');
|
||||
|
||||
const nixFolder = URI.file('/some');
|
||||
const nixRelativePathProvider: labels.IRelativePathProvider = {
|
||||
getWorkspace() { return { folders: [{ uri: nixFolder }] }; },
|
||||
getWorkspaceFolder(resource) { return { uri: nixFolder }; }
|
||||
};
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Windows, relative: nixRelativePathProvider }), 'folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Macintosh, relative: nixRelativePathProvider }), 'folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixFileUri, { os: OperatingSystem.Linux, relative: nixRelativePathProvider }), 'folder/file.txt');
|
||||
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Windows, relative: nixRelativePathProvider }), 'folder\\file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Macintosh, relative: nixRelativePathProvider }), 'folder/file.txt');
|
||||
assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Linux, relative: nixRelativePathProvider }), 'folder/file.txt');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { extname } from 'vs/base/common/path';
|
||||
import { IWorkspaceFolderProvider } from 'vs/base/common/labels';
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { extname as resourceExtname, basenameOrAuthority, joinPath, extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
@@ -16,7 +15,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
|
||||
|
||||
export interface IWorkspaceContextService extends IWorkspaceFolderProvider {
|
||||
export interface IWorkspaceContextService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWo
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspaceContextService, IWorkspace, isWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, isUntitledWorkspace, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { basenameOrAuthority, basename, joinPath, dirname, toLocalResource } from 'vs/base/common/resources';
|
||||
import { basenameOrAuthority, basename, joinPath, dirname } from 'vs/base/common/resources';
|
||||
import { tildify, getPathLabel } from 'vs/base/common/labels';
|
||||
import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting, IFormatterChangeEvent } from 'vs/platform/label/common/label';
|
||||
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
@@ -23,7 +23,6 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint<ResourceLabelFormatter[]>({
|
||||
extensionPoint: 'resourceLabelFormatters',
|
||||
@@ -176,17 +175,6 @@ export class LabelService extends Disposable implements ILabelService {
|
||||
|
||||
private doGetUriLabel(resource: URI, formatting?: ResourceLabelFormatting, options: { relative?: boolean; noPrefix?: boolean; endWithSeparator?: boolean } = {}): string {
|
||||
if (!formatting) {
|
||||
|
||||
// Without a formatter we have to fallback to figuring out what the
|
||||
// label could be that best matches the environment and workspace
|
||||
// the user is in.
|
||||
// As such, if the resource is with unfamiliar scheme, we convert it
|
||||
// to the default scheme and remote authority.
|
||||
|
||||
if (resource.scheme !== this.pathService.defaultUriScheme && resource.scheme !== Schemas.untitled) {
|
||||
resource = toLocalResource(resource, this.environmentService.remoteAuthority, this.pathService.defaultUriScheme);
|
||||
}
|
||||
|
||||
return getPathLabel(resource, {
|
||||
os: this.os,
|
||||
tildify: this.userHome ? { userHome: this.userHome } : undefined,
|
||||
|
||||
Reference in New Issue
Block a user