Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/dataTransfer.ts
3291 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 { distinct } from './arrays.js';
7
import { Iterable } from './iterator.js';
8
import { URI } from './uri.js';
9
import { generateUuid } from './uuid.js';
10
11
export interface IDataTransferFile {
12
readonly id: string;
13
readonly name: string;
14
readonly uri?: URI;
15
data(): Promise<Uint8Array>;
16
}
17
18
export interface IDataTransferItem {
19
id?: string;
20
asString(): Thenable<string>;
21
asFile(): IDataTransferFile | undefined;
22
value: unknown;
23
}
24
25
export function createStringDataTransferItem(stringOrPromise: string | Promise<string>, id?: string): IDataTransferItem {
26
return {
27
id,
28
asString: async () => stringOrPromise,
29
asFile: () => undefined,
30
value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined,
31
};
32
}
33
34
export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise<Uint8Array>, id?: string): IDataTransferItem {
35
const file = { id: generateUuid(), name: fileName, uri, data };
36
return {
37
id,
38
asString: async () => '',
39
asFile: () => file,
40
value: undefined,
41
};
42
}
43
44
export interface IReadonlyVSDataTransfer extends Iterable<readonly [string, IDataTransferItem]> {
45
/**
46
* Get the total number of entries in this data transfer.
47
*/
48
get size(): number;
49
50
/**
51
* Check if this data transfer contains data for `mimeType`.
52
*
53
* This uses exact matching and does not support wildcards.
54
*/
55
has(mimeType: string): boolean;
56
57
/**
58
* Check if this data transfer contains data matching `pattern`.
59
*
60
* This allows matching for wildcards, such as `image/*`.
61
*
62
* Use the special `files` mime type to match any file in the data transfer.
63
*/
64
matches(pattern: string): boolean;
65
66
/**
67
* Retrieve the first entry for `mimeType`.
68
*
69
* Note that if you want to find all entries for a given mime type, use {@link IReadonlyVSDataTransfer.entries} instead.
70
*/
71
get(mimeType: string): IDataTransferItem | undefined;
72
}
73
74
export class VSDataTransfer implements IReadonlyVSDataTransfer {
75
76
private readonly _entries = new Map<string, IDataTransferItem[]>();
77
78
public get size(): number {
79
let size = 0;
80
for (const _ of this._entries) {
81
size++;
82
}
83
return size;
84
}
85
86
public has(mimeType: string): boolean {
87
return this._entries.has(this.toKey(mimeType));
88
}
89
90
public matches(pattern: string): boolean {
91
const mimes = [...this._entries.keys()];
92
if (Iterable.some(this, ([_, item]) => item.asFile())) {
93
mimes.push('files');
94
}
95
96
return matchesMimeType_normalized(normalizeMimeType(pattern), mimes);
97
}
98
99
public get(mimeType: string): IDataTransferItem | undefined {
100
return this._entries.get(this.toKey(mimeType))?.[0];
101
}
102
103
/**
104
* Add a new entry to this data transfer.
105
*
106
* This does not replace existing entries for `mimeType`.
107
*/
108
public append(mimeType: string, value: IDataTransferItem): void {
109
const existing = this._entries.get(mimeType);
110
if (existing) {
111
existing.push(value);
112
} else {
113
this._entries.set(this.toKey(mimeType), [value]);
114
}
115
}
116
117
/**
118
* Set the entry for a given mime type.
119
*
120
* This replaces all existing entries for `mimeType`.
121
*/
122
public replace(mimeType: string, value: IDataTransferItem): void {
123
this._entries.set(this.toKey(mimeType), [value]);
124
}
125
126
/**
127
* Remove all entries for `mimeType`.
128
*/
129
public delete(mimeType: string) {
130
this._entries.delete(this.toKey(mimeType));
131
}
132
133
/**
134
* Iterate over all `[mime, item]` pairs in this data transfer.
135
*
136
* There may be multiple entries for each mime type.
137
*/
138
public *[Symbol.iterator](): IterableIterator<readonly [string, IDataTransferItem]> {
139
for (const [mine, items] of this._entries) {
140
for (const item of items) {
141
yield [mine, item];
142
}
143
}
144
}
145
146
private toKey(mimeType: string): string {
147
return normalizeMimeType(mimeType);
148
}
149
}
150
151
function normalizeMimeType(mimeType: string): string {
152
return mimeType.toLowerCase();
153
}
154
155
export function matchesMimeType(pattern: string, mimeTypes: readonly string[]): boolean {
156
return matchesMimeType_normalized(
157
normalizeMimeType(pattern),
158
mimeTypes.map(normalizeMimeType));
159
}
160
161
function matchesMimeType_normalized(normalizedPattern: string, normalizedMimeTypes: readonly string[]): boolean {
162
// Anything wildcard
163
if (normalizedPattern === '*/*') {
164
return normalizedMimeTypes.length > 0;
165
}
166
167
// Exact match
168
if (normalizedMimeTypes.includes(normalizedPattern)) {
169
return true;
170
}
171
172
// Wildcard, such as `image/*`
173
const wildcard = normalizedPattern.match(/^([a-z]+)\/([a-z]+|\*)$/i);
174
if (!wildcard) {
175
return false;
176
}
177
178
const [_, type, subtype] = wildcard;
179
if (subtype === '*') {
180
return normalizedMimeTypes.some(mime => mime.startsWith(type + '/'));
181
}
182
183
return false;
184
}
185
186
187
export const UriList = Object.freeze({
188
// http://amundsen.com/hypermedia/urilist/
189
create: (entries: ReadonlyArray<string | URI>): string => {
190
return distinct(entries.map(x => x.toString())).join('\r\n');
191
},
192
split: (str: string): string[] => {
193
return str.split('\r\n');
194
},
195
parse: (str: string): string[] => {
196
return UriList.split(str).filter(value => !value.startsWith('#'));
197
}
198
});
199
200