Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts
3321 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 * as path from 'path';
7
import * as vscode from 'vscode';
8
import { getDocumentDir, Mimes, Schemes } from './shared';
9
import { UriList } from './uriList';
10
11
class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider {
12
13
readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'link', 'url');
14
15
async provideDocumentDropEdits(
16
document: vscode.TextDocument,
17
position: vscode.Position,
18
dataTransfer: vscode.DataTransfer,
19
token: vscode.CancellationToken,
20
): Promise<vscode.DocumentDropEdit | undefined> {
21
const uriList = await this.getUriList(dataTransfer);
22
if (!uriList.entries.length || token.isCancellationRequested) {
23
return;
24
}
25
26
const snippet = await this.createUriListSnippet(document.uri, uriList);
27
if (!snippet || token.isCancellationRequested) {
28
return;
29
}
30
31
return {
32
kind: this.kind,
33
title: snippet.label,
34
insertText: snippet.snippet.value,
35
yieldTo: this.pasteAsCssUrlByDefault(document, position) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]
36
};
37
}
38
39
async provideDocumentPasteEdits(
40
document: vscode.TextDocument,
41
ranges: readonly vscode.Range[],
42
dataTransfer: vscode.DataTransfer,
43
_context: vscode.DocumentPasteEditContext,
44
token: vscode.CancellationToken
45
): Promise<vscode.DocumentPasteEdit[] | undefined> {
46
const uriList = await this.getUriList(dataTransfer);
47
if (!uriList.entries.length || token.isCancellationRequested) {
48
return;
49
}
50
51
const snippet = await this.createUriListSnippet(document.uri, uriList);
52
if (!snippet || token.isCancellationRequested) {
53
return;
54
}
55
56
return [{
57
kind: this.kind,
58
title: snippet.label,
59
insertText: snippet.snippet.value,
60
yieldTo: this.pasteAsCssUrlByDefault(document, ranges[0].start) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]
61
}];
62
}
63
64
private async getUriList(dataTransfer: vscode.DataTransfer): Promise<UriList> {
65
const urlList = await dataTransfer.get(Mimes.uriList)?.asString();
66
if (urlList) {
67
return UriList.from(urlList);
68
}
69
70
// Find file entries
71
const uris: vscode.Uri[] = [];
72
for (const [_, entry] of dataTransfer) {
73
const file = entry.asFile();
74
if (file?.uri) {
75
uris.push(file.uri);
76
}
77
}
78
79
return new UriList(uris.map(uri => ({ uri, str: uri.toString(true) })));
80
}
81
82
private async createUriListSnippet(docUri: vscode.Uri, uriList: UriList): Promise<{ readonly snippet: vscode.SnippetString; readonly label: string } | undefined> {
83
if (!uriList.entries.length) {
84
return;
85
}
86
87
const snippet = new vscode.SnippetString();
88
for (let i = 0; i < uriList.entries.length; i++) {
89
const uri = uriList.entries[i];
90
const relativePath = getRelativePath(getDocumentDir(docUri), uri.uri);
91
const urlText = relativePath ?? uri.str;
92
93
snippet.appendText(`url(${urlText})`);
94
if (i !== uriList.entries.length - 1) {
95
snippet.appendText(' ');
96
}
97
}
98
99
return {
100
snippet,
101
label: uriList.entries.length > 1
102
? vscode.l10n.t('Insert url() Functions')
103
: vscode.l10n.t('Insert url() Function')
104
};
105
}
106
107
private pasteAsCssUrlByDefault(document: vscode.TextDocument, position: vscode.Position): boolean {
108
const regex = /url\(.+?\)/gi;
109
for (const match of Array.from(document.lineAt(position.line).text.matchAll(regex))) {
110
if (position.character > match.index && position.character < match.index + match[0].length) {
111
return false;
112
}
113
}
114
return true;
115
}
116
}
117
118
function getRelativePath(fromFile: vscode.Uri | undefined, toFile: vscode.Uri): string | undefined {
119
if (fromFile && fromFile.scheme === toFile.scheme && fromFile.authority === toFile.authority) {
120
if (toFile.scheme === Schemes.file) {
121
// On windows, we must use the native `path.relative` to generate the relative path
122
// so that drive-letters are resolved cast insensitively. However we then want to
123
// convert back to a posix path to insert in to the document
124
const relativePath = path.relative(fromFile.fsPath, toFile.fsPath);
125
return path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep));
126
}
127
128
return path.posix.relative(fromFile.path, toFile.path);
129
}
130
131
return undefined;
132
}
133
134
export function registerDropOrPasteResourceSupport(selector: vscode.DocumentSelector): vscode.Disposable {
135
const provider = new DropOrPasteResourceProvider();
136
137
return vscode.Disposable.from(
138
vscode.languages.registerDocumentDropEditProvider(selector, provider, {
139
providedDropEditKinds: [provider.kind],
140
dropMimeTypes: [
141
Mimes.uriList,
142
'files'
143
]
144
}),
145
vscode.languages.registerDocumentPasteEditProvider(selector, provider, {
146
providedPasteEditKinds: [provider.kind],
147
pasteMimeTypes: [
148
Mimes.uriList,
149
'files'
150
]
151
})
152
);
153
}
154
155