未验证 提交 46101daf 编写于 作者: I Isidor Nikolic 提交者: GitHub

Merge pull request #43659 from Microsoft/foldersFromPath

Folders from path
......@@ -99,6 +99,7 @@
line-height: 17px;
min-height: 34px;
margin-top: -1px;
word-wrap: break-word;
}
/* Action bar support */
......
......@@ -1325,6 +1325,7 @@ export class CopyPathAction extends Action {
}
}
export function validateFileName(parent: IFileStat, name: string, allowOverwriting: boolean = false): string {
// Produce a well formed file name
......@@ -1335,21 +1336,29 @@ export function validateFileName(parent: IFileStat, name: string, allowOverwriti
return nls.localize('emptyFileNameError', "A file or folder name must be provided.");
}
const names: string[] = name.split(/[\\/]/).filter(part => !!part);
// Do not allow to overwrite existing file
if (!allowOverwriting) {
if (parent.children && parent.children.some((c) => {
if (isLinux) {
return c.name === name;
let p = parent;
const alreadyExisting = names.every((folderName) => {
let { exists, child } = alreadyExists(p, folderName);
if (!exists) {
return false;
} else {
p = child;
return true;
}
});
return c.name.toLowerCase() === name.toLowerCase();
})) {
if (alreadyExisting) {
return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name);
}
}
// Invalid File name
if (!paths.isValidBasename(name)) {
if (names.some((folderName) => !paths.isValidBasename(folderName))) {
return nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", trimLongName(name));
}
......@@ -1364,6 +1373,28 @@ export function validateFileName(parent: IFileStat, name: string, allowOverwriti
return null;
}
function alreadyExists(parent: IFileStat, name: string): { exists: boolean, child: IFileStat | undefined } {
let duplicateChild: IFileStat;
if (parent.children) {
let exists: boolean = parent.children.some((c) => {
let found: boolean;
if (isLinux) {
found = c.name === name;
} else {
found = c.name.toLowerCase() === name.toLowerCase();
}
if (found) {
duplicateChild = c;
}
return found;
});
return { exists, child: duplicateChild };
}
return { exists: false, child: undefined };
}
function trimLongName(name: string): string {
if (name && name.length > 255) {
return `${name.substr(0, 255)}...`;
......
......@@ -16,7 +16,7 @@ import resources = require('vs/base/common/resources');
import errors = require('vs/base/common/errors');
import { IAction, ActionRunner as BaseActionRunner, IActionRunner } from 'vs/base/common/actions';
import comparers = require('vs/base/common/comparers');
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { isMacintosh, isLinux } from 'vs/base/common/platform';
import glob = require('vs/base/common/glob');
import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels';
......@@ -308,6 +308,10 @@ export class FileRenderer implements IRenderer {
done(false, false);
}
}),
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_UP, (e: IKeyboardEvent) => {
const initialRelPath: string = relative(stat.root.resource.fsPath, stat.parent.resource.fsPath);
this.displayCurrentPath(inputBox, initialRelPath, fileKind);
}),
DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => {
done(inputBox.isInputValid(), true);
}),
......@@ -315,6 +319,22 @@ export class FileRenderer implements IRenderer {
styler
];
}
private displayCurrentPath(inputBox: InputBox, initialRelPath: string, fileKind: FileKind) {
if (inputBox.validate()) {
const value = inputBox.value;
if (value && value.search(/[\\/]/) !== -1) { // only show if there's a slash
const newPath = paths.normalize(paths.join(initialRelPath, value), true);
const fileType: string = FileKind[fileKind].toLowerCase();
inputBox.showMessage({
type: MessageType.INFO,
content: nls.localize('constructedPath', "Create {0} in **{1}**", fileType, newPath),
formatContent: true
});
}
}
}
}
// Explorer Accessibility Provider
......
......@@ -194,8 +194,12 @@ suite('Files - View Model', () => {
assert(validateFileName(s, '') !== null);
assert(validateFileName(s, ' ') !== null);
assert(validateFileName(s, 'Read Me') === null, 'name containing space');
assert(validateFileName(s, 'foo/bar') !== null);
assert(validateFileName(s, 'foo\\bar') !== null);
assert(validateFileName(s, 'foo/bar') === null);
assert(validateFileName(s, 'foo\\bar') === null);
assert(validateFileName(s, 'all/slashes/are/same') === null);
assert(validateFileName(s, 'theres/one/different\\slash') === null);
assert(validateFileName(s, '/slashAtBeginning') === null);
if (isWindows) {
assert(validateFileName(s, 'foo:bar') !== null);
assert(validateFileName(s, 'foo*bar') !== null);
......
......@@ -136,6 +136,33 @@ suite('FileService', () => {
}, error => onError(error, done));
});
test('createFolder: creating multiple folders at once', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
event = e;
});
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
service.resolveFile(uri.file(testDir)).done(parent => {
const resource = uri.file(path.join(parent.resource.fsPath, ...multiFolderPaths));
return service.createFolder(resource).then(f => {
const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];
assert.equal(f.name, lastFolderName);
assert.equal(fs.existsSync(f.resource.fsPath), true);
assert.ok(event);
assert.equal(event.resource.fsPath, resource.fsPath);
assert.equal(event.operation, FileOperation.CREATE);
assert.equal(event.target.resource.fsPath, resource.fsPath);
assert.equal(event.target.isDirectory, true);
toDispose.dispose();
done();
});
}, error => onError(error, done));
});
test('touchFile', function (done: () => void) {
service.touchFile(uri.file(path.join(testDir, 'test.txt'))).done(s => {
assert.equal(s.name, 'test.txt');
......@@ -156,6 +183,28 @@ suite('FileService', () => {
}, error => onError(error, done));
});
test('touchFile - multi folder', function (done: () => void) {
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
service.touchFile(uri.file(path.join(testDir, ...multiFolderPaths, 'test.txt'))).done(s => {
assert.equal(s.name, 'test.txt');
assert.equal(fs.existsSync(s.resource.fsPath), true);
assert.equal(fs.readFileSync(s.resource.fsPath).length, 0);
const stat = fs.statSync(s.resource.fsPath);
return TPromise.timeout(10).then(() => {
return service.touchFile(s.resource).done(s => {
const statNow = fs.statSync(s.resource.fsPath);
assert.ok(statNow.mtime.getTime() >= stat.mtime.getTime()); // one some OS the resolution seems to be 1s, so we use >= here
assert.equal(statNow.size, stat.size);
done();
});
});
}, error => onError(error, done));
});
test('renameFile', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
......@@ -179,6 +228,32 @@ suite('FileService', () => {
}, error => onError(error, done));
});
test('renameFile - multi folder', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
event = e;
});
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
const renameToPath = path.join(...multiFolderPaths, 'other.html');
const resource = uri.file(path.join(testDir, 'index.html'));
service.resolveFile(resource).done(source => {
return service.rename(source.resource, renameToPath).then(renamed => {
assert.equal(fs.existsSync(renamed.resource.fsPath), true);
assert.equal(fs.existsSync(source.resource.fsPath), false);
assert.ok(event);
assert.equal(event.resource.fsPath, resource.fsPath);
assert.equal(event.operation, FileOperation.MOVE);
assert.equal(event.target.resource.fsPath, renamed.resource.fsPath);
toDispose.dispose();
done();
});
}, error => onError(error, done));
});
test('renameFolder', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
......@@ -202,6 +277,31 @@ suite('FileService', () => {
});
});
test('renameFolder - multi folder', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
event = e;
});
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
const renameToPath = path.join(...multiFolderPaths);
const resource = uri.file(path.join(testDir, 'deep'));
service.resolveFile(resource).done(source => {
return service.rename(source.resource, renameToPath).then(renamed => {
assert.equal(fs.existsSync(renamed.resource.fsPath), true);
assert.equal(fs.existsSync(source.resource.fsPath), false);
assert.ok(event);
assert.equal(event.resource.fsPath, resource.fsPath);
assert.equal(event.operation, FileOperation.MOVE);
assert.equal(event.target.resource.fsPath, renamed.resource.fsPath);
toDispose.dispose();
done();
});
});
});
test('renameFile - MIX CASE', function (done: () => void) {
let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册