mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Merge pull request #43659 from Microsoft/foldersFromPath
Folders from path
This commit is contained in:
commit
46101daf74
|
@ -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);
|
||||
|
||||
return c.name.toLowerCase() === name.toLowerCase();
|
||||
})) {
|
||||
if (!exists) {
|
||||
return false;
|
||||
} else {
|
||||
p = child;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
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 => {
|
||||
|
|
Loading…
Reference in a new issue