Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { URI } from '../../../../base/common/uri.js';
7
import { ResourceTextEdit } from '../../../browser/services/bulkEditService.js';
8
import { DocumentDropEdit, DocumentPasteEdit, DropYieldTo, WorkspaceEdit } from '../../../common/languages.js';
9
import { Range } from '../../../common/core/range.js';
10
import { SnippetParser } from '../../snippet/browser/snippetParser.js';
11
import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';
12
13
/**
14
* Given a {@link DropOrPasteEdit} and set of ranges, creates a {@link WorkspaceEdit} that applies the insert text from
15
* the {@link DropOrPasteEdit} at each range plus any additional edits.
16
*/
17
export function createCombinedWorkspaceEdit(uri: URI, ranges: readonly Range[], edit: DocumentPasteEdit | DocumentDropEdit): WorkspaceEdit {
18
// If the edit insert text is empty, skip applying at each range
19
if (typeof edit.insertText === 'string' ? edit.insertText === '' : edit.insertText.snippet === '') {
20
return {
21
edits: edit.additionalEdit?.edits ?? []
22
};
23
}
24
25
return {
26
edits: [
27
...ranges.map(range =>
28
new ResourceTextEdit(uri,
29
{ range, text: typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) + '$0' : edit.insertText.snippet, insertAsSnippet: true }
30
)),
31
...(edit.additionalEdit?.edits ?? [])
32
]
33
};
34
}
35
36
export function sortEditsByYieldTo<T extends {
37
readonly kind: HierarchicalKind | undefined;
38
readonly handledMimeType?: string;
39
readonly yieldTo?: readonly DropYieldTo[];
40
}>(edits: readonly T[]): T[] {
41
function yieldsTo(yTo: DropYieldTo, other: T): boolean {
42
if ('mimeType' in yTo) {
43
return yTo.mimeType === other.handledMimeType;
44
}
45
return !!other.kind && yTo.kind.contains(other.kind);
46
}
47
48
// Build list of nodes each node yields to
49
const yieldsToMap = new Map<T, T[]>();
50
for (const edit of edits) {
51
for (const yTo of edit.yieldTo ?? []) {
52
for (const other of edits) {
53
if (other === edit) {
54
continue;
55
}
56
57
if (yieldsTo(yTo, other)) {
58
let arr = yieldsToMap.get(edit);
59
if (!arr) {
60
arr = [];
61
yieldsToMap.set(edit, arr);
62
}
63
arr.push(other);
64
}
65
}
66
}
67
}
68
69
if (!yieldsToMap.size) {
70
return Array.from(edits);
71
}
72
73
// Topological sort
74
const visited = new Set<T>();
75
const tempStack: T[] = [];
76
77
function visit(nodes: T[]): T[] {
78
if (!nodes.length) {
79
return [];
80
}
81
82
const node = nodes[0];
83
if (tempStack.includes(node)) {
84
console.warn('Yield to cycle detected', node);
85
return nodes;
86
}
87
88
if (visited.has(node)) {
89
return visit(nodes.slice(1));
90
}
91
92
let pre: T[] = [];
93
const yTo = yieldsToMap.get(node);
94
if (yTo) {
95
tempStack.push(node);
96
pre = visit(yTo);
97
tempStack.pop();
98
}
99
100
visited.add(node);
101
102
return [...pre, node, ...visit(nodes.slice(1))];
103
}
104
105
return visit(Array.from(edits));
106
}
107
108