Skip to content

Commit

Permalink
files 💄
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Mar 17, 2017
1 parent 8d09c1a commit 05f19ee
Show file tree
Hide file tree
Showing 16 changed files with 137 additions and 41 deletions.
4 changes: 2 additions & 2 deletions src/vs/base/common/labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import platform = require('vs/base/common/platform');
import types = require('vs/base/common/types');
import { nativeSep, normalize } from 'vs/base/common/paths';
import { endsWith, ltrim } from 'vs/base/common/strings';
import { isEqual, isParent } from 'vs/platform/files/common/files';
import { isEqualOrParent } from 'vs/platform/files/common/files';

export interface ILabelProvider {

Expand Down Expand Up @@ -45,7 +45,7 @@ export function getPathLabel(resource: URI | string, basePathProvider?: URI | st

const basepath = basePathProvider && getPath(basePathProvider);

if (basepath && (isEqual(absolutePath, basepath) || isParent(absolutePath, basepath))) {
if (basepath && isEqualOrParent(absolutePath, basepath)) {
if (basepath === absolutePath) {
return ''; // no label if pathes are identical
}
Expand Down
15 changes: 14 additions & 1 deletion src/vs/base/common/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,11 @@ export function equalsIgnoreCase(a: string, b: string): boolean {
return false;
}

for (let i = 0; i < len1; i++) {
return doEqualsIgnoreCase(a, b);
}

export function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean {
for (let i = 0; i < stopAt; i++) {

let codeA = a.charCodeAt(i),
codeB = b.charCodeAt(i);
Expand All @@ -396,6 +400,15 @@ export function equalsIgnoreCase(a: string, b: string): boolean {
return true;
}

export function beginsWithIgnoreCase(str: string, candidate: string): boolean {
const candidateLength = candidate.length;
if (candidate.length > str.length) {
return false;
}

return doEqualsIgnoreCase(str, candidate, candidateLength);
}

/**
* @returns the length of the common prefix of the two strings.
*/
Expand Down
27 changes: 26 additions & 1 deletion src/vs/base/test/common/strings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import strings = require('vs/base/common/strings');

suite('Strings', () => {
test('equalsIgnoreCase', function () {

assert(strings.equalsIgnoreCase('', ''));
assert(!strings.equalsIgnoreCase('', '1'));
assert(!strings.equalsIgnoreCase('1', ''));
Expand All @@ -21,6 +20,32 @@ suite('Strings', () => {
assert(strings.equalsIgnoreCase('ÖL', 'Öl'));
});

test('beginsWithIgnoreCase', function () {
assert(strings.beginsWithIgnoreCase('', ''));
assert(!strings.beginsWithIgnoreCase('', '1'));
assert(strings.beginsWithIgnoreCase('1', ''));

assert(strings.beginsWithIgnoreCase('a', 'a'));
assert(strings.beginsWithIgnoreCase('abc', 'Abc'));
assert(strings.beginsWithIgnoreCase('abc', 'ABC'));
assert(strings.beginsWithIgnoreCase('Höhenmeter', 'HÖhenmeter'));
assert(strings.beginsWithIgnoreCase('ÖL', 'Öl'));

assert(strings.beginsWithIgnoreCase('alles klar', 'a'));
assert(strings.beginsWithIgnoreCase('alles klar', 'A'));
assert(strings.beginsWithIgnoreCase('alles klar', 'alles k'));
assert(strings.beginsWithIgnoreCase('alles klar', 'alles K'));
assert(strings.beginsWithIgnoreCase('alles klar', 'ALLES K'));
assert(strings.beginsWithIgnoreCase('alles klar', 'alles klar'));
assert(strings.beginsWithIgnoreCase('alles klar', 'ALLES KLAR'));

assert(!strings.beginsWithIgnoreCase('alles klar', ' ALLES K'));
assert(!strings.beginsWithIgnoreCase('alles klar', 'ALLES K '));
assert(!strings.beginsWithIgnoreCase('alles klar', 'öALLES K '));
assert(!strings.beginsWithIgnoreCase('alles klar', ' '));
assert(!strings.beginsWithIgnoreCase('alles klar', 'ö'));
});

test('compareIgnoreCase', function () {

function assertCompareIgnoreCase(a: string, b: string): void {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/code/electron-main/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import CommonEvent, { Emitter } from 'vs/base/common/event';
import product from 'vs/platform/node/product';
import { OpenContext } from 'vs/code/common/windows';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { isParent, isEqual } from 'vs/platform/files/common/files';
import { isParent, isEqual, isEqualOrParent } from 'vs/platform/files/common/files';

enum WindowError {
UNRESPONSIVE,
Expand Down Expand Up @@ -1113,7 +1113,7 @@ export class WindowsManager implements IWindowsMainService {
}

// match on file path
if (typeof w.openedWorkspacePath === 'string' && filePath && (isEqual(filePath, w.openedWorkspacePath) || isParent(filePath, w.openedWorkspacePath))) {
if (typeof w.openedWorkspacePath === 'string' && filePath && isEqualOrParent(filePath, w.openedWorkspacePath)) {
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/vs/code/node/windowsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as fs from 'fs';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/code/common/windows';
import { isParent, isEqual } from 'vs/platform/files/common/files';
import { isEqualOrParent } from 'vs/platform/files/common/files';

/**
* Exported subset of VSCodeWindow for testing.
Expand Down Expand Up @@ -47,7 +47,7 @@ export function findBestWindowOrFolder<SimpleWindow extends ISimpleWindow>({ win
}

function findBestWindow<WINDOW extends ISimpleWindow>(windows: WINDOW[], filePath: string): WINDOW {
const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && (isEqual(filePath, window.openedWorkspacePath) || isParent(filePath, window.openedWorkspacePath)));
const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && isEqualOrParent(filePath, window.openedWorkspacePath));
if (containers.length) {
return containers.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0];
}
Expand Down
35 changes: 30 additions & 5 deletions src/vs/platform/files/common/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { isLinux } from 'vs/base/common/platform';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';
import { equalsIgnoreCase, beginsWithIgnoreCase } from 'vs/base/common/strings';

export const IFileService = createDecorator<IFileService>('fileService');

Expand Down Expand Up @@ -231,7 +232,7 @@ export class FileChangesEvent extends events.Event {

// For deleted also return true when deleted folder is parent of target path
if (type === FileChangeType.DELETED) {
return isEqual(resource.fsPath, change.resource.fsPath) || isParent(resource.fsPath, change.resource.fsPath);
return isEqualOrParent(resource.fsPath, change.resource.fsPath);
}

return isEqual(resource.fsPath, change.resource.fsPath);
Expand Down Expand Up @@ -325,31 +326,55 @@ export function isEqual(resourceOrPathA: string | URI, resourceOrPathB: string |
const pathA = resourceOrPathA;
const pathB = resourceOrPathB as string;

if (isLinux || identityEquals) {
if (isLinux) {
return identityEquals;
}

if (pathA.length !== pathB.length) {
return equalsIgnoreCase(pathA, pathB);
}

export function isParent(path: string, candidate: string): boolean {
if (candidate.length > path.length) {
return false;
}

return pathA.toLowerCase() === pathB.toLowerCase();
if (!isLinux) {
return beginsWithIgnoreCase(path, candidate + paths.nativeSep);
}

return path.indexOf(candidate + paths.nativeSep) === 0;
}

export function isParent(path: string, candidate: string): boolean {
export function isEqualOrParent(path: string, candidate: string): boolean {
if (path === candidate) {
return true;
}

if (candidate.length > path.length) {
return false;
}

if (!isLinux) {
path = path.toLowerCase();
candidate = candidate.toLowerCase();

if (path === candidate) {
return true;
}
}

return path.indexOf(candidate + paths.nativeSep) === 0;
}

export function indexOf(path: string, candidate: string): number {
if (candidate.length > path.length) {
return -1;
}

if (path === candidate) {
return 0;
}

if (!isLinux) {
path = path.toLowerCase();
candidate = candidate.toLowerCase();
Expand Down
35 changes: 34 additions & 1 deletion src/vs/platform/files/test/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { join } from 'vs/base/common/paths';
import { FileChangeType, FileChangesEvent, isEqual, isParent, indexOf } from 'vs/platform/files/common/files';
import { FileChangeType, FileChangesEvent, isEqual, isParent, isEqualOrParent, indexOf } from 'vs/platform/files/common/files';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';

suite('Files', () => {
Expand Down Expand Up @@ -94,8 +94,41 @@ suite('Files', () => {
}
});

test('isEqualOrParent', function () {
assert(isEqualOrParent('foo/bar/test.ts', 'foo'));
assert(isEqualOrParent('/', '/'));
assert(isEqualOrParent('/foo', '/foo'));
assert(!isEqualOrParent('/foo', '/f'));
assert(!isEqualOrParent('/foo', '/foo/b'));
assert(isEqualOrParent('foo/bar/test.ts', 'foo/bar'));
assert(!isEqualOrParent('foo/bar/test.ts', '/foo/bar'));
assert(!isEqualOrParent('foo/bar/test.ts', 'foo/barr'));
assert(isEqualOrParent('foo/bar/test.ts', 'foo/bar/test.ts'));
assert(!isEqualOrParent('foo/bar/test.ts', 'foo/bar/test'));
assert(!isEqualOrParent('foo/bar/test.ts', 'foo/bar/test.'));

if (!isLinux) {
assert(isEqualOrParent('/foo', '/fOO'));
assert(isEqualOrParent('/fOO', '/foo'));
assert(isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.ts'));
assert(!isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.'));
}

if (isWindows) {
assert.ok(!isEqualOrParent('c:\\some\\path', 'c:\\some\\path'));
assert.ok(isEqualOrParent('c:\\some\\path', 'c:\\some'));
assert.ok(!isEqualOrParent('c:\\some\\path', 'c:\\some\\path'));
assert.ok(isEqualOrParent('c:\\some\\path', 'c:\\some'));
}
});

test('indexOf', function () {
assert.equal(indexOf('/some/path', '/some/path'), 0);
assert.equal(indexOf('/some/path/more', '/some/path'), 0);

assert.equal(indexOf('c:\\some\\path', 'c:\\some\\path'), 0);
assert.equal(indexOf('c:\\some\\path\\more', 'c:\\some\\path'), 0);

assert.equal(indexOf('/some/path', '/some/other/path'), -1);

if (isLinux) {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/platform/workspace/common/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import paths = require('vs/base/common/paths');
import { isEqual, isParent } from 'vs/platform/files/common/files';
import { isEqualOrParent } from 'vs/platform/files/common/files';

export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');

Expand Down Expand Up @@ -84,7 +84,7 @@ export class WorkspaceContextService implements IWorkspaceContextService {

public isInsideWorkspace(resource: URI): boolean {
if (resource && this.workspace) {
return isEqual(resource.fsPath, this.workspace.resource.fsPath) || isParent(resource.fsPath, this.workspace.resource.fsPath);
return isEqualOrParent(resource.fsPath, this.workspace.resource.fsPath);
}

return false;
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/parts/files/browser/fileActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
import labels = require('vs/base/common/labels');
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService, IFileStat, isEqual, isParent } from 'vs/platform/files/common/files';
import { IFileService, IFileStat, isEqual, isEqualOrParent } from 'vs/platform/files/common/files';
import { toResource, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor';
import { FileStat, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerViewModel';
import { ExplorerView } from 'vs/workbench/parts/files/browser/views/explorerView';
Expand Down Expand Up @@ -288,7 +288,7 @@ class RenameFileAction extends BaseRenameAction {
public runAction(newName: string): TPromise<any> {
let isDirtyCaseChange = false;

const dirty = this.textFileService.getDirty().filter(d => isEqual(d.fsPath, this.element.resource.fsPath) || isParent(d.fsPath, this.element.resource.fsPath));
const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath));
const dirtyRenamed = dirty.map(d => {
const targetPath = paths.join(this.element.parent.resource.fsPath, newName);
let renamed: URI;
Expand Down Expand Up @@ -702,7 +702,7 @@ export class BaseDeleteFileAction extends BaseFileAction {

// Handle dirty
let revertPromise: TPromise<any> = TPromise.as(null);
const dirty = this.textFileService.getDirty().filter(d => isEqual(d.fsPath, this.element.resource.fsPath) || isParent(d.fsPath, this.element.resource.fsPath));
const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath));
if (dirty.length) {
let message: string;
if (this.element.isDirectory) {
Expand Down Expand Up @@ -994,7 +994,7 @@ export class PasteFileAction extends BaseFileAction {
}

// Check if target is ancestor of pasted folder
if (!isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && (isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) || isParent(this.element.resource.fsPath, fileToCopy.resource.fsPath))) {
if (!isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && isEqualOrParent(this.element.resource.fsPath, fileToCopy.resource.fsPath)) {
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/parts/files/browser/views/explorerView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { prepareActions } from 'vs/workbench/browser/actionBarRegistry';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocussedContext, ExplorerFocussedContext } from 'vs/workbench/parts/files/common/files';
import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileChange, IFileService, isEqual, isParent } from 'vs/platform/files/common/files';
import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileChange, IFileService, isEqual, isEqualOrParent } from 'vs/platform/files/common/files';
import { RefreshViewExplorerAction, NewFolderAction, NewFileAction } from 'vs/workbench/parts/files/browser/fileActions';
import { FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider } from 'vs/workbench/parts/files/browser/views/explorerViewer';
import lifecycle = require('vs/base/common/lifecycle');
Expand Down Expand Up @@ -734,7 +734,7 @@ export class ExplorerView extends CollapsibleViewletView {
// Drop those path which are parents of the current one
for (let i = resolvedDirectories.length - 1; i >= 0; i--) {
const resource = resolvedDirectories[i];
if (isEqual(stat.resource.fsPath, resource.fsPath) || isParent(stat.resource.fsPath, resource.fsPath)) {
if (isEqualOrParent(stat.resource.fsPath, resource.fsPath)) {
resolvedDirectories.splice(i);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/parts/files/browser/views/explorerViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { ContributableActionProvider } from 'vs/workbench/browser/actionBarRegistry';
import { IFilesConfiguration } from 'vs/workbench/parts/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileOperationResult, FileOperationResult, IFileService, isEqual, isParent } from 'vs/platform/files/common/files';
import { IFileOperationResult, FileOperationResult, IFileService, isEqual, isEqualOrParent } from 'vs/platform/files/common/files';
import { ResourceMap } from 'vs/base/common/map';
import { DuplicateFileAction, ImportFileAction, IEditableData, IFileViewletState } from 'vs/workbench/parts/files/browser/fileActions';
import { IDataSource, ITree, IElementCallback, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragAndDrop, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
Expand Down Expand Up @@ -697,7 +697,7 @@ export class FileDragAndDrop implements IDragAndDrop {
return true; // Can not move a file to the same parent unless we copy
}

if (isEqual(target.resource.fsPath, source.resource.fsPath) || isParent(target.resource.fsPath, source.resource.fsPath)) {
if (isEqualOrParent(target.resource.fsPath, source.resource.fsPath)) {
return true; // Can not move a parent folder into one of its children
}

Expand Down Expand Up @@ -759,7 +759,7 @@ export class FileDragAndDrop implements IDragAndDrop {
};

// 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => isEqual(d.fsPath, source.resource.fsPath) || isParent(d.fsPath, source.resource.fsPath));
const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, source.resource.fsPath));
return TPromise.join(dirty.map(d => {
let moved: URI;

Expand Down Expand Up @@ -802,7 +802,7 @@ export class FileDragAndDrop implements IDragAndDrop {

// Move with overwrite if the user confirms
if (this.messageService.confirm(confirm)) {
const targetDirty = this.textFileService.getDirty().filter(d => isEqual(d.fsPath, targetResource.fsPath) || isParent(d.fsPath, targetResource.fsPath));
const targetDirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, targetResource.fsPath));

// Make sure to revert all dirty in target first to be able to overwrite properly
return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => {
Expand Down
10 changes: 5 additions & 5 deletions src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IEditor, IEditorViewState, isCommonCodeEditor } from 'vs/editor/common/
import { toResource, IEditorStacksModel, SideBySideEditorInput, IEditorGroup, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files';
import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent, isEqual, indexOf, isParent } from 'vs/platform/files/common/files';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent, isEqual, indexOf, isEqualOrParent } from 'vs/platform/files/common/files';
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
Expand Down Expand Up @@ -113,16 +113,16 @@ export class FileEditorTracker implements IWorkbenchContribution {

// Do NOT close any opened editor that matches the resource path (either equal or being parent) of the
// resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same
// path but different casing.
if (movedTo && (isEqual(resource.fsPath, movedTo.fsPath) || isParent(resource.fsPath, movedTo.fsPath)) && resource.fsPath.indexOf(movedTo.fsPath) === 0) {
// path but different casing.
if (movedTo && isEqualOrParent(resource.fsPath, movedTo.fsPath) && resource.fsPath.indexOf(movedTo.fsPath) === 0) {
return;
}

let matches = false;
if (arg1 instanceof FileChangesEvent) {
matches = arg1.contains(resource, FileChangeType.DELETED);
} else {
matches = isEqual(resource.fsPath, arg1.fsPath) || isParent(resource.fsPath, arg1.fsPath);
matches = isEqualOrParent(resource.fsPath, arg1.fsPath);
}

if (!matches) {
Expand Down Expand Up @@ -194,7 +194,7 @@ export class FileEditorTracker implements IWorkbenchContribution {
const resource = input.getResource();

// Update Editor if file (or any parent of the input) got renamed or moved
if (isEqual(resource.fsPath, oldResource.fsPath) || isParent(resource.fsPath, oldResource.fsPath)) {
if (isEqualOrParent(resource.fsPath, oldResource.fsPath)) {
let reopenFileResource: URI;
if (oldResource.toString() === resource.toString()) {
reopenFileResource = newResource; // file got moved
Expand Down
Loading

0 comments on commit 05f19ee

Please sign in to comment.