From 249a796a76e2817bd4979e38a6d273b552700e3c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 20 Nov 2017 11:24:17 +0100 Subject: [PATCH] introduce and use getBaseLabel() --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 4 ++-- src/vs/base/common/labels.ts | 24 +++++++++++++++++-- src/vs/base/common/mime.ts | 18 +++++++------- src/vs/base/node/extfs.ts | 2 +- src/vs/base/test/common/labels.test.ts | 24 +++++++++++++++++++ src/vs/code/node/cliProcessMain.ts | 3 ++- .../electron-main/historyMainService.ts | 6 ++--- .../parts/quickopen/quickOpenController.ts | 4 ++-- .../browser/parts/titlebar/titlebarPart.ts | 6 +++-- src/vs/workbench/electron-browser/actions.ts | 4 ++-- .../parts/files/common/explorerModel.ts | 3 ++- .../parts/search/common/searchModel.ts | 6 ++--- .../page/electron-browser/welcomePage.ts | 4 ++-- .../node/configurationService.ts | 3 ++- .../services/files/node/fileService.ts | 3 ++- 15 files changed, 82 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 945dbd9c26b..26b4d09bcd3 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -11,7 +11,7 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte import { IMatch } from 'vs/base/common/filters'; import uri from 'vs/base/common/uri'; import paths = require('vs/base/common/paths'); -import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider } from 'vs/base/common/labels'; +import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider, getBaseLabel } from 'vs/base/common/labels'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { @@ -163,6 +163,6 @@ export class FileLabel extends IconLabel { public setFile(file: uri, provider: IWorkspaceFolderProvider, userHome: IUserHomeProvider): void { const parent = paths.dirname(file.fsPath); - this.setValue(paths.basename(file.fsPath), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath }); + this.setValue(getBaseLabel(file), parent && parent !== '.' ? getPathLabel(parent, provider, userHome) : '', { title: file.fsPath }); } } diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index a5ec95f8337..3cbc39b324c 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -6,7 +6,7 @@ import URI from 'vs/base/common/uri'; import platform = require('vs/base/common/platform'); -import { nativeSep, normalize, isEqualOrParent, isEqual, basename, join } from 'vs/base/common/paths'; +import { nativeSep, normalize, isEqualOrParent, isEqual, basename as pathsBasename, join } from 'vs/base/common/paths'; import { endsWith, ltrim } from 'vs/base/common/strings'; export interface IWorkspaceFolderProvider { @@ -46,7 +46,7 @@ export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFo } if (hasMultipleRoots) { - const rootName = basename(baseResource.uri.fsPath); + const rootName = pathsBasename(baseResource.uri.fsPath); pathLabel = pathLabel ? join(rootName, pathLabel) : rootName; // always show root basename if there are multiple } @@ -67,6 +67,26 @@ export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFo return res; } +export function getBaseLabel(resource: URI | string): string { + if (!resource) { + return null; + } + + if (typeof resource === 'string') { + resource = URI.file(resource); + } + + let base = pathsBasename(resource.fsPath); + + // Windows: basename('C:\') returns empty string, so make sure to always + // return the drive letter at least in that case. + if (!base) { + base = normalize(normalizeDriveLetter(resource.fsPath), true); + } + + return base; +} + function hasDriveLetter(path: string): boolean { return platform.isWindows && path && path[1] === ':'; } diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 12cfd795c41..467e8322249 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -112,23 +112,23 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] { } path = path.toLowerCase(); - let filename = paths.basename(path); + const filename = paths.basename(path); // 1.) User configured mappings have highest priority - let configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); + const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); if (configuredMime) { return [configuredMime, MIME_TEXT]; } // 2.) Registered mappings have middle priority - let registeredMime = guessMimeTypeByPath(path, filename, nonUserRegisteredAssociations); + const registeredMime = guessMimeTypeByPath(path, filename, nonUserRegisteredAssociations); if (registeredMime) { return [registeredMime, MIME_TEXT]; } // 3.) Firstline has lowest priority if (firstLine) { - let firstlineMime = guessMimeTypeByFirstline(firstLine); + const firstlineMime = guessMimeTypeByFirstline(firstLine); if (firstlineMime) { return [firstlineMime, MIME_TEXT]; } @@ -145,7 +145,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText // We want to prioritize associations based on the order they are registered so that the last registered // association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074 for (let i = associations.length - 1; i >= 0; i--) { - let association = associations[i]; + const association = associations[i]; // First exact name match if (filename === association.filenameLowercase) { @@ -156,7 +156,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText // Longest pattern match if (association.filepattern) { if (!patternMatch || association.filepattern.length > patternMatch.filepattern.length) { - let target = association.filepatternOnPath ? path : filename; // match on full path if pattern contains path separator + const target = association.filepatternOnPath ? path : filename; // match on full path if pattern contains path separator if (match(association.filepatternLowercase, target)) { patternMatch = association; } @@ -198,12 +198,12 @@ function guessMimeTypeByFirstline(firstLine: string): string { if (firstLine.length > 0) { for (let i = 0; i < registeredAssociations.length; ++i) { - let association = registeredAssociations[i]; + const association = registeredAssociations[i]; if (!association.firstline) { continue; } - let matches = firstLine.match(association.firstline); + const matches = firstLine.match(association.firstline); if (matches && matches.length > 0) { return association.mime; } @@ -227,7 +227,7 @@ export function isUnspecific(mime: string[] | string): boolean { export function suggestFilename(langId: string, prefix: string): string { for (let i = 0; i < registeredAssociations.length; i++) { - let association = registeredAssociations[i]; + const association = registeredAssociations[i]; if (association.userConfigured) { continue; // only support registered ones } diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index e50ad6b102c..77b60dad317 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -378,7 +378,7 @@ export function realcaseSync(path: string): string { return path; } - const name = paths.basename(path).toLowerCase(); + const name = (paths.basename(path) /* can be '' for windows drive letters */ || path).toLowerCase(); try { const entries = readdirSync(dir); const found = entries.filter(e => e.toLowerCase() === name); // use a case insensitive search diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index 650b4ee86a6..9659955759a 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -8,6 +8,7 @@ import * as assert from 'assert'; import labels = require('vs/base/common/labels'); import platform = require('vs/base/common/platform'); +import { getBaseLabel } from 'vs/base/common/labels'; suite('Labels', () => { test('shorten - windows', () => { @@ -143,4 +144,27 @@ suite('Labels', () => { assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'somefile.txt - monaco - Visual Studio Code'); assert.strictEqual(labels.template(t, { dirty: '* ', activeEditorShort: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), '* somefile.txt - monaco - Visual Studio Code'); }); + + test('getBaseLabel - unix', () => { + if (platform.isWindows) { + assert.ok(true); + return; + } + + assert.equal(getBaseLabel('/some/folder/file.txt'), 'file.txt'); + assert.equal(getBaseLabel('/some/folder'), 'folder'); + assert.equal(getBaseLabel('/'), '/'); + }); + + test('getBaseLabel - windows', () => { + if (!platform.isWindows) { + assert.ok(true); + return; + } + + assert.equal(getBaseLabel('c:'), 'C:\\'); + assert.equal(getBaseLabel('c:\\'), 'C:\\'); + assert.equal(getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt'); + assert.equal(getBaseLabel('c:\\some\\folder'), 'folder'); + }); }); \ No newline at end of file diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 07bd80a27bb..288dba2a9af 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -33,6 +33,7 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen import { mkdirp } from 'vs/base/node/pfs'; import { IChoiceService } from 'vs/platform/message/common/message'; import { ChoiceCliService } from 'vs/platform/message/node/messageCli'; +import { getBaseLabel } from 'vs/base/common/labels'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -95,7 +96,7 @@ class Main { const extension = path.isAbsolute(id) ? id : path.join(process.cwd(), id); return this.extensionManagementService.install(extension).then(() => { - console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", path.basename(extension))); + console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension))); }); }); diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 0815d3461bd..40fdc0b0802 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -12,7 +12,7 @@ import { trim } from 'vs/base/common/strings'; import { IStorageService } from 'vs/platform/storage/node/storage'; import { app } from 'electron'; import { ILogService } from 'vs/platform/log/common/log'; -import { getPathLabel } from 'vs/base/common/labels'; +import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; import { IPath } from 'vs/platform/windows/common/windows'; import CommonEvent, { Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; @@ -257,8 +257,8 @@ export class HistoryMainService implements IHistoryMainService { type: 'custom', name: nls.localize('recentFolders', "Recent Workspaces"), items: this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(workspace => { - const title = isSingleFolderWorkspaceIdentifier(workspace) ? path.basename(workspace) : getWorkspaceLabel(workspace, this.environmentService); - const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", path.basename(workspace), getPathLabel(path.dirname(workspace))) : nls.localize('codeWorkspace', "Code Workspace"); + const title = isSingleFolderWorkspaceIdentifier(workspace) ? getBaseLabel(workspace) : getWorkspaceLabel(workspace, this.environmentService); + const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(path.dirname(workspace))) : nls.localize('codeWorkspace', "Code Workspace"); return { type: 'task', diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index bc3517a4f7e..7f3f848b0fd 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -25,7 +25,6 @@ import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, Qu import { QuickOpenWidget, HideReason } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; import labels = require('vs/base/common/labels'); -import paths = require('vs/base/common/paths'); import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { Registry } from 'vs/platform/registry/common/platform'; import { IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor'; @@ -57,6 +56,7 @@ import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { FileKind, IFileService } from 'vs/platform/files/common/files'; import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { getBaseLabel } from 'vs/base/common/labels'; const HELP_PREFIX = '?'; @@ -1272,7 +1272,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { } else { const resourceInput = input as IResourceInput; this.resource = resourceInput.resource; - this.label = paths.basename(resourceInput.resource.fsPath); + this.label = getBaseLabel(resourceInput.resource); this.description = labels.getPathLabel(resources.dirname(this.resource), contextService, environmentService); this.dirty = this.resource && this.textFileService.isDirty(this.resource); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 14162ffd5ee..f7f6d7ee3d3 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -307,9 +307,11 @@ export class TitlebarPart extends Part implements ITitleService { const path = segments.slice(0, pathOffset).join(paths.sep); - let label = paths.basename(path); + let label: string; if (!isFile) { - label = paths.basename(paths.dirname(path)); + label = labels.getBaseLabel(paths.dirname(path)); + } else { + label = labels.getBaseLabel(path); } actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService)); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 70f30c4faf7..7f2ba451b94 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -38,7 +38,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import * as os from 'os'; import { webFrame } from 'electron'; -import { getPathLabel } from 'vs/base/common/labels'; +import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -724,7 +724,7 @@ export abstract class BaseOpenRecentAction extends Action { let description: string; if (isSingleFolderWorkspaceIdentifier(workspace)) { path = workspace; - label = paths.basename(path); + label = getBaseLabel(path); description = getPathLabel(paths.dirname(path), null, environmentService); } else { path = workspace.configPath; diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index eef97559c91..8e1a539b42d 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -14,6 +14,7 @@ import { IEditorInput } from 'vs/platform/editor/common/editor'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEditorGroup, toResource } from 'vs/workbench/common/editor'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { getPathLabel } from 'vs/base/common/labels'; export class Model { @@ -78,7 +79,7 @@ export class FileStat implements IFileStat { public isDirectoryResolved: boolean; - constructor(resource: URI, public root: FileStat, isDirectory?: boolean, hasChildren?: boolean, name: string = paths.basename(resource.fsPath), mtime?: number, etag?: string) { + constructor(resource: URI, public root: FileStat, isDirectory?: boolean, hasChildren?: boolean, name: string = getPathLabel(resource), mtime?: number, etag?: string) { this.resource = resource; this.name = name; this.isDirectory = !!isDirectory; diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 2fe02b67166..cd9a3adc2ab 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import paths = require('vs/base/common/paths'); import objects = require('vs/base/common/objects'); import strings = require('vs/base/common/strings'); import errors = require('vs/base/common/errors'); @@ -25,6 +24,7 @@ import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { getBaseLabel } from 'vs/base/common/labels'; export class Match { @@ -302,7 +302,7 @@ export class FileMatch extends Disposable { } public name(): string { - return paths.basename(this.resource().fsPath); + return getBaseLabel(this.resource()); } public add(match: Match, trigger?: boolean) { @@ -379,7 +379,7 @@ export class FolderMatch extends Disposable { } public name(): string { - return paths.basename(this.resource().fsPath); + return getBaseLabel(this.resource()); } public parent(): SearchResult { diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index f1d893f88ea..3ce78b87c09 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -30,7 +30,7 @@ import { IExtensionEnablementService, IExtensionManagementService, IExtensionGal import { used } from 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { tildify } from 'vs/base/common/labels'; +import { tildify, getBaseLabel } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils'; @@ -286,7 +286,7 @@ class WelcomePage { let parent: string; let wsPath: string; if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = path.basename(workspace); + label = getBaseLabel(workspace); parent = path.dirname(workspace); wsPath = workspace; } else { diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index b918b72fa17..89f541dacf6 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -40,6 +40,7 @@ import { Schemas } from 'vs/base/common/network'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; import { distinct } from 'vs/base/common/arrays'; import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; +import { getBaseLabel } from 'vs/base/common/labels'; export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { @@ -335,7 +336,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! const id = createHash('md5').update(folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); const folder = URI.file(folderPath.fsPath); - return new Workspace(id, paths.basename(folderPath.fsPath), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); + return new Workspace(id, getBaseLabel(folder), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); }); } diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index dcd29519524..efd4248eadd 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -39,6 +39,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { getBaseLabel } from 'vs/base/common/labels'; export interface IEncodingOverride { resource: uri; @@ -1007,7 +1008,7 @@ export class StatResolver { this.resource = resource; this.isDirectory = isDirectory; this.mtime = mtime; - this.name = paths.basename(resource.fsPath); + this.name = getBaseLabel(resource); this.etag = etag(size, mtime); this.size = size; -- GitLab