提交 81f8ee02 编写于 作者: B Benjamin Pasero

improve cross window tab dnd (support dirty, restore UI state)

上级 9e64b030
......@@ -39,4 +39,23 @@ export class DelayedDragHandler {
public dispose(): void {
this.clearDragTimeout();
}
}
\ No newline at end of file
}
// Common data transfers
export const DataTransfers = {
/**
* Application specific resource transfer type.
*/
URL: 'URL',
/**
* Browser specific transfer type to download.
*/
DOWNLOAD_URL: 'DownloadURL',
/**
* Typicaly transfer type for copy/paste transfers.
*/
TEXT: 'text/plain'
};
\ No newline at end of file
......@@ -10,6 +10,7 @@ import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
import URI from 'vs/base/common/uri';
import { basename } from 'vs/base/common/paths';
import { getPathLabel } from 'vs/base/common/labels';
import { DataTransfers } from 'vs/base/browser/dnd';
export class ElementsDragAndDropData implements _.IDragAndDropData {
......@@ -116,7 +117,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
// Apply some datatransfer types to allow for dragging the element outside of the application
const resource = this.toResource(source);
if (resource) {
originalEvent.dataTransfer.setData('text/plain', getPathLabel(resource));
originalEvent.dataTransfer.setData(DataTransfers.TEXT, getPathLabel(resource));
}
}
}
\ No newline at end of file
......@@ -24,6 +24,7 @@ import _ = require('vs/base/parts/tree/browser/tree');
import { KeyCode } from 'vs/base/common/keyCodes';
import Event, { Emitter } from 'vs/base/common/event';
import { IDomNodePagePosition } from 'vs/base/browser/dom';
import { DataTransfers } from 'vs/base/browser/dnd';
export interface IRow {
element: HTMLElement;
......@@ -1291,7 +1292,7 @@ export class TreeView extends HeightMap {
}
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer.setData('URL', item.uri);
e.dataTransfer.setData(DataTransfers.URL, item.uri);
if (e.dataTransfer.setDragImage) {
let label: string;
......
......@@ -12,6 +12,8 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isArray } from 'vs/base/common/types';
import URI from 'vs/base/common/uri';
import { DataTransfers } from 'vs/base/browser/dnd';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
export interface IEditorDescriptor {
instantiate(instantiationService: IInstantiationService): BaseEditor;
......@@ -204,20 +206,50 @@ export interface IDraggedResource {
isExternal: boolean;
}
export function extractResources(e: DragEvent, externalOnly?: boolean): IDraggedResource[] {
const resources: IDraggedResource[] = [];
export interface IDraggedEditor extends IDraggedResource {
backupResource?: URI;
viewState?: IEditorViewState;
}
export interface ISerializedDraggedEditor {
resource: string;
backupResource: string;
viewState: IEditorViewState;
}
export const CodeDataTransfers = {
EDITOR: 'CodeEditor'
};
export function extractResources(e: DragEvent, externalOnly?: boolean): (IDraggedResource | IDraggedEditor)[] {
const resources: (IDraggedResource | IDraggedEditor)[] = [];
if (e.dataTransfer.types.length > 0) {
// Check for in-app DND
// Check for window-to-window DND
if (!externalOnly) {
const rawData = e.dataTransfer.getData('URL');
if (rawData) {
// Data Transfer: Code Editor
const rawEditorData = e.dataTransfer.getData(CodeDataTransfers.EDITOR);
if (rawEditorData) {
try {
resources.push({ resource: URI.parse(rawData), isExternal: false });
const draggedEditor = JSON.parse(rawEditorData) as ISerializedDraggedEditor;
resources.push({ resource: URI.parse(draggedEditor.resource), backupResource: URI.parse(draggedEditor.backupResource), viewState: draggedEditor.viewState, isExternal: false });
} catch (error) {
// Invalid URI
}
}
// Data Transfer: URL
else {
const rawURLData = e.dataTransfer.getData(DataTransfers.URL);
if (rawURLData) {
try {
resources.push({ resource: URI.parse(rawURLData), isExternal: false });
} catch (error) {
// Invalid URI
}
}
}
}
// Check for native file transfer
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDraggedResource, IDraggedEditor, extractResources } from 'vs/workbench/browser/editor';
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { extname } from 'vs/base/common/paths';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { BACKUP_FILE_RESOLVE_OPTIONS, IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { TPromise } from 'vs/base/common/winjs.base';
import { Schemas } from 'vs/base/common/network';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Position } from 'vs/platform/editor/common/editor';
import { onUnexpectedError } from 'vs/base/common/errors';
/**
* Shared function across some editor components to handle drag & drop of external resources. E.g. of folders and workspace files
* to open them in the window instead of the editor or to handle dirty editors being dropped between instances of Code.
*/
export class EditorAreaDropHandler {
constructor(
@IFileService private fileService: IFileService,
@IWindowsService private windowsService: IWindowsService,
@IWindowService private windowService: IWindowService,
@IWorkspacesService private workspacesService: IWorkspacesService,
@ITextFileService private textFileService: ITextFileService,
@IBackupFileService private backupFileService: IBackupFileService,
@IEditorGroupService private groupService: IEditorGroupService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
) {
}
public handleDrop(event: DragEvent, afterDrop: () => void, targetPosition: Position, targetIndex?: number): void {
const resources = extractResources(event).filter(r => r.resource.scheme === Schemas.file || r.resource.scheme === Schemas.untitled);
if (!resources.length) {
return;
}
return this.doHandleDrop(resources).then(isWorkspaceOpening => {
if (isWorkspaceOpening) {
return void 0; // return early if the drop operation resulted in this window changing to a workspace
}
// Add external ones to recently open list unless dropped resource is a workspace
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
if (externalResources.length) {
this.windowsService.addRecentlyOpened(externalResources.map(resource => resource.fsPath));
}
// Open in Editor
return this.windowService.focusWindow()
.then(() => this.editorService.openEditors(resources.map(r => {
return {
input: {
resource: r.resource,
options: {
pinned: true,
index: targetIndex,
viewState: (r as IDraggedEditor).viewState
}
},
position: targetPosition
};
}))).then(() => {
// Finish with provided function
afterDrop();
});
}).done(null, onUnexpectedError);
}
private doHandleDrop(resources: (IDraggedResource | IDraggedEditor)[]): TPromise<boolean> {
// Check for dirty editor being dropped
if (resources.length === 1 && !resources[0].isExternal && (resources[0] as IDraggedEditor).backupResource) {
return this.handleDirtyEditorDrop(resources[0]);
}
// Check for workspace file being dropped
if (resources.some(r => r.isExternal)) {
return this.handleWorkspaceFileDrop(resources);
}
return TPromise.as(false);
}
private handleDirtyEditorDrop(droppedDirtyEditor: IDraggedEditor): TPromise<boolean> {
// Untitled: always ensure that we open a new untitled for each file we drop
if (droppedDirtyEditor.resource.scheme === Schemas.untitled) {
droppedDirtyEditor.resource = this.untitledEditorService.createOrGet().getResource();
}
// Return early if the resource is already dirty in target or opened already
if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.groupService.getStacksModel().isOpen(droppedDirtyEditor.resource)) {
return TPromise.as(false);
}
// Resolve the contents of the dropped dirty resource from source
return this.textFileService.resolveTextContent(droppedDirtyEditor.backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(content => {
// Set the contents of to the resource to the target
return this.backupFileService.backupResource(droppedDirtyEditor.resource, this.backupFileService.parseBackupContent(content.value));
}).then(() => false, () => false /* ignore any error */);
}
private handleWorkspaceFileDrop(resources: (IDraggedResource | IDraggedEditor)[]): TPromise<boolean> {
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
const externalWorkspaceResources: { workspaces: URI[], folders: URI[] } = {
workspaces: [],
folders: []
};
return TPromise.join(externalResources.map(resource => {
// Check for Workspace
if (extname(resource.fsPath) === `.${WORKSPACE_EXTENSION}`) {
externalWorkspaceResources.workspaces.push(resource);
return void 0;
}
// Check for Folder
return this.fileService.resolveFile(resource).then(stat => {
if (stat.isDirectory) {
externalWorkspaceResources.folders.push(stat.resource);
}
}, error => void 0);
})).then(_ => {
const { workspaces, folders } = externalWorkspaceResources;
// Return early if no external resource is a folder or workspace
if (workspaces.length === 0 && folders.length === 0) {
return false;
}
// Pass focus to window
this.windowService.focusWindow();
let workspacesToOpen: TPromise<string[]>;
// Open in separate windows if we drop workspaces or just one folder
if (workspaces.length > 0 || folders.length === 1) {
workspacesToOpen = TPromise.as([...workspaces, ...folders].map(resources => resources.fsPath));
}
// Multiple folders: Create new workspace with folders and open
else if (folders.length > 1) {
workspacesToOpen = this.workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]);
}
// Open
workspacesToOpen.then(workspaces => {
this.windowsService.openWindow(workspaces, { forceReuseWindow: true });
});
return true;
});
}
}
\ No newline at end of file
......@@ -26,20 +26,16 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { TitleControl, ITitleAreaControl, handleWorkspaceExternalDrop } from 'vs/workbench/browser/parts/editor/titleControl';
import { TitleControl, ITitleAreaControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier } from 'vs/workbench/common/editor';
import { extractResources } from 'vs/workbench/browser/editor';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Themable, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_GROUP_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BORDER } from 'vs/workbench/common/theme';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IMessageService } from 'vs/platform/message/common/message';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { EditorAreaDropHandler } from 'vs/workbench/browser/parts/editor/editorAreaDropHandler';
export enum Rochade {
NONE,
......@@ -148,12 +144,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionService private extensionService: IExtensionService,
@IInstantiationService private instantiationService: IInstantiationService,
@IWindowService private windowService: IWindowService,
@IWindowsService private windowsService: IWindowsService,
@IThemeService themeService: IThemeService,
@IFileService private fileService: IFileService,
@IMessageService private messageService: IMessageService,
@IWorkspacesService private workspacesService: IWorkspacesService
@IThemeService themeService: IThemeService
) {
super(themeService);
......@@ -1121,36 +1112,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// Check for URI transfer
else {
const droppedResources = extractResources(e).filter(r => r.resource.scheme === 'file' || r.resource.scheme === 'untitled');
if (droppedResources.length) {
handleWorkspaceExternalDrop(droppedResources, $this.fileService, $this.messageService, $this.windowsService, $this.windowService, $this.workspacesService).then(handled => {
if (handled) {
return;
}
// Add external ones to recently open list
const externalResources = droppedResources.filter(d => d.isExternal).map(d => d.resource);
if (externalResources.length) {
$this.windowsService.addRecentlyOpened(externalResources.map(resource => resource.fsPath));
}
// Open in Editor
$this.windowService.focusWindow()
.then(() => editorService.openEditors(droppedResources.map(d => {
return {
input: { resource: d.resource, options: { pinned: true } },
position: splitEditor ? freeGroup : position
};
}))).then(() => {
if (splitEditor && splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
}
const dropHandler = $this.instantiationService.createInstance(EditorAreaDropHandler);
dropHandler.handleDrop(e, () => {
if (splitEditor && splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
}
groupService.focusGroup(splitEditor ? splitTo : position);
})
.done(null, errors.onUnexpectedError);
});
}
groupService.focusGroup(splitEditor ? splitTo : position);
}, splitEditor ? freeGroup : position);
}
}
......
......@@ -30,22 +30,24 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService } from 'vs/platform/actions/common/actions';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { TitleControl, handleWorkspaceExternalDrop } from 'vs/workbench/browser/parts/editor/titleControl';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { extractResources } from 'vs/workbench/browser/editor';
import { CodeDataTransfers, ISerializedDraggedEditor } from 'vs/workbench/browser/editor';
import { getOrSet } from 'vs/base/common/map';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { Dimension } from 'vs/base/browser/builder';
import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { DataTransfers } from 'vs/base/browser/dnd';
import { EditorAreaDropHandler } from 'vs/workbench/browser/parts/editor/editorAreaDropHandler';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
interface IEditorInputLabel {
name: string;
......@@ -78,11 +80,9 @@ export class TabsTitleControl extends TitleControl {
@IMessageService messageService: IMessageService,
@IMenuService menuService: IMenuService,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IWindowService private windowService: IWindowService,
@IWindowsService private windowsService: IWindowsService,
@IThemeService themeService: IThemeService,
@IFileService private fileService: IFileService,
@IWorkspacesService private workspacesService: IWorkspacesService
@ITextFileService private textFileService: ITextFileService,
@IBackupFileService private backupFileService: IBackupFileService
) {
super(contextMenuService, instantiationService, editorService, editorGroupService, contextKeyService, keybindingService, telemetryService, messageService, menuService, quickOpenService, themeService);
......@@ -763,12 +763,22 @@ export class TabsTitleControl extends TitleControl {
const resource = toResource(editor, { supportSideBySide: true });
if (resource) {
const resourceStr = resource.toString();
e.dataTransfer.setData('URL', resourceStr); // enables cross window DND of tabs
e.dataTransfer.setData('text/plain', getPathLabel(resource)); // enables dropping tab resource path into text controls
e.dataTransfer.setData(DataTransfers.TEXT, getPathLabel(resource)); // enables dropping tab resource path into text controls
if (resource.scheme === 'file') {
e.dataTransfer.setData('DownloadURL', [MIME_BINARY, editor.getName(), resourceStr].join(':')); // enables support to drag a tab as file to desktop
e.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, editor.getName(), resourceStr].join(':')); // enables support to drag a tab as file to desktop
}
// Prepare IDraggedEditor transfer
const activeEditor = this.editorService.getActiveEditor();
const draggedEditor: ISerializedDraggedEditor = {
resource: resourceStr,
backupResource: this.textFileService.isDirty(resource) ? this.backupFileService.toBackupResource(resource).toString() : void 0,
viewState: activeEditor instanceof BaseTextEditor ? activeEditor.getControl().saveViewState() : void 0
};
e.dataTransfer.setData(CodeDataTransfers.EDITOR, JSON.stringify(draggedEditor)); // enables cross window DND of tabs into the editor area
}
// Fixes https://github.com/Microsoft/vscode/issues/18733
......@@ -872,37 +882,8 @@ export class TabsTitleControl extends TitleControl {
// External DND
else {
this.handleExternalDrop(e, targetPosition, targetIndex);
}
}
private handleExternalDrop(e: DragEvent, targetPosition: Position, targetIndex: number): void {
const droppedResources = extractResources(e).filter(r => r.resource.scheme === 'file' || r.resource.scheme === 'untitled');
if (droppedResources.length) {
DOM.EventHelper.stop(e, true);
handleWorkspaceExternalDrop(droppedResources, this.fileService, this.messageService, this.windowsService, this.windowService, this.workspacesService).then(handled => {
if (handled) {
return;
}
// Add external ones to recently open list
const externalResources = droppedResources.filter(d => d.isExternal).map(d => d.resource);
if (externalResources.length) {
this.windowsService.addRecentlyOpened(externalResources.map(resource => resource.fsPath));
}
// Open in Editor
this.windowService.focusWindow()
.then(() => this.editorService.openEditors(droppedResources.map(d => {
return {
input: { resource: d.resource, options: { pinned: true, index: targetIndex } },
position: targetPosition
};
}))).then(() => {
this.editorGroupService.focusGroup(targetPosition);
}).done(null, errors.onUnexpectedError);
});
const dropHandler = this.instantiationService.createInstance(EditorAreaDropHandler);
dropHandler.handleDrop(e, () => this.editorGroupService.focusGroup(targetPosition), targetPosition, targetIndex);
}
}
......
......@@ -37,12 +37,6 @@ import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/a
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Themable } from 'vs/workbench/common/theme';
import { IDraggedResource } from 'vs/workbench/browser/editor';
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { extname } from 'vs/base/common/paths';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Dimension } from 'vs/base/browser/builder';
......@@ -500,74 +494,3 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.editorActionsToolbar.dispose();
}
}
/**
* Shared function across some editor components to handle drag & drop of folders and workspace files
* to open them in the window instead of the editor.
*/
export function handleWorkspaceExternalDrop(
resources: IDraggedResource[],
fileService: IFileService,
messageService: IMessageService,
windowsService: IWindowsService,
windowService: IWindowService,
workspacesService: IWorkspacesService
): TPromise<boolean /* handled */> {
// Return early if there are no external resources
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
if (!externalResources.length) {
return TPromise.as(false);
}
const externalWorkspaceResources: { workspaces: URI[], folders: URI[] } = {
workspaces: [],
folders: []
};
return TPromise.join(externalResources.map(resource => {
// Check for Workspace
if (extname(resource.fsPath) === `.${WORKSPACE_EXTENSION}`) {
externalWorkspaceResources.workspaces.push(resource);
return void 0;
}
// Check for Folder
return fileService.resolveFile(resource).then(stat => {
if (stat.isDirectory) {
externalWorkspaceResources.folders.push(stat.resource);
}
}, error => void 0);
})).then(_ => {
const { workspaces, folders } = externalWorkspaceResources;
// Return early if no external resource is a folder or workspace
if (workspaces.length === 0 && folders.length === 0) {
return false;
}
// Pass focus to window
windowService.focusWindow();
let workspacesToOpen: TPromise<string[]>;
// Open in separate windows if we drop workspaces or just one folder
if (workspaces.length > 0 || folders.length === 1) {
workspacesToOpen = TPromise.as([...workspaces, ...folders].map(resources => resources.fsPath));
}
// Multiple folders: Create new workspace with folders and open
else if (folders.length > 1) {
workspacesToOpen = workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]);
}
// Open
workspacesToOpen.then(workspaces => {
windowsService.openWindow(workspaces, { forceReuseWindow: true });
});
return true;
});
}
......@@ -56,6 +56,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
import { getPathLabel } from 'vs/base/common/labels';
import { extractResources } from 'vs/workbench/browser/editor';
import { relative } from 'path';
import { DataTransfers } from 'vs/base/browser/dnd';
export class FileDataSource implements IDataSource {
constructor(
......@@ -793,10 +794,10 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
// Apply some datatransfer types to allow for dragging the element outside of the application
if (source) {
if (!source.isDirectory) {
originalEvent.dataTransfer.setData('DownloadURL', [MIME_BINARY, source.name, source.resource.toString()].join(':'));
originalEvent.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, source.name, source.resource.toString()].join(':'));
}
originalEvent.dataTransfer.setData('text/plain', getPathLabel(source.resource));
originalEvent.dataTransfer.setData(DataTransfers.TEXT, getPathLabel(source.resource));
}
}
......
......@@ -26,6 +26,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { PANEL_BACKGROUND } from 'vs/workbench/common/theme';
import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/parts/terminal/electron-browser/terminalColorRegistry';
import { DataTransfers } from 'vs/base/browser/dnd';
export class TerminalPanel extends Panel {
......@@ -256,7 +257,7 @@ export class TerminalPanel extends Panel {
}
// Check if the file was dragged from the tree explorer
let uri = e.dataTransfer.getData('URL');
let uri = e.dataTransfer.getData(DataTransfers.URL);
if (uri) {
uri = URI.parse(uri).path;
} else if (e.dataTransfer.files.length > 0) {
......
......@@ -40,6 +40,14 @@ export interface IBackupFileService {
*/
loadBackupResource(resource: Uri): TPromise<Uri>;
/**
* Given a resource, returns the associated backup resource.
*
* @param resource The resource to get the backup resource for.
* @return The backup resource.
*/
toBackupResource(resource: Uri): Uri;
/**
* Backs up a resource.
*
......
......@@ -135,7 +135,7 @@ export class BackupFileService implements IBackupFileService {
public loadBackupResource(resource: Uri): TPromise<Uri> {
return this.ready.then(model => {
const backupResource = this.getBackupResource(resource);
const backupResource = this.toBackupResource(resource);
if (!backupResource) {
return void 0;
}
......@@ -155,7 +155,7 @@ export class BackupFileService implements IBackupFileService {
}
return this.ready.then(model => {
const backupResource = this.getBackupResource(resource);
const backupResource = this.toBackupResource(resource);
if (!backupResource) {
return void 0;
}
......@@ -175,7 +175,7 @@ export class BackupFileService implements IBackupFileService {
public discardResourceBackup(resource: Uri): TPromise<void> {
return this.ready.then(model => {
const backupResource = this.getBackupResource(resource);
const backupResource = this.toBackupResource(resource);
if (!backupResource) {
return void 0;
}
......@@ -218,7 +218,7 @@ export class BackupFileService implements IBackupFileService {
return textSource.lines.slice(1).join(textSource.EOL); // The first line of a backup text file is the file name
}
protected getBackupResource(resource: Uri): Uri {
public toBackupResource(resource: Uri): Uri {
if (!this.backupEnabled) {
return null;
}
......
......@@ -41,8 +41,8 @@ class TestBackupFileService extends BackupFileService {
super(workspaceBackupPath, fileService);
}
public getBackupResource(resource: Uri): Uri {
return super.getBackupResource(resource);
public toBackupResource(resource: Uri): Uri {
return super.toBackupResource(resource);
}
}
......@@ -73,7 +73,7 @@ suite('BackupFileService', () => {
const workspaceHash = crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex');
const filePathHash = crypto.createHash('md5').update(backupResource.fsPath).digest('hex');
const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath;
assert.equal(service.getBackupResource(backupResource).fsPath, expectedPath);
assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
});
test('should get the correct backup path for untitled files', () => {
......@@ -82,7 +82,7 @@ suite('BackupFileService', () => {
const workspaceHash = crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex');
const filePathHash = crypto.createHash('md5').update(backupResource.fsPath).digest('hex');
const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath;
assert.equal(service.getBackupResource(backupResource).fsPath, expectedPath);
assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
});
});
......
......@@ -831,7 +831,7 @@ export class TestBackupFileService implements IBackupFileService {
public loadBackupResource(resource: URI): TPromise<URI> {
return this.hasBackup(resource).then(hasBackup => {
if (hasBackup) {
return this.getBackupResource(resource);
return this.toBackupResource(resource);
}
return void 0;
......@@ -846,7 +846,7 @@ export class TestBackupFileService implements IBackupFileService {
return TPromise.as(void 0);
}
public getBackupResource(resource: URI): URI {
public toBackupResource(resource: URI): URI {
return null;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册