Skip to content

Commit

Permalink
Enable resourceurls (#140709)
Browse files Browse the repository at this point in the history
* Enable resourceurls

* Add selection to resourceurls from trees

* Rename identifier

* Respond to PR feedback

* Do not loop opver openEditors
  • Loading branch information
alexr00 authored Jan 25, 2022
1 parent e0e77c8 commit 1ee13ae
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 24 deletions.
14 changes: 3 additions & 11 deletions src/vs/editor/browser/services/openerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, matchesSomeScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener';
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, matchesSomeScheme, OpenOptions, ResolveExternalUriOptions, selectionFragment } from 'vs/platform/opener/common/opener';

class CommandOpener implements IOpener {

Expand Down Expand Up @@ -62,16 +62,8 @@ class EditorOpener implements IOpener {
if (typeof target === 'string') {
target = URI.parse(target);
}
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment);
if (match) {
// support file:///some/file.js#73,84
// support file:///some/file.js#L73
selection = {
startLineNumber: parseInt(match[1]),
startColumn: match[2] ? parseInt(match[2]) : 1
};
// remove fragment
const selection: { startLineNumber: number; startColumn: number; } | undefined = selectionFragment(target);
if (selection) {
target = target.with({ fragment: '' });
}

Expand Down
14 changes: 14 additions & 0 deletions src/vs/platform/opener/common/opener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,17 @@ export function matchesScheme(target: URI | string, scheme: string): boolean {
export function matchesSomeScheme(target: URI | string, ...schemes: string[]): boolean {
return schemes.some(scheme => matchesScheme(target, scheme));
}

export function selectionFragment(target: URI): { startLineNumber: number; startColumn: number; } | undefined {
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment);
if (match) {
// support file:///some/file.js#73,84
// support file:///some/file.js#L73
selection = {
startLineNumber: parseInt(match[1]),
startColumn: match[2] ? parseInt(match[2]) : 1
};
}
return selection;
}
55 changes: 47 additions & 8 deletions src/vs/workbench/browser/dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { parse, stringify } from 'vs/base/common/marshalling';
import { ILabelService } from 'vs/platform/label/common/label';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { withNullAsUndefined } from 'vs/base/common/types';
import { ITreeDataTransfer } from 'vs/workbench/common/views';
import { selectionFragment } from 'vs/platform/opener/common/opener';

//#region Editor / Resources DND

Expand All @@ -46,6 +48,11 @@ export class DraggedEditorGroupIdentifier {
constructor(readonly identifier: GroupIdentifier) { }
}

export class DraggedTreeItemsIdentifier {

constructor(readonly identifier: string) { }
}

export const CodeDataTransfers = {
EDITORS: 'CodeEditors',
FILES: 'CodeFiles'
Expand Down Expand Up @@ -74,14 +81,7 @@ export function extractEditorsDropData(e: DragEvent): Array<IDraggedResourceEdit
else {
try {
const rawResourcesData = e.dataTransfer.getData(DataTransfers.RESOURCES);
if (rawResourcesData) {
const resourcesRaw: string[] = JSON.parse(rawResourcesData);
for (const resourceRaw of resourcesRaw) {
if (resourceRaw.indexOf(':') > 0) { // mitigate https://github.com/microsoft/vscode/issues/124946
editors.push({ resource: URI.parse(resourceRaw) });
}
}
}
editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData));
} catch (error) {
// Invalid transfer
}
Expand Down Expand Up @@ -127,6 +127,45 @@ export function extractEditorsDropData(e: DragEvent): Array<IDraggedResourceEdit
}
}
}

return editors;
}

function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] {
const editors: IDraggedResourceEditorInput[] = [];

if (rawResourcesData) {
const resourcesRaw: string[] = JSON.parse(rawResourcesData);
for (const resourceRaw of resourcesRaw) {
if (resourceRaw.indexOf(':') > 0) { // mitigate https://github.com/microsoft/vscode/issues/124946
const resource = URI.parse(resourceRaw);
editors.push({
resource,
options: {
selection: selectionFragment(resource)
}
});
}
}
}

return editors;
}

export async function extractTreeDropData(dataTransfer: ITreeDataTransfer): Promise<Array<IDraggedResourceEditorInput>> {
const editors: IDraggedResourceEditorInput[] = [];
const resourcesKey = DataTransfers.RESOURCES.toLowerCase();

// Data Transfer: Resources
if (dataTransfer.has(resourcesKey)) {
try {
const rawResourcesData = await dataTransfer.get(resourcesKey)?.asString();
editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData));
} catch (error) {
// Invalid transfer
}
}

return editors;
}

Expand Down
40 changes: 36 additions & 4 deletions src/vs/workbench/browser/parts/editor/editorDropTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/editordroptarget';
import { LocalSelectionTransfer, DraggedEditorIdentifier, ResourcesDropHandler, DraggedEditorGroupIdentifier, DragAndDropObserver, containsDragType, CodeDataTransfers, extractFilesDropData } from 'vs/workbench/browser/dnd';
import { LocalSelectionTransfer, DraggedEditorIdentifier, ResourcesDropHandler, DraggedEditorGroupIdentifier, DragAndDropObserver, containsDragType, CodeDataTransfers, extractFilesDropData, DraggedTreeItemsIdentifier, extractTreeDropData } from 'vs/workbench/browser/dnd';
import { addDisposableListener, EventType, EventHelper, isAncestor } from 'vs/base/browser/dom';
import { IEditorGroupsAccessor, IEditorGroupView, fillActiveEditorViewState } from 'vs/workbench/browser/parts/editor/editor';
import { EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IEditorIdentifier, EditorInputCapabilities } from 'vs/workbench/common/editor';
import { IEditorIdentifier, EditorInputCapabilities, IUntypedEditorInput } from 'vs/workbench/common/editor';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
import { GroupDirection, IEditorGroupsService, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService';
import { toDisposable } from 'vs/base/common/lifecycle';
Expand All @@ -21,6 +21,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { ITreeViewsDragAndDropService } from 'vs/workbench/services/views/common/treeViewsDragAndDropService';
import { ITreeDataTransfer } from 'vs/workbench/common/views';

interface IDropOperation {
splitDirection?: GroupDirection;
Expand All @@ -40,14 +42,16 @@ class DropOverlay extends Themable {

private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance<DraggedTreeItemsIdentifier>();

constructor(
private accessor: IEditorGroupsAccessor,
private groupView: IEditorGroupView,
@IThemeService themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService,
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@ITreeViewsDragAndDropService private readonly treeViewsDragAndDropService: ITreeViewsDragAndDropService<ITreeDataTransfer>
) {
super(themeService);

Expand Down Expand Up @@ -202,7 +206,7 @@ class DropOverlay extends Themable {
return undefined;
}

private handleDrop(event: DragEvent, splitDirection?: GroupDirection): void {
private async handleDrop(event: DragEvent, splitDirection?: GroupDirection): Promise<void> {

// Determine target group
const ensureTargetGroup = () => {
Expand Down Expand Up @@ -292,6 +296,34 @@ class DropOverlay extends Themable {
}
}

// Check for tree items
else if (this.treeItemsTransfer.hasData(DraggedTreeItemsIdentifier.prototype)) {
const data = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype);
if (Array.isArray(data)) {
const editors: IUntypedEditorInput[] = [];
for (const id of data) {
const dataTransferItem = await this.treeViewsDragAndDropService.removeDragOperationTransfer(id.identifier);
if (dataTransferItem) {
const extractedDropData = await extractTreeDropData(dataTransferItem);
editors.push(...extractedDropData.map(editor => {
return {
...editor,
resource: editor.resource,
options: {
...editor.options,
pinned: true
}
};
}));
}
}

this.editorService.openEditors(editors, ensureTargetGroup(), { validateTrust: true });
}

this.treeItemsTransfer.clearData(DraggedTreeItemsIdentifier.prototype);
}

// Web: check for file transfer
else if (isWeb && containsDragType(event, DataTransfers.FILES)) {
let targetGroup: IEditorGroupView | undefined = undefined;
Expand Down
5 changes: 4 additions & 1 deletion src/vs/workbench/browser/parts/views/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { Command } from 'vs/editor/common/languages';
import { isCancellationError } from 'vs/base/common/errors';
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
import { CodeDataTransfers, fillEditorsDragData } from 'vs/workbench/browser/dnd';
import { CodeDataTransfers, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { Schemas } from 'vs/base/common/network';
import { ITreeViewsDragAndDropService } from 'vs/workbench/services/views/common/treeViewsDragAndDropService';
import { generateUuid } from 'vs/base/common/uuid';
Expand Down Expand Up @@ -1241,6 +1241,8 @@ const TREE_DRAG_UUID_MIME = 'tree-dnd';

export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
private readonly treeMimeType: string;
private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance<DraggedTreeItemsIdentifier>();

constructor(
private readonly treeId: string,
@ILabelService private readonly labelService: ILabelService,
Expand All @@ -1262,6 +1264,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
const uuid = generateUuid();
this.treeViewsDragAndDropService.addDragOperationTransfer(uuid, this.dndController.handleDrag(itemHandles, uuid));
originalEvent.dataTransfer.setData(TREE_DRAG_UUID_MIME, uuid);
this.treeItemsTransfer.setData([new DraggedTreeItemsIdentifier(uuid)], DraggedTreeItemsIdentifier.prototype);
}

private addResourceInfoToTransfer(originalEvent: DragEvent, resources: URI[]) {
Expand Down
4 changes: 4 additions & 0 deletions src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ declare module 'vscode' {
* When the items are dropped on **another tree item** in **the same tree**, your `TreeDataTransferItem` objects
* will be preserved. See the documentation for `TreeDataTransferItem` for how best to take advantage of this.
*
* To add a data transfer item that can be dragged into the editor, use the application specific mime type "resourceurls".
* The data for "resourceurls" should be an array of `toString()`ed Uris. To specify a cursor position in the file,
* set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number.
*
* @param source The source items for the drag and drop operation.
* @param treeDataTransfer The data transfer associated with this drag.
*/
Expand Down

0 comments on commit 1ee13ae

Please sign in to comment.