Path: blob/main/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { URI } from '../../../../base/common/uri.js';6import { ResourceTextEdit } from '../../../browser/services/bulkEditService.js';7import { DocumentDropEdit, DocumentPasteEdit, DropYieldTo, WorkspaceEdit } from '../../../common/languages.js';8import { Range } from '../../../common/core/range.js';9import { SnippetParser } from '../../snippet/browser/snippetParser.js';10import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';1112/**13* Given a {@link DropOrPasteEdit} and set of ranges, creates a {@link WorkspaceEdit} that applies the insert text from14* the {@link DropOrPasteEdit} at each range plus any additional edits.15*/16export function createCombinedWorkspaceEdit(uri: URI, ranges: readonly Range[], edit: DocumentPasteEdit | DocumentDropEdit): WorkspaceEdit {17// If the edit insert text is empty, skip applying at each range18if (typeof edit.insertText === 'string' ? edit.insertText === '' : edit.insertText.snippet === '') {19return {20edits: edit.additionalEdit?.edits ?? []21};22}2324return {25edits: [26...ranges.map(range =>27new ResourceTextEdit(uri,28{ range, text: typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) + '$0' : edit.insertText.snippet, insertAsSnippet: true }29)),30...(edit.additionalEdit?.edits ?? [])31]32};33}3435export function sortEditsByYieldTo<T extends {36readonly kind: HierarchicalKind | undefined;37readonly handledMimeType?: string;38readonly yieldTo?: readonly DropYieldTo[];39}>(edits: readonly T[]): T[] {40function yieldsTo(yTo: DropYieldTo, other: T): boolean {41if ('mimeType' in yTo) {42return yTo.mimeType === other.handledMimeType;43}44return !!other.kind && yTo.kind.contains(other.kind);45}4647// Build list of nodes each node yields to48const yieldsToMap = new Map<T, T[]>();49for (const edit of edits) {50for (const yTo of edit.yieldTo ?? []) {51for (const other of edits) {52if (other === edit) {53continue;54}5556if (yieldsTo(yTo, other)) {57let arr = yieldsToMap.get(edit);58if (!arr) {59arr = [];60yieldsToMap.set(edit, arr);61}62arr.push(other);63}64}65}66}6768if (!yieldsToMap.size) {69return Array.from(edits);70}7172// Topological sort73const visited = new Set<T>();74const tempStack: T[] = [];7576function visit(nodes: T[]): T[] {77if (!nodes.length) {78return [];79}8081const node = nodes[0];82if (tempStack.includes(node)) {83console.warn('Yield to cycle detected', node);84return nodes;85}8687if (visited.has(node)) {88return visit(nodes.slice(1));89}9091let pre: T[] = [];92const yTo = yieldsToMap.get(node);93if (yTo) {94tempStack.push(node);95pre = visit(yTo);96tempStack.pop();97}9899visited.add(node);100101return [...pre, node, ...visit(nodes.slice(1))];102}103104return visit(Array.from(edits));105}106107108