Path: blob/main/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts
3321 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 * as path from 'path';6import * as vscode from 'vscode';7import { getDocumentDir, Mimes, Schemes } from './shared';8import { UriList } from './uriList';910class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider {1112readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'link', 'url');1314async provideDocumentDropEdits(15document: vscode.TextDocument,16position: vscode.Position,17dataTransfer: vscode.DataTransfer,18token: vscode.CancellationToken,19): Promise<vscode.DocumentDropEdit | undefined> {20const uriList = await this.getUriList(dataTransfer);21if (!uriList.entries.length || token.isCancellationRequested) {22return;23}2425const snippet = await this.createUriListSnippet(document.uri, uriList);26if (!snippet || token.isCancellationRequested) {27return;28}2930return {31kind: this.kind,32title: snippet.label,33insertText: snippet.snippet.value,34yieldTo: this.pasteAsCssUrlByDefault(document, position) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]35};36}3738async provideDocumentPasteEdits(39document: vscode.TextDocument,40ranges: readonly vscode.Range[],41dataTransfer: vscode.DataTransfer,42_context: vscode.DocumentPasteEditContext,43token: vscode.CancellationToken44): Promise<vscode.DocumentPasteEdit[] | undefined> {45const uriList = await this.getUriList(dataTransfer);46if (!uriList.entries.length || token.isCancellationRequested) {47return;48}4950const snippet = await this.createUriListSnippet(document.uri, uriList);51if (!snippet || token.isCancellationRequested) {52return;53}5455return [{56kind: this.kind,57title: snippet.label,58insertText: snippet.snippet.value,59yieldTo: this.pasteAsCssUrlByDefault(document, ranges[0].start) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]60}];61}6263private async getUriList(dataTransfer: vscode.DataTransfer): Promise<UriList> {64const urlList = await dataTransfer.get(Mimes.uriList)?.asString();65if (urlList) {66return UriList.from(urlList);67}6869// Find file entries70const uris: vscode.Uri[] = [];71for (const [_, entry] of dataTransfer) {72const file = entry.asFile();73if (file?.uri) {74uris.push(file.uri);75}76}7778return new UriList(uris.map(uri => ({ uri, str: uri.toString(true) })));79}8081private async createUriListSnippet(docUri: vscode.Uri, uriList: UriList): Promise<{ readonly snippet: vscode.SnippetString; readonly label: string } | undefined> {82if (!uriList.entries.length) {83return;84}8586const snippet = new vscode.SnippetString();87for (let i = 0; i < uriList.entries.length; i++) {88const uri = uriList.entries[i];89const relativePath = getRelativePath(getDocumentDir(docUri), uri.uri);90const urlText = relativePath ?? uri.str;9192snippet.appendText(`url(${urlText})`);93if (i !== uriList.entries.length - 1) {94snippet.appendText(' ');95}96}9798return {99snippet,100label: uriList.entries.length > 1101? vscode.l10n.t('Insert url() Functions')102: vscode.l10n.t('Insert url() Function')103};104}105106private pasteAsCssUrlByDefault(document: vscode.TextDocument, position: vscode.Position): boolean {107const regex = /url\(.+?\)/gi;108for (const match of Array.from(document.lineAt(position.line).text.matchAll(regex))) {109if (position.character > match.index && position.character < match.index + match[0].length) {110return false;111}112}113return true;114}115}116117function getRelativePath(fromFile: vscode.Uri | undefined, toFile: vscode.Uri): string | undefined {118if (fromFile && fromFile.scheme === toFile.scheme && fromFile.authority === toFile.authority) {119if (toFile.scheme === Schemes.file) {120// On windows, we must use the native `path.relative` to generate the relative path121// so that drive-letters are resolved cast insensitively. However we then want to122// convert back to a posix path to insert in to the document123const relativePath = path.relative(fromFile.fsPath, toFile.fsPath);124return path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep));125}126127return path.posix.relative(fromFile.path, toFile.path);128}129130return undefined;131}132133export function registerDropOrPasteResourceSupport(selector: vscode.DocumentSelector): vscode.Disposable {134const provider = new DropOrPasteResourceProvider();135136return vscode.Disposable.from(137vscode.languages.registerDocumentDropEditProvider(selector, provider, {138providedDropEditKinds: [provider.kind],139dropMimeTypes: [140Mimes.uriList,141'files'142]143}),144vscode.languages.registerDocumentPasteEditProvider(selector, provider, {145providedPasteEditKinds: [provider.kind],146pasteMimeTypes: [147Mimes.uriList,148'files'149]150})151);152}153154155